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