• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Support for dw9718 vcm driver.
3  *
4  * Copyright (c) 2014 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 "dw9718.h"
24 
25 static struct dw9718_device dw9718_dev;
26 
dw9718_i2c_rd8(struct i2c_client * client,u8 reg,u8 * val)27 static int dw9718_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 = DW9718_VCM_ADDR;
33 	msg[0].flags = 0;
34 	msg[0].len = 1;
35 	msg[0].buf = buf;
36 
37 	msg[1].addr = DW9718_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 
dw9718_i2c_wr8(struct i2c_client * client,u8 reg,u8 val)50 static int dw9718_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 = DW9718_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 
dw9718_i2c_wr16(struct i2c_client * client,u8 reg,u16 val)66 static int dw9718_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 = DW9718_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 
dw9718_t_focus_abs(struct v4l2_subdev * sd,s32 value)82 int dw9718_t_focus_abs(struct v4l2_subdev *sd, s32 value)
83 {
84 	struct i2c_client *client = v4l2_get_subdevdata(sd);
85 	int ret;
86 
87 	value = clamp(value, 0, DW9718_MAX_FOCUS_POS);
88 	ret = dw9718_i2c_wr16(client, DW9718_DATA_M, value);
89 	/*pr_info("%s: value = %d\n", __func__, value);*/
90 	if (ret < 0)
91 		return ret;
92 
93 	getnstimeofday(&dw9718_dev.focus_time);
94 	dw9718_dev.focus = value;
95 
96 	return 0;
97 }
98 
dw9718_vcm_power_up(struct v4l2_subdev * sd)99 int dw9718_vcm_power_up(struct v4l2_subdev *sd)
100 {
101 	struct i2c_client *client = v4l2_get_subdevdata(sd);
102 	int ret;
103 	u8 value;
104 
105 	if (dw9718_dev.power_on)
106 		return 0;
107 
108 	/* Enable power */
109 	ret = dw9718_dev.platform_data->power_ctrl(sd, 1);
110 	if (ret) {
111 		dev_err(&client->dev, "DW9718_PD power_ctrl failed %d\n", ret);
112 		return ret;
113 	}
114 	/* Wait for VBAT to stabilize */
115 	udelay(100);
116 
117 	/* Detect device */
118 	ret = dw9718_i2c_rd8(client, DW9718_SACT, &value);
119 	if (ret < 0) {
120 		dev_err(&client->dev, "read DW9718_SACT failed %d\n", ret);
121 		goto fail_powerdown;
122 	}
123 	/*
124 	 * WORKAROUND: for module P8V12F-203 which are used on
125 	 * Cherrytrail Refresh Davis Reef AoB, register SACT is not
126 	 * returning default value as spec. But VCM works as expected and
127 	 * root cause is still under discussion with vendor.
128 	 * workaround here to avoid aborting the power up sequence and just
129 	 * give a warning about this error.
130 	 */
131 	if (value != DW9718_SACT_DEFAULT_VAL)
132 		dev_warn(&client->dev, "%s error, incorrect ID\n", __func__);
133 
134 	/* Initialize according to recommended settings */
135 	ret = dw9718_i2c_wr8(client, DW9718_CONTROL,
136 			     DW9718_CONTROL_SW_LINEAR |
137 			     DW9718_CONTROL_S_SAC4 |
138 			     DW9718_CONTROL_OCP_DISABLE |
139 			     DW9718_CONTROL_UVLO_DISABLE);
140 	if (ret < 0) {
141 		dev_err(&client->dev, "write DW9718_CONTROL  failed %d\n", ret);
142 		goto fail_powerdown;
143 	}
144 	ret = dw9718_i2c_wr8(client, DW9718_SACT,
145 			     DW9718_SACT_MULT_TWO |
146 			     DW9718_SACT_PERIOD_8_8MS);
147 	if (ret < 0) {
148 		dev_err(&client->dev, "write DW9718_SACT  failed %d\n", ret);
149 		goto fail_powerdown;
150 	}
151 
152 	ret = dw9718_t_focus_abs(sd, dw9718_dev.focus);
153 	if (ret)
154 		return ret;
155 	dw9718_dev.initialized = true;
156 	dw9718_dev.power_on = 1;
157 
158 	return 0;
159 
160 fail_powerdown:
161 	dev_err(&client->dev, "%s error, powerup failed\n", __func__);
162 	dw9718_dev.platform_data->power_ctrl(sd, 0);
163 	return ret;
164 }
165 
dw9718_vcm_power_down(struct v4l2_subdev * sd)166 int dw9718_vcm_power_down(struct v4l2_subdev *sd)
167 {
168 	struct i2c_client *client = v4l2_get_subdevdata(sd);
169 	int ret;
170 
171 	if (!dw9718_dev.power_on)
172 		return 0;
173 
174 	ret =  dw9718_dev.platform_data->power_ctrl(sd, 0);
175 	if (ret) {
176 		dev_err(&client->dev, "%s power_ctrl failed\n",
177 				__func__);
178 		return ret;
179 	}
180 	dw9718_dev.power_on = 0;
181 
182 	return 0;
183 }
184 
dw9718_q_focus_status(struct v4l2_subdev * sd,s32 * value)185 int dw9718_q_focus_status(struct v4l2_subdev *sd, s32 *value)
186 {
187 	static const struct timespec move_time = {
188 		.tv_sec = 0,
189 		.tv_nsec = 60000000
190 	};
191 	struct timespec current_time, finish_time, delta_time;
192 
193 	getnstimeofday(&current_time);
194 	finish_time = timespec_add(dw9718_dev.focus_time, move_time);
195 	delta_time = timespec_sub(current_time, finish_time);
196 	if (delta_time.tv_sec >= 0 && delta_time.tv_nsec >= 0) {
197 		*value = ATOMISP_FOCUS_HP_COMPLETE |
198 			 ATOMISP_FOCUS_STATUS_ACCEPTS_NEW_MOVE;
199 	} else {
200 		*value = ATOMISP_FOCUS_STATUS_MOVING |
201 			 ATOMISP_FOCUS_HP_IN_PROGRESS;
202 	}
203 
204 	return 0;
205 }
206 
dw9718_t_focus_rel(struct v4l2_subdev * sd,s32 value)207 int dw9718_t_focus_rel(struct v4l2_subdev *sd, s32 value)
208 {
209 	return dw9718_t_focus_abs(sd, dw9718_dev.focus + value);
210 }
211 
dw9718_q_focus_abs(struct v4l2_subdev * sd,s32 * value)212 int dw9718_q_focus_abs(struct v4l2_subdev *sd, s32 *value)
213 {
214 	*value  = dw9718_dev.focus;
215 	return 0;
216 }
dw9718_t_vcm_slew(struct v4l2_subdev * sd,s32 value)217 int dw9718_t_vcm_slew(struct v4l2_subdev *sd, s32 value)
218 {
219 	return 0;
220 }
221 
dw9718_t_vcm_timing(struct v4l2_subdev * sd,s32 value)222 int dw9718_t_vcm_timing(struct v4l2_subdev *sd, s32 value)
223 {
224 	return 0;
225 }
226 
dw9718_vcm_init(struct v4l2_subdev * sd)227 int dw9718_vcm_init(struct v4l2_subdev *sd)
228 {
229 	dw9718_dev.platform_data = camera_get_af_platform_data();
230 	dw9718_dev.focus = DW9718_DEFAULT_FOCUS_POSITION;
231 	dw9718_dev.power_on = 0;
232 	return (NULL == dw9718_dev.platform_data) ? -ENODEV : 0;
233 }
234