dw9807-vcm.c (8322B)
1// SPDX-License-Identifier: GPL-2.0 2// Copyright (C) 2018 Intel Corporation 3 4#include <linux/acpi.h> 5#include <linux/delay.h> 6#include <linux/i2c.h> 7#include <linux/iopoll.h> 8#include <linux/module.h> 9#include <linux/pm_runtime.h> 10#include <media/v4l2-ctrls.h> 11#include <media/v4l2-device.h> 12 13#define DW9807_MAX_FOCUS_POS 1023 14/* 15 * This sets the minimum granularity for the focus positions. 16 * A value of 1 gives maximum accuracy for a desired focus position. 17 */ 18#define DW9807_FOCUS_STEPS 1 19/* 20 * This acts as the minimum granularity of lens movement. 21 * Keep this value power of 2, so the control steps can be 22 * uniformly adjusted for gradual lens movement, with desired 23 * number of control steps. 24 */ 25#define DW9807_CTRL_STEPS 16 26#define DW9807_CTRL_DELAY_US 1000 27 28#define DW9807_CTL_ADDR 0x02 29/* 30 * DW9807 separates two registers to control the VCM position. 31 * One for MSB value, another is LSB value. 32 */ 33#define DW9807_MSB_ADDR 0x03 34#define DW9807_LSB_ADDR 0x04 35#define DW9807_STATUS_ADDR 0x05 36#define DW9807_MODE_ADDR 0x06 37#define DW9807_RESONANCE_ADDR 0x07 38 39#define MAX_RETRY 10 40 41struct dw9807_device { 42 struct v4l2_ctrl_handler ctrls_vcm; 43 struct v4l2_subdev sd; 44 u16 current_val; 45}; 46 47static inline struct dw9807_device *sd_to_dw9807_vcm( 48 struct v4l2_subdev *subdev) 49{ 50 return container_of(subdev, struct dw9807_device, sd); 51} 52 53static int dw9807_i2c_check(struct i2c_client *client) 54{ 55 const char status_addr = DW9807_STATUS_ADDR; 56 char status_result; 57 int ret; 58 59 ret = i2c_master_send(client, &status_addr, sizeof(status_addr)); 60 if (ret < 0) { 61 dev_err(&client->dev, "I2C write STATUS address fail ret = %d\n", 62 ret); 63 return ret; 64 } 65 66 ret = i2c_master_recv(client, &status_result, sizeof(status_result)); 67 if (ret < 0) { 68 dev_err(&client->dev, "I2C read STATUS value fail ret = %d\n", 69 ret); 70 return ret; 71 } 72 73 return status_result; 74} 75 76static int dw9807_set_dac(struct i2c_client *client, u16 data) 77{ 78 const char tx_data[3] = { 79 DW9807_MSB_ADDR, ((data >> 8) & 0x03), (data & 0xff) 80 }; 81 int val, ret; 82 83 /* 84 * According to the datasheet, need to check the bus status before we 85 * write VCM position. This ensure that we really write the value 86 * into the register 87 */ 88 ret = readx_poll_timeout(dw9807_i2c_check, client, val, val <= 0, 89 DW9807_CTRL_DELAY_US, MAX_RETRY * DW9807_CTRL_DELAY_US); 90 91 if (ret || val < 0) { 92 if (ret) { 93 dev_warn(&client->dev, 94 "Cannot do the write operation because VCM is busy\n"); 95 } 96 97 return ret ? -EBUSY : val; 98 } 99 100 /* Write VCM position to registers */ 101 ret = i2c_master_send(client, tx_data, sizeof(tx_data)); 102 if (ret < 0) { 103 dev_err(&client->dev, 104 "I2C write MSB fail ret=%d\n", ret); 105 106 return ret; 107 } 108 109 return 0; 110} 111 112static int dw9807_set_ctrl(struct v4l2_ctrl *ctrl) 113{ 114 struct dw9807_device *dev_vcm = container_of(ctrl->handler, 115 struct dw9807_device, ctrls_vcm); 116 117 if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) { 118 struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd); 119 120 dev_vcm->current_val = ctrl->val; 121 return dw9807_set_dac(client, ctrl->val); 122 } 123 124 return -EINVAL; 125} 126 127static const struct v4l2_ctrl_ops dw9807_vcm_ctrl_ops = { 128 .s_ctrl = dw9807_set_ctrl, 129}; 130 131static int dw9807_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 132{ 133 return pm_runtime_resume_and_get(sd->dev); 134} 135 136static int dw9807_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 137{ 138 pm_runtime_put(sd->dev); 139 140 return 0; 141} 142 143static const struct v4l2_subdev_internal_ops dw9807_int_ops = { 144 .open = dw9807_open, 145 .close = dw9807_close, 146}; 147 148static const struct v4l2_subdev_ops dw9807_ops = { }; 149 150static void dw9807_subdev_cleanup(struct dw9807_device *dw9807_dev) 151{ 152 v4l2_async_unregister_subdev(&dw9807_dev->sd); 153 v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm); 154 media_entity_cleanup(&dw9807_dev->sd.entity); 155} 156 157static int dw9807_init_controls(struct dw9807_device *dev_vcm) 158{ 159 struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm; 160 const struct v4l2_ctrl_ops *ops = &dw9807_vcm_ctrl_ops; 161 struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd); 162 163 v4l2_ctrl_handler_init(hdl, 1); 164 165 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, 166 0, DW9807_MAX_FOCUS_POS, DW9807_FOCUS_STEPS, 0); 167 168 dev_vcm->sd.ctrl_handler = hdl; 169 if (hdl->error) { 170 dev_err(&client->dev, "%s fail error: 0x%x\n", 171 __func__, hdl->error); 172 return hdl->error; 173 } 174 175 return 0; 176} 177 178static int dw9807_probe(struct i2c_client *client) 179{ 180 struct dw9807_device *dw9807_dev; 181 int rval; 182 183 dw9807_dev = devm_kzalloc(&client->dev, sizeof(*dw9807_dev), 184 GFP_KERNEL); 185 if (dw9807_dev == NULL) 186 return -ENOMEM; 187 188 v4l2_i2c_subdev_init(&dw9807_dev->sd, client, &dw9807_ops); 189 dw9807_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 190 dw9807_dev->sd.internal_ops = &dw9807_int_ops; 191 192 rval = dw9807_init_controls(dw9807_dev); 193 if (rval) 194 goto err_cleanup; 195 196 rval = media_entity_pads_init(&dw9807_dev->sd.entity, 0, NULL); 197 if (rval < 0) 198 goto err_cleanup; 199 200 dw9807_dev->sd.entity.function = MEDIA_ENT_F_LENS; 201 202 rval = v4l2_async_register_subdev(&dw9807_dev->sd); 203 if (rval < 0) 204 goto err_cleanup; 205 206 pm_runtime_set_active(&client->dev); 207 pm_runtime_enable(&client->dev); 208 pm_runtime_idle(&client->dev); 209 210 return 0; 211 212err_cleanup: 213 v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm); 214 media_entity_cleanup(&dw9807_dev->sd.entity); 215 216 return rval; 217} 218 219static int dw9807_remove(struct i2c_client *client) 220{ 221 struct v4l2_subdev *sd = i2c_get_clientdata(client); 222 struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); 223 224 pm_runtime_disable(&client->dev); 225 226 dw9807_subdev_cleanup(dw9807_dev); 227 228 return 0; 229} 230 231/* 232 * This function sets the vcm position, so it consumes least current 233 * The lens position is gradually moved in units of DW9807_CTRL_STEPS, 234 * to make the movements smoothly. 235 */ 236static int __maybe_unused dw9807_vcm_suspend(struct device *dev) 237{ 238 struct i2c_client *client = to_i2c_client(dev); 239 struct v4l2_subdev *sd = i2c_get_clientdata(client); 240 struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); 241 const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 }; 242 int ret, val; 243 244 for (val = dw9807_dev->current_val & ~(DW9807_CTRL_STEPS - 1); 245 val >= 0; val -= DW9807_CTRL_STEPS) { 246 ret = dw9807_set_dac(client, val); 247 if (ret) 248 dev_err_once(dev, "%s I2C failure: %d", __func__, ret); 249 usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); 250 } 251 252 /* Power down */ 253 ret = i2c_master_send(client, tx_data, sizeof(tx_data)); 254 if (ret < 0) { 255 dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); 256 return ret; 257 } 258 259 return 0; 260} 261 262/* 263 * This function sets the vcm position to the value set by the user 264 * through v4l2_ctrl_ops s_ctrl handler 265 * The lens position is gradually moved in units of DW9807_CTRL_STEPS, 266 * to make the movements smoothly. 267 */ 268static int __maybe_unused dw9807_vcm_resume(struct device *dev) 269{ 270 struct i2c_client *client = to_i2c_client(dev); 271 struct v4l2_subdev *sd = i2c_get_clientdata(client); 272 struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); 273 const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 }; 274 int ret, val; 275 276 /* Power on */ 277 ret = i2c_master_send(client, tx_data, sizeof(tx_data)); 278 if (ret < 0) { 279 dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); 280 return ret; 281 } 282 283 for (val = dw9807_dev->current_val % DW9807_CTRL_STEPS; 284 val < dw9807_dev->current_val + DW9807_CTRL_STEPS - 1; 285 val += DW9807_CTRL_STEPS) { 286 ret = dw9807_set_dac(client, val); 287 if (ret) 288 dev_err_ratelimited(dev, "%s I2C failure: %d", 289 __func__, ret); 290 usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); 291 } 292 293 return 0; 294} 295 296static const struct of_device_id dw9807_of_table[] = { 297 { .compatible = "dongwoon,dw9807-vcm" }, 298 /* Compatibility for older firmware, NEVER USE THIS IN FIRMWARE! */ 299 { .compatible = "dongwoon,dw9807" }, 300 { /* sentinel */ } 301}; 302MODULE_DEVICE_TABLE(of, dw9807_of_table); 303 304static const struct dev_pm_ops dw9807_pm_ops = { 305 SET_SYSTEM_SLEEP_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume) 306 SET_RUNTIME_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume, NULL) 307}; 308 309static struct i2c_driver dw9807_i2c_driver = { 310 .driver = { 311 .name = "dw9807", 312 .pm = &dw9807_pm_ops, 313 .of_match_table = dw9807_of_table, 314 }, 315 .probe_new = dw9807_probe, 316 .remove = dw9807_remove, 317}; 318 319module_i2c_driver(dw9807_i2c_driver); 320 321MODULE_AUTHOR("Chiang, Alan"); 322MODULE_DESCRIPTION("DW9807 VCM driver"); 323MODULE_LICENSE("GPL v2");