18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (C) 2018 Intel Corporation 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/acpi.h> 58c2ecf20Sopenharmony_ci#include <linux/delay.h> 68c2ecf20Sopenharmony_ci#include <linux/i2c.h> 78c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 108c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 118c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define DW9807_MAX_FOCUS_POS 1023 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * This sets the minimum granularity for the focus positions. 168c2ecf20Sopenharmony_ci * A value of 1 gives maximum accuracy for a desired focus position. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci#define DW9807_FOCUS_STEPS 1 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * This acts as the minimum granularity of lens movement. 218c2ecf20Sopenharmony_ci * Keep this value power of 2, so the control steps can be 228c2ecf20Sopenharmony_ci * uniformly adjusted for gradual lens movement, with desired 238c2ecf20Sopenharmony_ci * number of control steps. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci#define DW9807_CTRL_STEPS 16 268c2ecf20Sopenharmony_ci#define DW9807_CTRL_DELAY_US 1000 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define DW9807_CTL_ADDR 0x02 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * DW9807 separates two registers to control the VCM position. 318c2ecf20Sopenharmony_ci * One for MSB value, another is LSB value. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci#define DW9807_MSB_ADDR 0x03 348c2ecf20Sopenharmony_ci#define DW9807_LSB_ADDR 0x04 358c2ecf20Sopenharmony_ci#define DW9807_STATUS_ADDR 0x05 368c2ecf20Sopenharmony_ci#define DW9807_MODE_ADDR 0x06 378c2ecf20Sopenharmony_ci#define DW9807_RESONANCE_ADDR 0x07 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define MAX_RETRY 10 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct dw9807_device { 428c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler ctrls_vcm; 438c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 448c2ecf20Sopenharmony_ci u16 current_val; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic inline struct dw9807_device *sd_to_dw9807_vcm( 488c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci return container_of(subdev, struct dw9807_device, sd); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int dw9807_i2c_check(struct i2c_client *client) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci const char status_addr = DW9807_STATUS_ADDR; 568c2ecf20Sopenharmony_ci char status_result; 578c2ecf20Sopenharmony_ci int ret; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci ret = i2c_master_send(client, &status_addr, sizeof(status_addr)); 608c2ecf20Sopenharmony_ci if (ret < 0) { 618c2ecf20Sopenharmony_ci dev_err(&client->dev, "I2C write STATUS address fail ret = %d\n", 628c2ecf20Sopenharmony_ci ret); 638c2ecf20Sopenharmony_ci return ret; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci ret = i2c_master_recv(client, &status_result, sizeof(status_result)); 678c2ecf20Sopenharmony_ci if (ret < 0) { 688c2ecf20Sopenharmony_ci dev_err(&client->dev, "I2C read STATUS value fail ret = %d\n", 698c2ecf20Sopenharmony_ci ret); 708c2ecf20Sopenharmony_ci return ret; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return status_result; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int dw9807_set_dac(struct i2c_client *client, u16 data) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci const char tx_data[3] = { 798c2ecf20Sopenharmony_ci DW9807_MSB_ADDR, ((data >> 8) & 0x03), (data & 0xff) 808c2ecf20Sopenharmony_ci }; 818c2ecf20Sopenharmony_ci int val, ret; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* 848c2ecf20Sopenharmony_ci * According to the datasheet, need to check the bus status before we 858c2ecf20Sopenharmony_ci * write VCM position. This ensure that we really write the value 868c2ecf20Sopenharmony_ci * into the register 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ci ret = readx_poll_timeout(dw9807_i2c_check, client, val, val <= 0, 898c2ecf20Sopenharmony_ci DW9807_CTRL_DELAY_US, MAX_RETRY * DW9807_CTRL_DELAY_US); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (ret || val < 0) { 928c2ecf20Sopenharmony_ci if (ret) { 938c2ecf20Sopenharmony_ci dev_warn(&client->dev, 948c2ecf20Sopenharmony_ci "Cannot do the write operation because VCM is busy\n"); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return ret ? -EBUSY : val; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* Write VCM position to registers */ 1018c2ecf20Sopenharmony_ci ret = i2c_master_send(client, tx_data, sizeof(tx_data)); 1028c2ecf20Sopenharmony_ci if (ret < 0) { 1038c2ecf20Sopenharmony_ci dev_err(&client->dev, 1048c2ecf20Sopenharmony_ci "I2C write MSB fail ret=%d\n", ret); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return ret; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int dw9807_set_ctrl(struct v4l2_ctrl *ctrl) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct dw9807_device *dev_vcm = container_of(ctrl->handler, 1158c2ecf20Sopenharmony_ci struct dw9807_device, ctrls_vcm); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) { 1188c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci dev_vcm->current_val = ctrl->val; 1218c2ecf20Sopenharmony_ci return dw9807_set_dac(client, ctrl->val); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return -EINVAL; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops dw9807_vcm_ctrl_ops = { 1288c2ecf20Sopenharmony_ci .s_ctrl = dw9807_set_ctrl, 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int dw9807_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci int rval; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci rval = pm_runtime_get_sync(sd->dev); 1368c2ecf20Sopenharmony_ci if (rval < 0) { 1378c2ecf20Sopenharmony_ci pm_runtime_put_noidle(sd->dev); 1388c2ecf20Sopenharmony_ci return rval; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int dw9807_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci pm_runtime_put(sd->dev); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops dw9807_int_ops = { 1528c2ecf20Sopenharmony_ci .open = dw9807_open, 1538c2ecf20Sopenharmony_ci .close = dw9807_close, 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops dw9807_ops = { }; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void dw9807_subdev_cleanup(struct dw9807_device *dw9807_dev) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(&dw9807_dev->sd); 1618c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm); 1628c2ecf20Sopenharmony_ci media_entity_cleanup(&dw9807_dev->sd.entity); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int dw9807_init_controls(struct dw9807_device *dev_vcm) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm; 1688c2ecf20Sopenharmony_ci const struct v4l2_ctrl_ops *ops = &dw9807_vcm_ctrl_ops; 1698c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 1); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, 1748c2ecf20Sopenharmony_ci 0, DW9807_MAX_FOCUS_POS, DW9807_FOCUS_STEPS, 0); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci dev_vcm->sd.ctrl_handler = hdl; 1778c2ecf20Sopenharmony_ci if (hdl->error) { 1788c2ecf20Sopenharmony_ci dev_err(&client->dev, "%s fail error: 0x%x\n", 1798c2ecf20Sopenharmony_ci __func__, hdl->error); 1808c2ecf20Sopenharmony_ci return hdl->error; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int dw9807_probe(struct i2c_client *client) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct dw9807_device *dw9807_dev; 1898c2ecf20Sopenharmony_ci int rval; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci dw9807_dev = devm_kzalloc(&client->dev, sizeof(*dw9807_dev), 1928c2ecf20Sopenharmony_ci GFP_KERNEL); 1938c2ecf20Sopenharmony_ci if (dw9807_dev == NULL) 1948c2ecf20Sopenharmony_ci return -ENOMEM; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(&dw9807_dev->sd, client, &dw9807_ops); 1978c2ecf20Sopenharmony_ci dw9807_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 1988c2ecf20Sopenharmony_ci dw9807_dev->sd.internal_ops = &dw9807_int_ops; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci rval = dw9807_init_controls(dw9807_dev); 2018c2ecf20Sopenharmony_ci if (rval) 2028c2ecf20Sopenharmony_ci goto err_cleanup; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci rval = media_entity_pads_init(&dw9807_dev->sd.entity, 0, NULL); 2058c2ecf20Sopenharmony_ci if (rval < 0) 2068c2ecf20Sopenharmony_ci goto err_cleanup; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci dw9807_dev->sd.entity.function = MEDIA_ENT_F_LENS; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci rval = v4l2_async_register_subdev(&dw9807_dev->sd); 2118c2ecf20Sopenharmony_ci if (rval < 0) 2128c2ecf20Sopenharmony_ci goto err_cleanup; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci pm_runtime_set_active(&client->dev); 2158c2ecf20Sopenharmony_ci pm_runtime_enable(&client->dev); 2168c2ecf20Sopenharmony_ci pm_runtime_idle(&client->dev); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cierr_cleanup: 2218c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm); 2228c2ecf20Sopenharmony_ci media_entity_cleanup(&dw9807_dev->sd.entity); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return rval; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int dw9807_remove(struct i2c_client *client) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 2308c2ecf20Sopenharmony_ci struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci pm_runtime_disable(&client->dev); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci dw9807_subdev_cleanup(dw9807_dev); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* 2408c2ecf20Sopenharmony_ci * This function sets the vcm position, so it consumes least current 2418c2ecf20Sopenharmony_ci * The lens position is gradually moved in units of DW9807_CTRL_STEPS, 2428c2ecf20Sopenharmony_ci * to make the movements smoothly. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_cistatic int __maybe_unused dw9807_vcm_suspend(struct device *dev) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 2478c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 2488c2ecf20Sopenharmony_ci struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); 2498c2ecf20Sopenharmony_ci const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 }; 2508c2ecf20Sopenharmony_ci int ret, val; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci for (val = dw9807_dev->current_val & ~(DW9807_CTRL_STEPS - 1); 2538c2ecf20Sopenharmony_ci val >= 0; val -= DW9807_CTRL_STEPS) { 2548c2ecf20Sopenharmony_ci ret = dw9807_set_dac(client, val); 2558c2ecf20Sopenharmony_ci if (ret) 2568c2ecf20Sopenharmony_ci dev_err_once(dev, "%s I2C failure: %d", __func__, ret); 2578c2ecf20Sopenharmony_ci usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* Power down */ 2618c2ecf20Sopenharmony_ci ret = i2c_master_send(client, tx_data, sizeof(tx_data)); 2628c2ecf20Sopenharmony_ci if (ret < 0) { 2638c2ecf20Sopenharmony_ci dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); 2648c2ecf20Sopenharmony_ci return ret; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* 2718c2ecf20Sopenharmony_ci * This function sets the vcm position to the value set by the user 2728c2ecf20Sopenharmony_ci * through v4l2_ctrl_ops s_ctrl handler 2738c2ecf20Sopenharmony_ci * The lens position is gradually moved in units of DW9807_CTRL_STEPS, 2748c2ecf20Sopenharmony_ci * to make the movements smoothly. 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_cistatic int __maybe_unused dw9807_vcm_resume(struct device *dev) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 2798c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 2808c2ecf20Sopenharmony_ci struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); 2818c2ecf20Sopenharmony_ci const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 }; 2828c2ecf20Sopenharmony_ci int ret, val; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Power on */ 2858c2ecf20Sopenharmony_ci ret = i2c_master_send(client, tx_data, sizeof(tx_data)); 2868c2ecf20Sopenharmony_ci if (ret < 0) { 2878c2ecf20Sopenharmony_ci dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); 2888c2ecf20Sopenharmony_ci return ret; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci for (val = dw9807_dev->current_val % DW9807_CTRL_STEPS; 2928c2ecf20Sopenharmony_ci val < dw9807_dev->current_val + DW9807_CTRL_STEPS - 1; 2938c2ecf20Sopenharmony_ci val += DW9807_CTRL_STEPS) { 2948c2ecf20Sopenharmony_ci ret = dw9807_set_dac(client, val); 2958c2ecf20Sopenharmony_ci if (ret) 2968c2ecf20Sopenharmony_ci dev_err_ratelimited(dev, "%s I2C failure: %d", 2978c2ecf20Sopenharmony_ci __func__, ret); 2988c2ecf20Sopenharmony_ci usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic const struct of_device_id dw9807_of_table[] = { 3058c2ecf20Sopenharmony_ci { .compatible = "dongwoon,dw9807-vcm" }, 3068c2ecf20Sopenharmony_ci { /* sentinel */ } 3078c2ecf20Sopenharmony_ci}; 3088c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, dw9807_of_table); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic const struct dev_pm_ops dw9807_pm_ops = { 3118c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume) 3128c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume, NULL) 3138c2ecf20Sopenharmony_ci}; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic struct i2c_driver dw9807_i2c_driver = { 3168c2ecf20Sopenharmony_ci .driver = { 3178c2ecf20Sopenharmony_ci .name = "dw9807", 3188c2ecf20Sopenharmony_ci .pm = &dw9807_pm_ops, 3198c2ecf20Sopenharmony_ci .of_match_table = dw9807_of_table, 3208c2ecf20Sopenharmony_ci }, 3218c2ecf20Sopenharmony_ci .probe_new = dw9807_probe, 3228c2ecf20Sopenharmony_ci .remove = dw9807_remove, 3238c2ecf20Sopenharmony_ci}; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cimodule_i2c_driver(dw9807_i2c_driver); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chiang, Alan"); 3288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DW9807 VCM driver"); 3298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 330