]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blame - drivers/staging/media/atomisp/i2c/imx/dw9714.c
Merge remote-tracking branches 'asoc/topic/ac97', 'asoc/topic/ac97-mfd', 'asoc/topic...
[mirror_ubuntu-focal-kernel.git] / drivers / staging / media / atomisp / i2c / imx / dw9714.c
CommitLineData
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
24static struct dw9714_device dw9714_dev;
25static 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
43int 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
54int dw9714_vcm_power_down(struct v4l2_subdev *sd)
55{
56 return dw9714_dev.platform_data->power_ctrl(sd, 0);
57}
58
59
4a3039e2 60static 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
134int 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
149int 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
164int 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
170int 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
196int 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
210int 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
218int 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}