]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
a49d2536 AC |
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 | ||
4a3039e2 | 60 | static int dw9714_t_focus_vcm(struct v4l2_subdev *sd, u16 val) |
a49d2536 AC |
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 | } |