License cleanup: add SPDX GPL-2.0 license identifier to files with no license
[linux-2.6-block.git] / drivers / staging / media / atomisp / i2c / imx / dw9714.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/bitops.h>
3 #include <linux/device.h>
4 #include <linux/delay.h>
5 #include <linux/errno.h>
6 #include <linux/fs.h>
7 #include <linux/gpio.h>
8 #include <linux/init.h>
9 #include <linux/i2c.h>
10 #include <linux/io.h>
11 #include <linux/kernel.h>
12 #include <linux/mm.h>
13 #include <linux/kmod.h>
14 #include <linux/module.h>
15 #include <linux/moduleparam.h>
16 #include <linux/string.h>
17 #include <linux/slab.h>
18 #include <linux/types.h>
19 #include <media/v4l2-device.h>
20 #include <asm/intel-mid.h>
21
22 #include "dw9714.h"
23
24 static struct dw9714_device dw9714_dev;
25 static int dw9714_i2c_write(struct i2c_client *client, u16 data)
26 {
27         struct i2c_msg msg;
28         const int num_msg = 1;
29         int ret;
30         u16 val;
31
32         val = cpu_to_be16(data);
33         msg.addr = DW9714_VCM_ADDR;
34         msg.flags = 0;
35         msg.len = DW9714_16BIT;
36         msg.buf = (u8 *)&val;
37
38         ret = i2c_transfer(client->adapter, &msg, 1);
39
40         return ret == num_msg ? 0 : -EIO;
41 }
42
43 int dw9714_vcm_power_up(struct v4l2_subdev *sd)
44 {
45         int ret;
46
47         /* Enable power */
48         ret = dw9714_dev.platform_data->power_ctrl(sd, 1);
49         /* waiting time requested by DW9714A(vcm) */
50         usleep_range(12000, 12500);
51         return ret;
52 }
53
54 int dw9714_vcm_power_down(struct v4l2_subdev *sd)
55 {
56         return dw9714_dev.platform_data->power_ctrl(sd, 0);
57 }
58
59
60 static int dw9714_t_focus_vcm(struct v4l2_subdev *sd, u16 val)
61 {
62         struct i2c_client *client = v4l2_get_subdevdata(sd);
63         int ret = -EINVAL;
64         u8 mclk = vcm_step_mclk(dw9714_dev.vcm_settings.step_setting);
65         u8 s = vcm_step_s(dw9714_dev.vcm_settings.step_setting);
66
67         /*
68          * For different mode, VCM_PROTECTION_OFF/ON required by the
69          * control procedure. For DW9714_DIRECT/DLC mode, slew value is
70          * VCM_DEFAULT_S(0).
71          */
72         switch (dw9714_dev.vcm_mode) {
73         case DW9714_DIRECT:
74                 if (dw9714_dev.vcm_settings.update) {
75                         ret = dw9714_i2c_write(client, VCM_PROTECTION_OFF);
76                         if (ret)
77                                 return ret;
78                         ret = dw9714_i2c_write(client, DIRECT_VCM);
79                         if (ret)
80                                 return ret;
81                         ret = dw9714_i2c_write(client, VCM_PROTECTION_ON);
82                         if (ret)
83                                 return ret;
84                         dw9714_dev.vcm_settings.update = false;
85                 }
86                 ret = dw9714_i2c_write(client,
87                                         vcm_val(val, VCM_DEFAULT_S));
88                 break;
89         case DW9714_LSC:
90                 if (dw9714_dev.vcm_settings.update) {
91                         ret = dw9714_i2c_write(client, VCM_PROTECTION_OFF);
92                         if (ret)
93                                 return ret;
94                         ret = dw9714_i2c_write(client,
95                                 vcm_dlc_mclk(DLC_DISABLE, mclk));
96                         if (ret)
97                                 return ret;
98                         ret = dw9714_i2c_write(client,
99                                 vcm_tsrc(dw9714_dev.vcm_settings.t_src));
100                         if (ret)
101                                 return ret;
102                         ret = dw9714_i2c_write(client, VCM_PROTECTION_ON);
103                         if (ret)
104                                 return ret;
105                         dw9714_dev.vcm_settings.update = false;
106                 }
107                 ret = dw9714_i2c_write(client, vcm_val(val, s));
108                 break;
109         case DW9714_DLC:
110                 if (dw9714_dev.vcm_settings.update) {
111                         ret = dw9714_i2c_write(client, VCM_PROTECTION_OFF);
112                         if (ret)
113                                 return ret;
114                         ret = dw9714_i2c_write(client,
115                                         vcm_dlc_mclk(DLC_ENABLE, mclk));
116                         if (ret)
117                                 return ret;
118                         ret = dw9714_i2c_write(client,
119                                 vcm_tsrc(dw9714_dev.vcm_settings.t_src));
120                         if (ret)
121                                 return ret;
122                         ret = dw9714_i2c_write(client, VCM_PROTECTION_ON);
123                         if (ret)
124                                 return ret;
125                         dw9714_dev.vcm_settings.update = false;
126                 }
127                 ret = dw9714_i2c_write(client,
128                                         vcm_val(val, VCM_DEFAULT_S));
129                 break;
130         }
131         return ret;
132 }
133
134 int dw9714_t_focus_abs(struct v4l2_subdev *sd, s32 value)
135 {
136         int ret;
137
138         value = clamp(value, 0, DW9714_MAX_FOCUS_POS);
139         ret = dw9714_t_focus_vcm(sd, value);
140         if (ret == 0) {
141                 dw9714_dev.number_of_steps = value - dw9714_dev.focus;
142                 dw9714_dev.focus = value;
143                 getnstimeofday(&(dw9714_dev.timestamp_t_focus_abs));
144         }
145
146         return ret;
147 }
148
149 int dw9714_t_focus_abs_init(struct v4l2_subdev *sd)
150 {
151         int ret;
152
153         ret = dw9714_t_focus_vcm(sd, DW9714_DEFAULT_FOCUS_POS);
154         if (ret == 0) {
155                 dw9714_dev.number_of_steps =
156                         DW9714_DEFAULT_FOCUS_POS - dw9714_dev.focus;
157                 dw9714_dev.focus = DW9714_DEFAULT_FOCUS_POS;
158                 getnstimeofday(&(dw9714_dev.timestamp_t_focus_abs));
159         }
160
161         return ret;
162 }
163
164 int dw9714_t_focus_rel(struct v4l2_subdev *sd, s32 value)
165 {
166
167         return dw9714_t_focus_abs(sd, dw9714_dev.focus + value);
168 }
169
170 int dw9714_q_focus_status(struct v4l2_subdev *sd, s32 *value)
171 {
172         u32 status = 0;
173         struct timespec temptime;
174         const struct timespec timedelay = {
175                 0,
176                 min_t(u32, abs(dw9714_dev.number_of_steps)*DELAY_PER_STEP_NS,
177                         DELAY_MAX_PER_STEP_NS),
178         };
179
180         ktime_get_ts(&temptime);
181
182         temptime = timespec_sub(temptime, (dw9714_dev.timestamp_t_focus_abs));
183
184         if (timespec_compare(&temptime, &timedelay) <= 0) {
185                 status |= ATOMISP_FOCUS_STATUS_MOVING;
186                 status |= ATOMISP_FOCUS_HP_IN_PROGRESS;
187         } else {
188                 status |= ATOMISP_FOCUS_STATUS_ACCEPTS_NEW_MOVE;
189                 status |= ATOMISP_FOCUS_HP_COMPLETE;
190         }
191         *value = status;
192
193         return 0;
194 }
195
196 int dw9714_q_focus_abs(struct v4l2_subdev *sd, s32 *value)
197 {
198         s32 val;
199
200         dw9714_q_focus_status(sd, &val);
201
202         if (val & ATOMISP_FOCUS_STATUS_MOVING)
203                 *value  = dw9714_dev.focus - dw9714_dev.number_of_steps;
204         else
205                 *value  = dw9714_dev.focus;
206
207         return 0;
208 }
209
210 int dw9714_t_vcm_slew(struct v4l2_subdev *sd, s32 value)
211 {
212         dw9714_dev.vcm_settings.step_setting = value;
213         dw9714_dev.vcm_settings.update = true;
214
215         return 0;
216 }
217
218 int dw9714_t_vcm_timing(struct v4l2_subdev *sd, s32 value)
219 {
220         dw9714_dev.vcm_settings.t_src = value;
221         dw9714_dev.vcm_settings.update = true;
222
223         return 0;
224 }