diff options
Diffstat (limited to 'drivers/media/video/omap3isp/isph3a_aewb.c')
-rw-r--r-- | drivers/media/video/omap3isp/isph3a_aewb.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/drivers/media/video/omap3isp/isph3a_aewb.c b/drivers/media/video/omap3isp/isph3a_aewb.c new file mode 100644 index 00000000000..8068cefd8d8 --- /dev/null +++ b/drivers/media/video/omap3isp/isph3a_aewb.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /* | ||
2 | * isph3a.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - H3A 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 | #include <linux/slab.h> | ||
29 | #include <linux/uaccess.h> | ||
30 | |||
31 | #include "isp.h" | ||
32 | #include "isph3a.h" | ||
33 | #include "ispstat.h" | ||
34 | |||
35 | /* | ||
36 | * h3a_aewb_update_regs - Helper function to update h3a registers. | ||
37 | */ | ||
38 | static void h3a_aewb_setup_regs(struct ispstat *aewb, void *priv) | ||
39 | { | ||
40 | struct omap3isp_h3a_aewb_config *conf = priv; | ||
41 | u32 pcr; | ||
42 | u32 win1; | ||
43 | u32 start; | ||
44 | u32 blk; | ||
45 | u32 subwin; | ||
46 | |||
47 | if (aewb->state == ISPSTAT_DISABLED) | ||
48 | return; | ||
49 | |||
50 | isp_reg_writel(aewb->isp, aewb->active_buf->iommu_addr, | ||
51 | OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST); | ||
52 | |||
53 | if (!aewb->update) | ||
54 | return; | ||
55 | |||
56 | /* Converting config metadata into reg values */ | ||
57 | pcr = conf->saturation_limit << ISPH3A_PCR_AEW_AVE2LMT_SHIFT; | ||
58 | pcr |= !!conf->alaw_enable << ISPH3A_PCR_AEW_ALAW_EN_SHIFT; | ||
59 | |||
60 | win1 = ((conf->win_height >> 1) - 1) << ISPH3A_AEWWIN1_WINH_SHIFT; | ||
61 | win1 |= ((conf->win_width >> 1) - 1) << ISPH3A_AEWWIN1_WINW_SHIFT; | ||
62 | win1 |= (conf->ver_win_count - 1) << ISPH3A_AEWWIN1_WINVC_SHIFT; | ||
63 | win1 |= (conf->hor_win_count - 1) << ISPH3A_AEWWIN1_WINHC_SHIFT; | ||
64 | |||
65 | start = conf->hor_win_start << ISPH3A_AEWINSTART_WINSH_SHIFT; | ||
66 | start |= conf->ver_win_start << ISPH3A_AEWINSTART_WINSV_SHIFT; | ||
67 | |||
68 | blk = conf->blk_ver_win_start << ISPH3A_AEWINBLK_WINSV_SHIFT; | ||
69 | blk |= ((conf->blk_win_height >> 1) - 1) << ISPH3A_AEWINBLK_WINH_SHIFT; | ||
70 | |||
71 | subwin = ((conf->subsample_ver_inc >> 1) - 1) << | ||
72 | ISPH3A_AEWSUBWIN_AEWINCV_SHIFT; | ||
73 | subwin |= ((conf->subsample_hor_inc >> 1) - 1) << | ||
74 | ISPH3A_AEWSUBWIN_AEWINCH_SHIFT; | ||
75 | |||
76 | isp_reg_writel(aewb->isp, win1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1); | ||
77 | isp_reg_writel(aewb->isp, start, OMAP3_ISP_IOMEM_H3A, | ||
78 | ISPH3A_AEWINSTART); | ||
79 | isp_reg_writel(aewb->isp, blk, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK); | ||
80 | isp_reg_writel(aewb->isp, subwin, OMAP3_ISP_IOMEM_H3A, | ||
81 | ISPH3A_AEWSUBWIN); | ||
82 | isp_reg_clr_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
83 | ISPH3A_PCR_AEW_MASK, pcr); | ||
84 | |||
85 | aewb->update = 0; | ||
86 | aewb->config_counter += aewb->inc_config; | ||
87 | aewb->inc_config = 0; | ||
88 | aewb->buf_size = conf->buf_size; | ||
89 | } | ||
90 | |||
91 | static void h3a_aewb_enable(struct ispstat *aewb, int enable) | ||
92 | { | ||
93 | if (enable) { | ||
94 | isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
95 | ISPH3A_PCR_AEW_EN); | ||
96 | /* This bit is already set if AF is enabled */ | ||
97 | if (aewb->isp->isp_af.state != ISPSTAT_ENABLED) | ||
98 | isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
99 | ISPCTRL_H3A_CLK_EN); | ||
100 | } else { | ||
101 | isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, | ||
102 | ISPH3A_PCR_AEW_EN); | ||
103 | /* This bit can't be cleared if AF is enabled */ | ||
104 | if (aewb->isp->isp_af.state != ISPSTAT_ENABLED) | ||
105 | isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
106 | ISPCTRL_H3A_CLK_EN); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | static int h3a_aewb_busy(struct ispstat *aewb) | ||
111 | { | ||
112 | return isp_reg_readl(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) | ||
113 | & ISPH3A_PCR_BUSYAEAWB; | ||
114 | } | ||
115 | |||
116 | static u32 h3a_aewb_get_buf_size(struct omap3isp_h3a_aewb_config *conf) | ||
117 | { | ||
118 | /* Number of configured windows + extra row for black data */ | ||
119 | u32 win_count = (conf->ver_win_count + 1) * conf->hor_win_count; | ||
120 | |||
121 | /* | ||
122 | * Unsaturated block counts for each 8 windows. | ||
123 | * 1 extra for the last (win_count % 8) windows if win_count is not | ||
124 | * divisible by 8. | ||
125 | */ | ||
126 | win_count += (win_count + 7) / 8; | ||
127 | |||
128 | return win_count * AEWB_PACKET_SIZE; | ||
129 | } | ||
130 | |||
131 | static int h3a_aewb_validate_params(struct ispstat *aewb, void *new_conf) | ||
132 | { | ||
133 | struct omap3isp_h3a_aewb_config *user_cfg = new_conf; | ||
134 | u32 buf_size; | ||
135 | |||
136 | if (unlikely(user_cfg->saturation_limit > | ||
137 | OMAP3ISP_AEWB_MAX_SATURATION_LIM)) | ||
138 | return -EINVAL; | ||
139 | |||
140 | if (unlikely(user_cfg->win_height < OMAP3ISP_AEWB_MIN_WIN_H || | ||
141 | user_cfg->win_height > OMAP3ISP_AEWB_MAX_WIN_H || | ||
142 | user_cfg->win_height & 0x01)) | ||
143 | return -EINVAL; | ||
144 | |||
145 | if (unlikely(user_cfg->win_width < OMAP3ISP_AEWB_MIN_WIN_W || | ||
146 | user_cfg->win_width > OMAP3ISP_AEWB_MAX_WIN_W || | ||
147 | user_cfg->win_width & 0x01)) | ||
148 | return -EINVAL; | ||
149 | |||
150 | if (unlikely(user_cfg->ver_win_count < OMAP3ISP_AEWB_MIN_WINVC || | ||
151 | user_cfg->ver_win_count > OMAP3ISP_AEWB_MAX_WINVC)) | ||
152 | return -EINVAL; | ||
153 | |||
154 | if (unlikely(user_cfg->hor_win_count < OMAP3ISP_AEWB_MIN_WINHC || | ||
155 | user_cfg->hor_win_count > OMAP3ISP_AEWB_MAX_WINHC)) | ||
156 | return -EINVAL; | ||
157 | |||
158 | if (unlikely(user_cfg->ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) | ||
159 | return -EINVAL; | ||
160 | |||
161 | if (unlikely(user_cfg->hor_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) | ||
162 | return -EINVAL; | ||
163 | |||
164 | if (unlikely(user_cfg->blk_ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) | ||
165 | return -EINVAL; | ||
166 | |||
167 | if (unlikely(user_cfg->blk_win_height < OMAP3ISP_AEWB_MIN_WIN_H || | ||
168 | user_cfg->blk_win_height > OMAP3ISP_AEWB_MAX_WIN_H || | ||
169 | user_cfg->blk_win_height & 0x01)) | ||
170 | return -EINVAL; | ||
171 | |||
172 | if (unlikely(user_cfg->subsample_ver_inc < OMAP3ISP_AEWB_MIN_SUB_INC || | ||
173 | user_cfg->subsample_ver_inc > OMAP3ISP_AEWB_MAX_SUB_INC || | ||
174 | user_cfg->subsample_ver_inc & 0x01)) | ||
175 | return -EINVAL; | ||
176 | |||
177 | if (unlikely(user_cfg->subsample_hor_inc < OMAP3ISP_AEWB_MIN_SUB_INC || | ||
178 | user_cfg->subsample_hor_inc > OMAP3ISP_AEWB_MAX_SUB_INC || | ||
179 | user_cfg->subsample_hor_inc & 0x01)) | ||
180 | return -EINVAL; | ||
181 | |||
182 | buf_size = h3a_aewb_get_buf_size(user_cfg); | ||
183 | if (buf_size > user_cfg->buf_size) | ||
184 | user_cfg->buf_size = buf_size; | ||
185 | else if (user_cfg->buf_size > OMAP3ISP_AEWB_MAX_BUF_SIZE) | ||
186 | user_cfg->buf_size = OMAP3ISP_AEWB_MAX_BUF_SIZE; | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * h3a_aewb_set_params - Helper function to check & store user given params. | ||
193 | * @new_conf: Pointer to AE and AWB parameters struct. | ||
194 | * | ||
195 | * As most of them are busy-lock registers, need to wait until AEW_BUSY = 0 to | ||
196 | * program them during ISR. | ||
197 | */ | ||
198 | static void h3a_aewb_set_params(struct ispstat *aewb, void *new_conf) | ||
199 | { | ||
200 | struct omap3isp_h3a_aewb_config *user_cfg = new_conf; | ||
201 | struct omap3isp_h3a_aewb_config *cur_cfg = aewb->priv; | ||
202 | int update = 0; | ||
203 | |||
204 | if (cur_cfg->saturation_limit != user_cfg->saturation_limit) { | ||
205 | cur_cfg->saturation_limit = user_cfg->saturation_limit; | ||
206 | update = 1; | ||
207 | } | ||
208 | if (cur_cfg->alaw_enable != user_cfg->alaw_enable) { | ||
209 | cur_cfg->alaw_enable = user_cfg->alaw_enable; | ||
210 | update = 1; | ||
211 | } | ||
212 | if (cur_cfg->win_height != user_cfg->win_height) { | ||
213 | cur_cfg->win_height = user_cfg->win_height; | ||
214 | update = 1; | ||
215 | } | ||
216 | if (cur_cfg->win_width != user_cfg->win_width) { | ||
217 | cur_cfg->win_width = user_cfg->win_width; | ||
218 | update = 1; | ||
219 | } | ||
220 | if (cur_cfg->ver_win_count != user_cfg->ver_win_count) { | ||
221 | cur_cfg->ver_win_count = user_cfg->ver_win_count; | ||
222 | update = 1; | ||
223 | } | ||
224 | if (cur_cfg->hor_win_count != user_cfg->hor_win_count) { | ||
225 | cur_cfg->hor_win_count = user_cfg->hor_win_count; | ||
226 | update = 1; | ||
227 | } | ||
228 | if (cur_cfg->ver_win_start != user_cfg->ver_win_start) { | ||
229 | cur_cfg->ver_win_start = user_cfg->ver_win_start; | ||
230 | update = 1; | ||
231 | } | ||
232 | if (cur_cfg->hor_win_start != user_cfg->hor_win_start) { | ||
233 | cur_cfg->hor_win_start = user_cfg->hor_win_start; | ||
234 | update = 1; | ||
235 | } | ||
236 | if (cur_cfg->blk_ver_win_start != user_cfg->blk_ver_win_start) { | ||
237 | cur_cfg->blk_ver_win_start = user_cfg->blk_ver_win_start; | ||
238 | update = 1; | ||
239 | } | ||
240 | if (cur_cfg->blk_win_height != user_cfg->blk_win_height) { | ||
241 | cur_cfg->blk_win_height = user_cfg->blk_win_height; | ||
242 | update = 1; | ||
243 | } | ||
244 | if (cur_cfg->subsample_ver_inc != user_cfg->subsample_ver_inc) { | ||
245 | cur_cfg->subsample_ver_inc = user_cfg->subsample_ver_inc; | ||
246 | update = 1; | ||
247 | } | ||
248 | if (cur_cfg->subsample_hor_inc != user_cfg->subsample_hor_inc) { | ||
249 | cur_cfg->subsample_hor_inc = user_cfg->subsample_hor_inc; | ||
250 | update = 1; | ||
251 | } | ||
252 | |||
253 | if (update || !aewb->configured) { | ||
254 | aewb->inc_config++; | ||
255 | aewb->update = 1; | ||
256 | cur_cfg->buf_size = h3a_aewb_get_buf_size(cur_cfg); | ||
257 | } | ||
258 | } | ||
259 | |||
260 | static long h3a_aewb_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
261 | { | ||
262 | struct ispstat *stat = v4l2_get_subdevdata(sd); | ||
263 | |||
264 | switch (cmd) { | ||
265 | case VIDIOC_OMAP3ISP_AEWB_CFG: | ||
266 | return omap3isp_stat_config(stat, arg); | ||
267 | case VIDIOC_OMAP3ISP_STAT_REQ: | ||
268 | return omap3isp_stat_request_statistics(stat, arg); | ||
269 | case VIDIOC_OMAP3ISP_STAT_EN: { | ||
270 | unsigned long *en = arg; | ||
271 | return omap3isp_stat_enable(stat, !!*en); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | return -ENOIOCTLCMD; | ||
276 | } | ||
277 | |||
278 | static const struct ispstat_ops h3a_aewb_ops = { | ||
279 | .validate_params = h3a_aewb_validate_params, | ||
280 | .set_params = h3a_aewb_set_params, | ||
281 | .setup_regs = h3a_aewb_setup_regs, | ||
282 | .enable = h3a_aewb_enable, | ||
283 | .busy = h3a_aewb_busy, | ||
284 | }; | ||
285 | |||
286 | static const struct v4l2_subdev_core_ops h3a_aewb_subdev_core_ops = { | ||
287 | .ioctl = h3a_aewb_ioctl, | ||
288 | .subscribe_event = omap3isp_stat_subscribe_event, | ||
289 | .unsubscribe_event = omap3isp_stat_unsubscribe_event, | ||
290 | }; | ||
291 | |||
292 | static const struct v4l2_subdev_video_ops h3a_aewb_subdev_video_ops = { | ||
293 | .s_stream = omap3isp_stat_s_stream, | ||
294 | }; | ||
295 | |||
296 | static const struct v4l2_subdev_ops h3a_aewb_subdev_ops = { | ||
297 | .core = &h3a_aewb_subdev_core_ops, | ||
298 | .video = &h3a_aewb_subdev_video_ops, | ||
299 | }; | ||
300 | |||
301 | /* | ||
302 | * omap3isp_h3a_aewb_init - Module Initialisation. | ||
303 | */ | ||
304 | int omap3isp_h3a_aewb_init(struct isp_device *isp) | ||
305 | { | ||
306 | struct ispstat *aewb = &isp->isp_aewb; | ||
307 | struct omap3isp_h3a_aewb_config *aewb_cfg; | ||
308 | struct omap3isp_h3a_aewb_config *aewb_recover_cfg; | ||
309 | int ret; | ||
310 | |||
311 | aewb_cfg = kzalloc(sizeof(*aewb_cfg), GFP_KERNEL); | ||
312 | if (!aewb_cfg) | ||
313 | return -ENOMEM; | ||
314 | |||
315 | memset(aewb, 0, sizeof(*aewb)); | ||
316 | aewb->ops = &h3a_aewb_ops; | ||
317 | aewb->priv = aewb_cfg; | ||
318 | aewb->dma_ch = -1; | ||
319 | aewb->event_type = V4L2_EVENT_OMAP3ISP_AEWB; | ||
320 | aewb->isp = isp; | ||
321 | |||
322 | /* Set recover state configuration */ | ||
323 | aewb_recover_cfg = kzalloc(sizeof(*aewb_recover_cfg), GFP_KERNEL); | ||
324 | if (!aewb_recover_cfg) { | ||
325 | dev_err(aewb->isp->dev, "AEWB: cannot allocate memory for " | ||
326 | "recover configuration.\n"); | ||
327 | ret = -ENOMEM; | ||
328 | goto err_recover_alloc; | ||
329 | } | ||
330 | |||
331 | aewb_recover_cfg->saturation_limit = OMAP3ISP_AEWB_MAX_SATURATION_LIM; | ||
332 | aewb_recover_cfg->win_height = OMAP3ISP_AEWB_MIN_WIN_H; | ||
333 | aewb_recover_cfg->win_width = OMAP3ISP_AEWB_MIN_WIN_W; | ||
334 | aewb_recover_cfg->ver_win_count = OMAP3ISP_AEWB_MIN_WINVC; | ||
335 | aewb_recover_cfg->hor_win_count = OMAP3ISP_AEWB_MIN_WINHC; | ||
336 | aewb_recover_cfg->blk_ver_win_start = aewb_recover_cfg->ver_win_start + | ||
337 | aewb_recover_cfg->win_height * aewb_recover_cfg->ver_win_count; | ||
338 | aewb_recover_cfg->blk_win_height = OMAP3ISP_AEWB_MIN_WIN_H; | ||
339 | aewb_recover_cfg->subsample_ver_inc = OMAP3ISP_AEWB_MIN_SUB_INC; | ||
340 | aewb_recover_cfg->subsample_hor_inc = OMAP3ISP_AEWB_MIN_SUB_INC; | ||
341 | |||
342 | if (h3a_aewb_validate_params(aewb, aewb_recover_cfg)) { | ||
343 | dev_err(aewb->isp->dev, "AEWB: recover configuration is " | ||
344 | "invalid.\n"); | ||
345 | ret = -EINVAL; | ||
346 | goto err_conf; | ||
347 | } | ||
348 | |||
349 | aewb_recover_cfg->buf_size = h3a_aewb_get_buf_size(aewb_recover_cfg); | ||
350 | aewb->recover_priv = aewb_recover_cfg; | ||
351 | |||
352 | ret = omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops); | ||
353 | if (ret) | ||
354 | goto err_conf; | ||
355 | |||
356 | return 0; | ||
357 | |||
358 | err_conf: | ||
359 | kfree(aewb_recover_cfg); | ||
360 | err_recover_alloc: | ||
361 | kfree(aewb_cfg); | ||
362 | |||
363 | return ret; | ||
364 | } | ||
365 | |||
366 | /* | ||
367 | * omap3isp_h3a_aewb_cleanup - Module exit. | ||
368 | */ | ||
369 | void omap3isp_h3a_aewb_cleanup(struct isp_device *isp) | ||
370 | { | ||
371 | kfree(isp->isp_aewb.priv); | ||
372 | kfree(isp->isp_aewb.recover_priv); | ||
373 | omap3isp_stat_free(&isp->isp_aewb); | ||
374 | } | ||