linux/drivers/media/i2c/dw9807-vcm.c
<<
>>
Prefs
   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        int rval;
 134
 135        rval = pm_runtime_get_sync(sd->dev);
 136        if (rval < 0) {
 137                pm_runtime_put_noidle(sd->dev);
 138                return rval;
 139        }
 140
 141        return 0;
 142}
 143
 144static int dw9807_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 145{
 146        pm_runtime_put(sd->dev);
 147
 148        return 0;
 149}
 150
 151static const struct v4l2_subdev_internal_ops dw9807_int_ops = {
 152        .open = dw9807_open,
 153        .close = dw9807_close,
 154};
 155
 156static const struct v4l2_subdev_ops dw9807_ops = { };
 157
 158static void dw9807_subdev_cleanup(struct dw9807_device *dw9807_dev)
 159{
 160        v4l2_async_unregister_subdev(&dw9807_dev->sd);
 161        v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm);
 162        media_entity_cleanup(&dw9807_dev->sd.entity);
 163}
 164
 165static int dw9807_init_controls(struct dw9807_device *dev_vcm)
 166{
 167        struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm;
 168        const struct v4l2_ctrl_ops *ops = &dw9807_vcm_ctrl_ops;
 169        struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd);
 170
 171        v4l2_ctrl_handler_init(hdl, 1);
 172
 173        v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE,
 174                          0, DW9807_MAX_FOCUS_POS, DW9807_FOCUS_STEPS, 0);
 175
 176        dev_vcm->sd.ctrl_handler = hdl;
 177        if (hdl->error) {
 178                dev_err(&client->dev, "%s fail error: 0x%x\n",
 179                        __func__, hdl->error);
 180                return hdl->error;
 181        }
 182
 183        return 0;
 184}
 185
 186static int dw9807_probe(struct i2c_client *client)
 187{
 188        struct dw9807_device *dw9807_dev;
 189        int rval;
 190
 191        dw9807_dev = devm_kzalloc(&client->dev, sizeof(*dw9807_dev),
 192                                  GFP_KERNEL);
 193        if (dw9807_dev == NULL)
 194                return -ENOMEM;
 195
 196        v4l2_i2c_subdev_init(&dw9807_dev->sd, client, &dw9807_ops);
 197        dw9807_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 198        dw9807_dev->sd.internal_ops = &dw9807_int_ops;
 199
 200        rval = dw9807_init_controls(dw9807_dev);
 201        if (rval)
 202                goto err_cleanup;
 203
 204        rval = media_entity_pads_init(&dw9807_dev->sd.entity, 0, NULL);
 205        if (rval < 0)
 206                goto err_cleanup;
 207
 208        dw9807_dev->sd.entity.function = MEDIA_ENT_F_LENS;
 209
 210        rval = v4l2_async_register_subdev(&dw9807_dev->sd);
 211        if (rval < 0)
 212                goto err_cleanup;
 213
 214        pm_runtime_set_active(&client->dev);
 215        pm_runtime_enable(&client->dev);
 216        pm_runtime_idle(&client->dev);
 217
 218        return 0;
 219
 220err_cleanup:
 221        v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm);
 222        media_entity_cleanup(&dw9807_dev->sd.entity);
 223
 224        return rval;
 225}
 226
 227static int dw9807_remove(struct i2c_client *client)
 228{
 229        struct v4l2_subdev *sd = i2c_get_clientdata(client);
 230        struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd);
 231
 232        pm_runtime_disable(&client->dev);
 233
 234        dw9807_subdev_cleanup(dw9807_dev);
 235
 236        return 0;
 237}
 238
 239/*
 240 * This function sets the vcm position, so it consumes least current
 241 * The lens position is gradually moved in units of DW9807_CTRL_STEPS,
 242 * to make the movements smoothly.
 243 */
 244static int __maybe_unused dw9807_vcm_suspend(struct device *dev)
 245{
 246        struct i2c_client *client = to_i2c_client(dev);
 247        struct v4l2_subdev *sd = i2c_get_clientdata(client);
 248        struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd);
 249        const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 };
 250        int ret, val;
 251
 252        for (val = dw9807_dev->current_val & ~(DW9807_CTRL_STEPS - 1);
 253             val >= 0; val -= DW9807_CTRL_STEPS) {
 254                ret = dw9807_set_dac(client, val);
 255                if (ret)
 256                        dev_err_once(dev, "%s I2C failure: %d", __func__, ret);
 257                usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10);
 258        }
 259
 260        /* Power down */
 261        ret = i2c_master_send(client, tx_data, sizeof(tx_data));
 262        if (ret < 0) {
 263                dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret);
 264                return ret;
 265        }
 266
 267        return 0;
 268}
 269
 270/*
 271 * This function sets the vcm position to the value set by the user
 272 * through v4l2_ctrl_ops s_ctrl handler
 273 * The lens position is gradually moved in units of DW9807_CTRL_STEPS,
 274 * to make the movements smoothly.
 275 */
 276static int  __maybe_unused dw9807_vcm_resume(struct device *dev)
 277{
 278        struct i2c_client *client = to_i2c_client(dev);
 279        struct v4l2_subdev *sd = i2c_get_clientdata(client);
 280        struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd);
 281        const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 };
 282        int ret, val;
 283
 284        /* Power on */
 285        ret = i2c_master_send(client, tx_data, sizeof(tx_data));
 286        if (ret < 0) {
 287                dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret);
 288                return ret;
 289        }
 290
 291        for (val = dw9807_dev->current_val % DW9807_CTRL_STEPS;
 292             val < dw9807_dev->current_val + DW9807_CTRL_STEPS - 1;
 293             val += DW9807_CTRL_STEPS) {
 294                ret = dw9807_set_dac(client, val);
 295                if (ret)
 296                        dev_err_ratelimited(dev, "%s I2C failure: %d",
 297                                                __func__, ret);
 298                usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10);
 299        }
 300
 301        return 0;
 302}
 303
 304static const struct of_device_id dw9807_of_table[] = {
 305        { .compatible = "dongwoon,dw9807-vcm" },
 306        { /* sentinel */ }
 307};
 308MODULE_DEVICE_TABLE(of, dw9807_of_table);
 309
 310static const struct dev_pm_ops dw9807_pm_ops = {
 311        SET_SYSTEM_SLEEP_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume)
 312        SET_RUNTIME_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume, NULL)
 313};
 314
 315static struct i2c_driver dw9807_i2c_driver = {
 316        .driver = {
 317                .name = "dw9807",
 318                .pm = &dw9807_pm_ops,
 319                .of_match_table = dw9807_of_table,
 320        },
 321        .probe_new = dw9807_probe,
 322        .remove = dw9807_remove,
 323};
 324
 325module_i2c_driver(dw9807_i2c_driver);
 326
 327MODULE_AUTHOR("Chiang, Alan <alanx.chiang@intel.com>");
 328MODULE_DESCRIPTION("DW9807 VCM driver");
 329MODULE_LICENSE("GPL v2");
 330