GNU Linux-libre 4.14.254-gnu1
[releases.git] / drivers / staging / media / atomisp / i2c / imx / dw9719.c
1 /*
2  * Support for dw9719 vcm driver.
3  *
4  * Copyright (c) 2012 Intel Corporation. All Rights Reserved.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License version
8  * 2 as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA.
19  *
20  */
21
22 #include <linux/delay.h>
23 #include "dw9719.h"
24
25 static struct dw9719_device dw9719_dev;
26
27 static int dw9719_i2c_rd8(struct i2c_client *client, u8 reg, u8 *val)
28 {
29         struct i2c_msg msg[2];
30         u8 buf[2] = { reg };
31
32         msg[0].addr = DW9719_VCM_ADDR;
33         msg[0].flags = 0;
34         msg[0].len = 1;
35         msg[0].buf = buf;
36
37         msg[1].addr = DW9719_VCM_ADDR;
38         msg[1].flags = I2C_M_RD;
39         msg[1].len = 1;
40         msg[1].buf = &buf[1];
41         *val = 0;
42
43         if (i2c_transfer(client->adapter, msg, 2) != 2)
44                 return -EIO;
45         *val = buf[1];
46
47         return 0;
48 }
49
50 static int dw9719_i2c_wr8(struct i2c_client *client, u8 reg, u8 val)
51 {
52         struct i2c_msg msg;
53         u8 buf[2] = { reg, val };
54
55         msg.addr = DW9719_VCM_ADDR;
56         msg.flags = 0;
57         msg.len = sizeof(buf);
58         msg.buf = buf;
59
60         if (i2c_transfer(client->adapter, &msg, 1) != 1)
61                 return -EIO;
62
63         return 0;
64 }
65
66 static int dw9719_i2c_wr16(struct i2c_client *client, u8 reg, u16 val)
67 {
68         struct i2c_msg msg;
69         u8 buf[3] = { reg, (u8)(val >> 8), (u8)(val & 0xff)};
70
71         msg.addr = DW9719_VCM_ADDR;
72         msg.flags = 0;
73         msg.len = sizeof(buf);
74         msg.buf = buf;
75
76         if (i2c_transfer(client->adapter, &msg, 1) != 1)
77                 return -EIO;
78
79         return 0;
80 }
81
82 int dw9719_vcm_power_up(struct v4l2_subdev *sd)
83 {
84         struct i2c_client *client = v4l2_get_subdevdata(sd);
85         int ret;
86         u8 value;
87
88         /* Enable power */
89         ret = dw9719_dev.platform_data->power_ctrl(sd, 1);
90         /* waiting time requested by DW9714A(vcm) */
91         if (ret)
92                 return ret;
93         /* Wait for VBAT to stabilize */
94         udelay(1);
95
96         /*
97          * Jiggle SCL pin to wake up device.
98          */
99         ret = dw9719_i2c_wr8(client, DW9719_CONTROL, 1);
100         /* Need 100us to transit from SHUTDOWN to STANDBY*/
101         usleep_range(100, 1000);
102
103         /* Enable the ringing compensation */
104         ret = dw9719_i2c_wr8(client, DW9719_CONTROL, DW9719_ENABLE_RINGING);
105         if (ret < 0)
106                 goto fail_powerdown;
107
108         /* Use SAC3 mode */
109         ret = dw9719_i2c_wr8(client, DW9719_MODE, DW9719_MODE_SAC3);
110         if (ret < 0)
111                 goto fail_powerdown;
112
113         /* Set the resonance frequency */
114         ret = dw9719_i2c_wr8(client, DW9719_VCM_FREQ, DW9719_DEFAULT_VCM_FREQ);
115         if (ret < 0)
116                 goto fail_powerdown;
117
118         /* Detect device */
119         ret = dw9719_i2c_rd8(client, DW9719_INFO, &value);
120         if (ret < 0)
121                 goto fail_powerdown;
122         if (value != DW9719_ID) {
123                 ret = -ENXIO;
124                 goto fail_powerdown;
125         }
126         dw9719_dev.focus = 0;
127         dw9719_dev.initialized = true;
128
129         return 0;
130
131 fail_powerdown:
132         dw9719_dev.platform_data->power_ctrl(sd, 0);
133         return ret;
134 }
135
136 int dw9719_vcm_power_down(struct v4l2_subdev *sd)
137 {
138         return dw9719_dev.platform_data->power_ctrl(sd, 0);
139 }
140
141 int dw9719_q_focus_status(struct v4l2_subdev *sd, s32 *value)
142 {
143         static const struct timespec move_time = {
144
145                 .tv_sec = 0,
146                 .tv_nsec = 60000000
147         };
148         struct timespec current_time, finish_time, delta_time;
149
150         getnstimeofday(&current_time);
151         finish_time = timespec_add(dw9719_dev.focus_time, move_time);
152         delta_time = timespec_sub(current_time, finish_time);
153         if (delta_time.tv_sec >= 0 && delta_time.tv_nsec >= 0) {
154                 *value = ATOMISP_FOCUS_HP_COMPLETE |
155                          ATOMISP_FOCUS_STATUS_ACCEPTS_NEW_MOVE;
156         } else {
157                 *value = ATOMISP_FOCUS_STATUS_MOVING |
158                          ATOMISP_FOCUS_HP_IN_PROGRESS;
159         }
160
161         return 0;
162 }
163
164 int dw9719_t_focus_abs(struct v4l2_subdev *sd, s32 value)
165 {
166         struct i2c_client *client = v4l2_get_subdevdata(sd);
167         int ret;
168
169         value = clamp(value, 0, DW9719_MAX_FOCUS_POS);
170         ret = dw9719_i2c_wr16(client, DW9719_VCM_CURRENT, value);
171         if (ret < 0)
172                 return ret;
173
174         getnstimeofday(&dw9719_dev.focus_time);
175         dw9719_dev.focus = value;
176
177         return 0;
178 }
179
180 int dw9719_t_focus_rel(struct v4l2_subdev *sd, s32 value)
181 {
182         return dw9719_t_focus_abs(sd, dw9719_dev.focus + value);
183 }
184
185 int dw9719_q_focus_abs(struct v4l2_subdev *sd, s32 *value)
186 {
187         *value = dw9719_dev.focus;
188         return 0;
189 }
190 int dw9719_t_vcm_slew(struct v4l2_subdev *sd, s32 value)
191 {
192         return 0;
193 }
194
195 int dw9719_t_vcm_timing(struct v4l2_subdev *sd, s32 value)
196 {
197         return 0;
198 }