Commit | Line | Data |
---|---|---|
68e342b3 DC |
1 | /* |
2 | * isph3a_af.c | |
3 | * | |
4 | * TI OMAP3 ISP - H3A AF module | |
5 | * | |
6 | * Copyright (C) 2010 Nokia Corporation | |
7 | * Copyright (C) 2009 Texas Instruments, Inc. | |
8 | * | |
9 | * Contacts: David Cohen <dacohen@gmail.com> | |
10 | * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
11 | * Sakari Ailus <sakari.ailus@iki.fi> | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or modify | |
14 | * it under the terms of the GNU General Public License version 2 as | |
15 | * published by the Free Software Foundation. | |
16 | * | |
17 | * This program is distributed in the hope that it will be useful, but | |
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
20 | * General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with this program; if not, write to the Free Software | |
24 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
25 | * 02110-1301 USA | |
26 | */ | |
27 | ||
28 | /* Linux specific include files */ | |
29 | #include <linux/device.h> | |
30 | #include <linux/slab.h> | |
31 | ||
32 | #include "isp.h" | |
33 | #include "isph3a.h" | |
34 | #include "ispstat.h" | |
35 | ||
36 | #define IS_OUT_OF_BOUNDS(value, min, max) \ | |
37 | (((value) < (min)) || ((value) > (max))) | |
38 | ||
39 | static void h3a_af_setup_regs(struct ispstat *af, void *priv) | |
40 | { | |
41 | struct omap3isp_h3a_af_config *conf = priv; | |
42 | u32 pcr; | |
43 | u32 pax1; | |
44 | u32 pax2; | |
45 | u32 paxstart; | |
46 | u32 coef; | |
47 | u32 base_coef_set0; | |
48 | u32 base_coef_set1; | |
49 | int index; | |
50 | ||
51 | if (af->state == ISPSTAT_DISABLED) | |
52 | return; | |
53 | ||
54 | isp_reg_writel(af->isp, af->active_buf->iommu_addr, OMAP3_ISP_IOMEM_H3A, | |
55 | ISPH3A_AFBUFST); | |
56 | ||
57 | if (!af->update) | |
58 | return; | |
59 | ||
60 | /* Configure Hardware Registers */ | |
61 | pax1 = ((conf->paxel.width >> 1) - 1) << AF_PAXW_SHIFT; | |
62 | /* Set height in AFPAX1 */ | |
63 | pax1 |= (conf->paxel.height >> 1) - 1; | |
64 | isp_reg_writel(af->isp, pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1); | |
65 | ||
66 | /* Configure AFPAX2 Register */ | |
67 | /* Set Line Increment in AFPAX2 Register */ | |
68 | pax2 = ((conf->paxel.line_inc >> 1) - 1) << AF_LINE_INCR_SHIFT; | |
69 | /* Set Vertical Count */ | |
70 | pax2 |= (conf->paxel.v_cnt - 1) << AF_VT_COUNT_SHIFT; | |
71 | /* Set Horizontal Count */ | |
72 | pax2 |= (conf->paxel.h_cnt - 1); | |
73 | isp_reg_writel(af->isp, pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2); | |
74 | ||
75 | /* Configure PAXSTART Register */ | |
76 | /*Configure Horizontal Start */ | |
77 | paxstart = conf->paxel.h_start << AF_HZ_START_SHIFT; | |
78 | /* Configure Vertical Start */ | |
79 | paxstart |= conf->paxel.v_start; | |
80 | isp_reg_writel(af->isp, paxstart, OMAP3_ISP_IOMEM_H3A, | |
81 | ISPH3A_AFPAXSTART); | |
82 | ||
83 | /*SetIIRSH Register */ | |
84 | isp_reg_writel(af->isp, conf->iir.h_start, | |
85 | OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH); | |
86 | ||
87 | base_coef_set0 = ISPH3A_AFCOEF010; | |
88 | base_coef_set1 = ISPH3A_AFCOEF110; | |
89 | for (index = 0; index <= 8; index += 2) { | |
90 | /*Set IIR Filter0 Coefficients */ | |
91 | coef = 0; | |
92 | coef |= conf->iir.coeff_set0[index]; | |
93 | coef |= conf->iir.coeff_set0[index + 1] << | |
94 | AF_COEF_SHIFT; | |
95 | isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A, | |
96 | base_coef_set0); | |
97 | base_coef_set0 += AFCOEF_OFFSET; | |
98 | ||
99 | /*Set IIR Filter1 Coefficients */ | |
100 | coef = 0; | |
101 | coef |= conf->iir.coeff_set1[index]; | |
102 | coef |= conf->iir.coeff_set1[index + 1] << | |
103 | AF_COEF_SHIFT; | |
104 | isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A, | |
105 | base_coef_set1); | |
106 | base_coef_set1 += AFCOEF_OFFSET; | |
107 | } | |
108 | /* set AFCOEF0010 Register */ | |
109 | isp_reg_writel(af->isp, conf->iir.coeff_set0[10], | |
110 | OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010); | |
111 | /* set AFCOEF1010 Register */ | |
112 | isp_reg_writel(af->isp, conf->iir.coeff_set1[10], | |
113 | OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010); | |
114 | ||
115 | /* PCR Register */ | |
116 | /* Set RGB Position */ | |
117 | pcr = conf->rgb_pos << AF_RGBPOS_SHIFT; | |
118 | /* Set Accumulator Mode */ | |
119 | if (conf->fvmode == OMAP3ISP_AF_MODE_PEAK) | |
120 | pcr |= AF_FVMODE; | |
121 | /* Set A-law */ | |
122 | if (conf->alaw_enable) | |
123 | pcr |= AF_ALAW_EN; | |
124 | /* HMF Configurations */ | |
125 | if (conf->hmf.enable) { | |
126 | /* Enable HMF */ | |
127 | pcr |= AF_MED_EN; | |
128 | /* Set Median Threshold */ | |
129 | pcr |= conf->hmf.threshold << AF_MED_TH_SHIFT; | |
130 | } | |
131 | /* Set PCR Register */ | |
132 | isp_reg_clr_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | |
133 | AF_PCR_MASK, pcr); | |
134 | ||
135 | af->update = 0; | |
136 | af->config_counter += af->inc_config; | |
137 | af->inc_config = 0; | |
138 | af->buf_size = conf->buf_size; | |
139 | } | |
140 | ||
141 | static void h3a_af_enable(struct ispstat *af, int enable) | |
142 | { | |
143 | if (enable) { | |
144 | isp_reg_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | |
145 | ISPH3A_PCR_AF_EN); | |
be9a1b98 | 146 | omap3isp_subclk_enable(af->isp, OMAP3_ISP_SUBCLK_AF); |
68e342b3 DC |
147 | } else { |
148 | isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | |
149 | ISPH3A_PCR_AF_EN); | |
be9a1b98 | 150 | omap3isp_subclk_disable(af->isp, OMAP3_ISP_SUBCLK_AF); |
68e342b3 DC |
151 | } |
152 | } | |
153 | ||
154 | static int h3a_af_busy(struct ispstat *af) | |
155 | { | |
156 | return isp_reg_readl(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) | |
157 | & ISPH3A_PCR_BUSYAF; | |
158 | } | |
159 | ||
160 | static u32 h3a_af_get_buf_size(struct omap3isp_h3a_af_config *conf) | |
161 | { | |
162 | return conf->paxel.h_cnt * conf->paxel.v_cnt * OMAP3ISP_AF_PAXEL_SIZE; | |
163 | } | |
164 | ||
165 | /* Function to check paxel parameters */ | |
166 | static int h3a_af_validate_params(struct ispstat *af, void *new_conf) | |
167 | { | |
168 | struct omap3isp_h3a_af_config *user_cfg = new_conf; | |
169 | struct omap3isp_h3a_af_paxel *paxel_cfg = &user_cfg->paxel; | |
170 | struct omap3isp_h3a_af_iir *iir_cfg = &user_cfg->iir; | |
171 | int index; | |
172 | u32 buf_size; | |
173 | ||
174 | /* Check horizontal Count */ | |
175 | if (IS_OUT_OF_BOUNDS(paxel_cfg->h_cnt, | |
176 | OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN, | |
177 | OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MAX)) | |
178 | return -EINVAL; | |
179 | ||
180 | /* Check Vertical Count */ | |
181 | if (IS_OUT_OF_BOUNDS(paxel_cfg->v_cnt, | |
182 | OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN, | |
183 | OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MAX)) | |
184 | return -EINVAL; | |
185 | ||
186 | if (IS_OUT_OF_BOUNDS(paxel_cfg->height, OMAP3ISP_AF_PAXEL_HEIGHT_MIN, | |
187 | OMAP3ISP_AF_PAXEL_HEIGHT_MAX) || | |
188 | paxel_cfg->height % 2) | |
189 | return -EINVAL; | |
190 | ||
191 | /* Check width */ | |
192 | if (IS_OUT_OF_BOUNDS(paxel_cfg->width, OMAP3ISP_AF_PAXEL_WIDTH_MIN, | |
193 | OMAP3ISP_AF_PAXEL_WIDTH_MAX) || | |
194 | paxel_cfg->width % 2) | |
195 | return -EINVAL; | |
196 | ||
197 | /* Check Line Increment */ | |
198 | if (IS_OUT_OF_BOUNDS(paxel_cfg->line_inc, | |
199 | OMAP3ISP_AF_PAXEL_INCREMENT_MIN, | |
200 | OMAP3ISP_AF_PAXEL_INCREMENT_MAX) || | |
201 | paxel_cfg->line_inc % 2) | |
202 | return -EINVAL; | |
203 | ||
204 | /* Check Horizontal Start */ | |
205 | if ((paxel_cfg->h_start < iir_cfg->h_start) || | |
206 | IS_OUT_OF_BOUNDS(paxel_cfg->h_start, | |
207 | OMAP3ISP_AF_PAXEL_HZSTART_MIN, | |
208 | OMAP3ISP_AF_PAXEL_HZSTART_MAX)) | |
209 | return -EINVAL; | |
210 | ||
211 | /* Check IIR */ | |
212 | for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) { | |
213 | if ((iir_cfg->coeff_set0[index]) > OMAP3ISP_AF_COEF_MAX) | |
214 | return -EINVAL; | |
215 | ||
216 | if ((iir_cfg->coeff_set1[index]) > OMAP3ISP_AF_COEF_MAX) | |
217 | return -EINVAL; | |
218 | } | |
219 | ||
220 | if (IS_OUT_OF_BOUNDS(iir_cfg->h_start, OMAP3ISP_AF_IIRSH_MIN, | |
221 | OMAP3ISP_AF_IIRSH_MAX)) | |
222 | return -EINVAL; | |
223 | ||
224 | /* Hack: If paxel size is 12, the 10th AF window may be corrupted */ | |
225 | if ((paxel_cfg->h_cnt * paxel_cfg->v_cnt > 9) && | |
226 | (paxel_cfg->width * paxel_cfg->height == 12)) | |
227 | return -EINVAL; | |
228 | ||
229 | buf_size = h3a_af_get_buf_size(user_cfg); | |
230 | if (buf_size > user_cfg->buf_size) | |
231 | /* User buf_size request wasn't enough */ | |
232 | user_cfg->buf_size = buf_size; | |
233 | else if (user_cfg->buf_size > OMAP3ISP_AF_MAX_BUF_SIZE) | |
234 | user_cfg->buf_size = OMAP3ISP_AF_MAX_BUF_SIZE; | |
235 | ||
236 | return 0; | |
237 | } | |
238 | ||
239 | /* Update local parameters */ | |
240 | static void h3a_af_set_params(struct ispstat *af, void *new_conf) | |
241 | { | |
242 | struct omap3isp_h3a_af_config *user_cfg = new_conf; | |
243 | struct omap3isp_h3a_af_config *cur_cfg = af->priv; | |
244 | int update = 0; | |
245 | int index; | |
246 | ||
247 | /* alaw */ | |
248 | if (cur_cfg->alaw_enable != user_cfg->alaw_enable) { | |
249 | update = 1; | |
250 | goto out; | |
251 | } | |
252 | ||
253 | /* hmf */ | |
254 | if (cur_cfg->hmf.enable != user_cfg->hmf.enable) { | |
255 | update = 1; | |
256 | goto out; | |
257 | } | |
258 | if (cur_cfg->hmf.threshold != user_cfg->hmf.threshold) { | |
259 | update = 1; | |
260 | goto out; | |
261 | } | |
262 | ||
263 | /* rgbpos */ | |
264 | if (cur_cfg->rgb_pos != user_cfg->rgb_pos) { | |
265 | update = 1; | |
266 | goto out; | |
267 | } | |
268 | ||
269 | /* iir */ | |
270 | if (cur_cfg->iir.h_start != user_cfg->iir.h_start) { | |
271 | update = 1; | |
272 | goto out; | |
273 | } | |
274 | for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) { | |
275 | if (cur_cfg->iir.coeff_set0[index] != | |
276 | user_cfg->iir.coeff_set0[index]) { | |
277 | update = 1; | |
278 | goto out; | |
279 | } | |
280 | if (cur_cfg->iir.coeff_set1[index] != | |
281 | user_cfg->iir.coeff_set1[index]) { | |
282 | update = 1; | |
283 | goto out; | |
284 | } | |
285 | } | |
286 | ||
287 | /* paxel */ | |
288 | if ((cur_cfg->paxel.width != user_cfg->paxel.width) || | |
289 | (cur_cfg->paxel.height != user_cfg->paxel.height) || | |
290 | (cur_cfg->paxel.h_start != user_cfg->paxel.h_start) || | |
291 | (cur_cfg->paxel.v_start != user_cfg->paxel.v_start) || | |
292 | (cur_cfg->paxel.h_cnt != user_cfg->paxel.h_cnt) || | |
293 | (cur_cfg->paxel.v_cnt != user_cfg->paxel.v_cnt) || | |
294 | (cur_cfg->paxel.line_inc != user_cfg->paxel.line_inc)) { | |
295 | update = 1; | |
296 | goto out; | |
297 | } | |
298 | ||
299 | /* af_mode */ | |
300 | if (cur_cfg->fvmode != user_cfg->fvmode) | |
301 | update = 1; | |
302 | ||
303 | out: | |
304 | if (update || !af->configured) { | |
305 | memcpy(cur_cfg, user_cfg, sizeof(*cur_cfg)); | |
306 | af->inc_config++; | |
307 | af->update = 1; | |
308 | /* | |
309 | * User might be asked for a bigger buffer than necessary for | |
310 | * this configuration. In order to return the right amount of | |
311 | * data during buffer request, let's calculate the size here | |
312 | * instead of stick with user_cfg->buf_size. | |
313 | */ | |
314 | cur_cfg->buf_size = h3a_af_get_buf_size(cur_cfg); | |
315 | } | |
316 | } | |
317 | ||
318 | static long h3a_af_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | |
319 | { | |
320 | struct ispstat *stat = v4l2_get_subdevdata(sd); | |
321 | ||
322 | switch (cmd) { | |
323 | case VIDIOC_OMAP3ISP_AF_CFG: | |
324 | return omap3isp_stat_config(stat, arg); | |
325 | case VIDIOC_OMAP3ISP_STAT_REQ: | |
326 | return omap3isp_stat_request_statistics(stat, arg); | |
327 | case VIDIOC_OMAP3ISP_STAT_EN: { | |
328 | int *en = arg; | |
329 | return omap3isp_stat_enable(stat, !!*en); | |
330 | } | |
331 | } | |
332 | ||
333 | return -ENOIOCTLCMD; | |
334 | ||
335 | } | |
336 | ||
337 | static const struct ispstat_ops h3a_af_ops = { | |
338 | .validate_params = h3a_af_validate_params, | |
339 | .set_params = h3a_af_set_params, | |
340 | .setup_regs = h3a_af_setup_regs, | |
341 | .enable = h3a_af_enable, | |
342 | .busy = h3a_af_busy, | |
343 | }; | |
344 | ||
345 | static const struct v4l2_subdev_core_ops h3a_af_subdev_core_ops = { | |
346 | .ioctl = h3a_af_ioctl, | |
347 | .subscribe_event = omap3isp_stat_subscribe_event, | |
348 | .unsubscribe_event = omap3isp_stat_unsubscribe_event, | |
349 | }; | |
350 | ||
351 | static const struct v4l2_subdev_video_ops h3a_af_subdev_video_ops = { | |
352 | .s_stream = omap3isp_stat_s_stream, | |
353 | }; | |
354 | ||
355 | static const struct v4l2_subdev_ops h3a_af_subdev_ops = { | |
356 | .core = &h3a_af_subdev_core_ops, | |
357 | .video = &h3a_af_subdev_video_ops, | |
358 | }; | |
359 | ||
360 | /* Function to register the AF character device driver. */ | |
361 | int omap3isp_h3a_af_init(struct isp_device *isp) | |
362 | { | |
363 | struct ispstat *af = &isp->isp_af; | |
364 | struct omap3isp_h3a_af_config *af_cfg; | |
365 | struct omap3isp_h3a_af_config *af_recover_cfg; | |
68e342b3 | 366 | |
cf2b4cf6 | 367 | af_cfg = devm_kzalloc(isp->dev, sizeof(*af_cfg), GFP_KERNEL); |
68e342b3 DC |
368 | if (af_cfg == NULL) |
369 | return -ENOMEM; | |
370 | ||
68e342b3 DC |
371 | af->ops = &h3a_af_ops; |
372 | af->priv = af_cfg; | |
373 | af->dma_ch = -1; | |
374 | af->event_type = V4L2_EVENT_OMAP3ISP_AF; | |
375 | af->isp = isp; | |
376 | ||
377 | /* Set recover state configuration */ | |
cf2b4cf6 LP |
378 | af_recover_cfg = devm_kzalloc(isp->dev, sizeof(*af_recover_cfg), |
379 | GFP_KERNEL); | |
68e342b3 DC |
380 | if (!af_recover_cfg) { |
381 | dev_err(af->isp->dev, "AF: cannot allocate memory for recover " | |
382 | "configuration.\n"); | |
cf2b4cf6 | 383 | return -ENOMEM; |
68e342b3 DC |
384 | } |
385 | ||
386 | af_recover_cfg->paxel.h_start = OMAP3ISP_AF_PAXEL_HZSTART_MIN; | |
387 | af_recover_cfg->paxel.width = OMAP3ISP_AF_PAXEL_WIDTH_MIN; | |
388 | af_recover_cfg->paxel.height = OMAP3ISP_AF_PAXEL_HEIGHT_MIN; | |
389 | af_recover_cfg->paxel.h_cnt = OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN; | |
390 | af_recover_cfg->paxel.v_cnt = OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN; | |
391 | af_recover_cfg->paxel.line_inc = OMAP3ISP_AF_PAXEL_INCREMENT_MIN; | |
392 | if (h3a_af_validate_params(af, af_recover_cfg)) { | |
393 | dev_err(af->isp->dev, "AF: recover configuration is " | |
394 | "invalid.\n"); | |
cf2b4cf6 | 395 | return -EINVAL; |
68e342b3 DC |
396 | } |
397 | ||
398 | af_recover_cfg->buf_size = h3a_af_get_buf_size(af_recover_cfg); | |
399 | af->recover_priv = af_recover_cfg; | |
400 | ||
cf2b4cf6 | 401 | return omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops); |
68e342b3 DC |
402 | } |
403 | ||
404 | void omap3isp_h3a_af_cleanup(struct isp_device *isp) | |
405 | { | |
63b4ca23 | 406 | omap3isp_stat_cleanup(&isp->isp_af); |
68e342b3 | 407 | } |