diff options
Diffstat (limited to 'drivers/media/video/omap3isp/isphist.c')
-rw-r--r-- | drivers/media/video/omap3isp/isphist.c | 520 |
1 files changed, 520 insertions, 0 deletions
diff --git a/drivers/media/video/omap3isp/isphist.c b/drivers/media/video/omap3isp/isphist.c new file mode 100644 index 000000000000..1743856b30d1 --- /dev/null +++ b/drivers/media/video/omap3isp/isphist.c | |||
@@ -0,0 +1,520 @@ | |||
1 | /* | ||
2 | * isphist.c | ||
3 | * | ||
4 | * TI OMAP3 ISP - Histogram 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/delay.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/uaccess.h> | ||
31 | #include <linux/device.h> | ||
32 | |||
33 | #include "isp.h" | ||
34 | #include "ispreg.h" | ||
35 | #include "isphist.h" | ||
36 | |||
37 | #define HIST_CONFIG_DMA 1 | ||
38 | |||
39 | #define HIST_USING_DMA(hist) ((hist)->dma_ch >= 0) | ||
40 | |||
41 | /* | ||
42 | * hist_reset_mem - clear Histogram memory before start stats engine. | ||
43 | */ | ||
44 | static void hist_reset_mem(struct ispstat *hist) | ||
45 | { | ||
46 | struct isp_device *isp = hist->isp; | ||
47 | struct omap3isp_hist_config *conf = hist->priv; | ||
48 | unsigned int i; | ||
49 | |||
50 | isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); | ||
51 | |||
52 | /* | ||
53 | * By setting it, the histogram internal buffer is being cleared at the | ||
54 | * same time it's being read. This bit must be cleared afterwards. | ||
55 | */ | ||
56 | isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); | ||
57 | |||
58 | /* | ||
59 | * We'll clear 4 words at each iteration for optimization. It avoids | ||
60 | * 3/4 of the jumps. We also know HIST_MEM_SIZE is divisible by 4. | ||
61 | */ | ||
62 | for (i = OMAP3ISP_HIST_MEM_SIZE / 4; i > 0; i--) { | ||
63 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
64 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
65 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
66 | isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
67 | } | ||
68 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); | ||
69 | |||
70 | hist->wait_acc_frames = conf->num_acc_frames; | ||
71 | } | ||
72 | |||
73 | static void hist_dma_config(struct ispstat *hist) | ||
74 | { | ||
75 | hist->dma_config.data_type = OMAP_DMA_DATA_TYPE_S32; | ||
76 | hist->dma_config.sync_mode = OMAP_DMA_SYNC_ELEMENT; | ||
77 | hist->dma_config.frame_count = 1; | ||
78 | hist->dma_config.src_amode = OMAP_DMA_AMODE_CONSTANT; | ||
79 | hist->dma_config.src_start = OMAP3ISP_HIST_REG_BASE + ISPHIST_DATA; | ||
80 | hist->dma_config.dst_amode = OMAP_DMA_AMODE_POST_INC; | ||
81 | hist->dma_config.src_or_dst_synch = OMAP_DMA_SRC_SYNC; | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * hist_setup_regs - Helper function to update Histogram registers. | ||
86 | */ | ||
87 | static void hist_setup_regs(struct ispstat *hist, void *priv) | ||
88 | { | ||
89 | struct isp_device *isp = hist->isp; | ||
90 | struct omap3isp_hist_config *conf = priv; | ||
91 | int c; | ||
92 | u32 cnt; | ||
93 | u32 wb_gain; | ||
94 | u32 reg_hor[OMAP3ISP_HIST_MAX_REGIONS]; | ||
95 | u32 reg_ver[OMAP3ISP_HIST_MAX_REGIONS]; | ||
96 | |||
97 | if (!hist->update || hist->state == ISPSTAT_DISABLED || | ||
98 | hist->state == ISPSTAT_DISABLING) | ||
99 | return; | ||
100 | |||
101 | cnt = conf->cfa << ISPHIST_CNT_CFA_SHIFT; | ||
102 | |||
103 | wb_gain = conf->wg[0] << ISPHIST_WB_GAIN_WG00_SHIFT; | ||
104 | wb_gain |= conf->wg[1] << ISPHIST_WB_GAIN_WG01_SHIFT; | ||
105 | wb_gain |= conf->wg[2] << ISPHIST_WB_GAIN_WG02_SHIFT; | ||
106 | if (conf->cfa == OMAP3ISP_HIST_CFA_BAYER) | ||
107 | wb_gain |= conf->wg[3] << ISPHIST_WB_GAIN_WG03_SHIFT; | ||
108 | |||
109 | /* Regions size and position */ | ||
110 | for (c = 0; c < OMAP3ISP_HIST_MAX_REGIONS; c++) { | ||
111 | if (c < conf->num_regions) { | ||
112 | reg_hor[c] = conf->region[c].h_start << | ||
113 | ISPHIST_REG_START_SHIFT; | ||
114 | reg_hor[c] = conf->region[c].h_end << | ||
115 | ISPHIST_REG_END_SHIFT; | ||
116 | reg_ver[c] = conf->region[c].v_start << | ||
117 | ISPHIST_REG_START_SHIFT; | ||
118 | reg_ver[c] = conf->region[c].v_end << | ||
119 | ISPHIST_REG_END_SHIFT; | ||
120 | } else { | ||
121 | reg_hor[c] = 0; | ||
122 | reg_ver[c] = 0; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | cnt |= conf->hist_bins << ISPHIST_CNT_BINS_SHIFT; | ||
127 | switch (conf->hist_bins) { | ||
128 | case OMAP3ISP_HIST_BINS_256: | ||
129 | cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 8) << | ||
130 | ISPHIST_CNT_SHIFT_SHIFT; | ||
131 | break; | ||
132 | case OMAP3ISP_HIST_BINS_128: | ||
133 | cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 7) << | ||
134 | ISPHIST_CNT_SHIFT_SHIFT; | ||
135 | break; | ||
136 | case OMAP3ISP_HIST_BINS_64: | ||
137 | cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 6) << | ||
138 | ISPHIST_CNT_SHIFT_SHIFT; | ||
139 | break; | ||
140 | default: /* OMAP3ISP_HIST_BINS_32 */ | ||
141 | cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 5) << | ||
142 | ISPHIST_CNT_SHIFT_SHIFT; | ||
143 | break; | ||
144 | } | ||
145 | |||
146 | hist_reset_mem(hist); | ||
147 | |||
148 | isp_reg_writel(isp, cnt, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT); | ||
149 | isp_reg_writel(isp, wb_gain, OMAP3_ISP_IOMEM_HIST, ISPHIST_WB_GAIN); | ||
150 | isp_reg_writel(isp, reg_hor[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_HORZ); | ||
151 | isp_reg_writel(isp, reg_ver[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_VERT); | ||
152 | isp_reg_writel(isp, reg_hor[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_HORZ); | ||
153 | isp_reg_writel(isp, reg_ver[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_VERT); | ||
154 | isp_reg_writel(isp, reg_hor[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_HORZ); | ||
155 | isp_reg_writel(isp, reg_ver[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_VERT); | ||
156 | isp_reg_writel(isp, reg_hor[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_HORZ); | ||
157 | isp_reg_writel(isp, reg_ver[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_VERT); | ||
158 | |||
159 | hist->update = 0; | ||
160 | hist->config_counter += hist->inc_config; | ||
161 | hist->inc_config = 0; | ||
162 | hist->buf_size = conf->buf_size; | ||
163 | } | ||
164 | |||
165 | static void hist_enable(struct ispstat *hist, int enable) | ||
166 | { | ||
167 | if (enable) { | ||
168 | isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, | ||
169 | ISPHIST_PCR_ENABLE); | ||
170 | isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
171 | ISPCTRL_HIST_CLK_EN); | ||
172 | } else { | ||
173 | isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, | ||
174 | ISPHIST_PCR_ENABLE); | ||
175 | isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, | ||
176 | ISPCTRL_HIST_CLK_EN); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | static int hist_busy(struct ispstat *hist) | ||
181 | { | ||
182 | return isp_reg_readl(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR) | ||
183 | & ISPHIST_PCR_BUSY; | ||
184 | } | ||
185 | |||
186 | static void hist_dma_cb(int lch, u16 ch_status, void *data) | ||
187 | { | ||
188 | struct ispstat *hist = data; | ||
189 | |||
190 | if (ch_status & ~OMAP_DMA_BLOCK_IRQ) { | ||
191 | dev_dbg(hist->isp->dev, "hist: DMA error. status = 0x%04x\n", | ||
192 | ch_status); | ||
193 | omap_stop_dma(lch); | ||
194 | hist_reset_mem(hist); | ||
195 | atomic_set(&hist->buf_err, 1); | ||
196 | } | ||
197 | isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, | ||
198 | ISPHIST_CNT_CLEAR); | ||
199 | |||
200 | omap3isp_stat_dma_isr(hist); | ||
201 | if (hist->state != ISPSTAT_DISABLED) | ||
202 | omap3isp_hist_dma_done(hist->isp); | ||
203 | } | ||
204 | |||
205 | static int hist_buf_dma(struct ispstat *hist) | ||
206 | { | ||
207 | dma_addr_t dma_addr = hist->active_buf->dma_addr; | ||
208 | |||
209 | if (unlikely(!dma_addr)) { | ||
210 | dev_dbg(hist->isp->dev, "hist: invalid DMA buffer address\n"); | ||
211 | hist_reset_mem(hist); | ||
212 | return STAT_NO_BUF; | ||
213 | } | ||
214 | |||
215 | isp_reg_writel(hist->isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); | ||
216 | isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, | ||
217 | ISPHIST_CNT_CLEAR); | ||
218 | omap3isp_flush(hist->isp); | ||
219 | hist->dma_config.dst_start = dma_addr; | ||
220 | hist->dma_config.elem_count = hist->buf_size / sizeof(u32); | ||
221 | omap_set_dma_params(hist->dma_ch, &hist->dma_config); | ||
222 | |||
223 | omap_start_dma(hist->dma_ch); | ||
224 | |||
225 | return STAT_BUF_WAITING_DMA; | ||
226 | } | ||
227 | |||
228 | static int hist_buf_pio(struct ispstat *hist) | ||
229 | { | ||
230 | struct isp_device *isp = hist->isp; | ||
231 | u32 *buf = hist->active_buf->virt_addr; | ||
232 | unsigned int i; | ||
233 | |||
234 | if (!buf) { | ||
235 | dev_dbg(isp->dev, "hist: invalid PIO buffer address\n"); | ||
236 | hist_reset_mem(hist); | ||
237 | return STAT_NO_BUF; | ||
238 | } | ||
239 | |||
240 | isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); | ||
241 | |||
242 | /* | ||
243 | * By setting it, the histogram internal buffer is being cleared at the | ||
244 | * same time it's being read. This bit must be cleared just after all | ||
245 | * data is acquired. | ||
246 | */ | ||
247 | isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); | ||
248 | |||
249 | /* | ||
250 | * We'll read 4 times a 4-bytes-word at each iteration for | ||
251 | * optimization. It avoids 3/4 of the jumps. We also know buf_size is | ||
252 | * divisible by 16. | ||
253 | */ | ||
254 | for (i = hist->buf_size / 16; i > 0; i--) { | ||
255 | *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
256 | *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
257 | *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
258 | *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
259 | } | ||
260 | isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, | ||
261 | ISPHIST_CNT_CLEAR); | ||
262 | |||
263 | return STAT_BUF_DONE; | ||
264 | } | ||
265 | |||
266 | /* | ||
267 | * hist_buf_process - Callback from ISP driver for HIST interrupt. | ||
268 | */ | ||
269 | static int hist_buf_process(struct ispstat *hist) | ||
270 | { | ||
271 | struct omap3isp_hist_config *user_cfg = hist->priv; | ||
272 | int ret; | ||
273 | |||
274 | if (atomic_read(&hist->buf_err) || hist->state != ISPSTAT_ENABLED) { | ||
275 | hist_reset_mem(hist); | ||
276 | return STAT_NO_BUF; | ||
277 | } | ||
278 | |||
279 | if (--(hist->wait_acc_frames)) | ||
280 | return STAT_NO_BUF; | ||
281 | |||
282 | if (HIST_USING_DMA(hist)) | ||
283 | ret = hist_buf_dma(hist); | ||
284 | else | ||
285 | ret = hist_buf_pio(hist); | ||
286 | |||
287 | hist->wait_acc_frames = user_cfg->num_acc_frames; | ||
288 | |||
289 | return ret; | ||
290 | } | ||
291 | |||
292 | static u32 hist_get_buf_size(struct omap3isp_hist_config *conf) | ||
293 | { | ||
294 | return OMAP3ISP_HIST_MEM_SIZE_BINS(conf->hist_bins) * conf->num_regions; | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | * hist_validate_params - Helper function to check user given params. | ||
299 | * @user_cfg: Pointer to user configuration structure. | ||
300 | * | ||
301 | * Returns 0 on success configuration. | ||
302 | */ | ||
303 | static int hist_validate_params(struct ispstat *hist, void *new_conf) | ||
304 | { | ||
305 | struct omap3isp_hist_config *user_cfg = new_conf; | ||
306 | int c; | ||
307 | u32 buf_size; | ||
308 | |||
309 | if (user_cfg->cfa > OMAP3ISP_HIST_CFA_FOVEONX3) | ||
310 | return -EINVAL; | ||
311 | |||
312 | /* Regions size and position */ | ||
313 | |||
314 | if ((user_cfg->num_regions < OMAP3ISP_HIST_MIN_REGIONS) || | ||
315 | (user_cfg->num_regions > OMAP3ISP_HIST_MAX_REGIONS)) | ||
316 | return -EINVAL; | ||
317 | |||
318 | /* Regions */ | ||
319 | for (c = 0; c < user_cfg->num_regions; c++) { | ||
320 | if (user_cfg->region[c].h_start & ~ISPHIST_REG_START_END_MASK) | ||
321 | return -EINVAL; | ||
322 | if (user_cfg->region[c].h_end & ~ISPHIST_REG_START_END_MASK) | ||
323 | return -EINVAL; | ||
324 | if (user_cfg->region[c].v_start & ~ISPHIST_REG_START_END_MASK) | ||
325 | return -EINVAL; | ||
326 | if (user_cfg->region[c].v_end & ~ISPHIST_REG_START_END_MASK) | ||
327 | return -EINVAL; | ||
328 | if (user_cfg->region[c].h_start > user_cfg->region[c].h_end) | ||
329 | return -EINVAL; | ||
330 | if (user_cfg->region[c].v_start > user_cfg->region[c].v_end) | ||
331 | return -EINVAL; | ||
332 | } | ||
333 | |||
334 | switch (user_cfg->num_regions) { | ||
335 | case 1: | ||
336 | if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_256) | ||
337 | return -EINVAL; | ||
338 | break; | ||
339 | case 2: | ||
340 | if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_128) | ||
341 | return -EINVAL; | ||
342 | break; | ||
343 | default: /* 3 or 4 */ | ||
344 | if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_64) | ||
345 | return -EINVAL; | ||
346 | break; | ||
347 | } | ||
348 | |||
349 | buf_size = hist_get_buf_size(user_cfg); | ||
350 | if (buf_size > user_cfg->buf_size) | ||
351 | /* User's buf_size request wasn't enoght */ | ||
352 | user_cfg->buf_size = buf_size; | ||
353 | else if (user_cfg->buf_size > OMAP3ISP_HIST_MAX_BUF_SIZE) | ||
354 | user_cfg->buf_size = OMAP3ISP_HIST_MAX_BUF_SIZE; | ||
355 | |||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | static int hist_comp_params(struct ispstat *hist, | ||
360 | struct omap3isp_hist_config *user_cfg) | ||
361 | { | ||
362 | struct omap3isp_hist_config *cur_cfg = hist->priv; | ||
363 | int c; | ||
364 | |||
365 | if (cur_cfg->cfa != user_cfg->cfa) | ||
366 | return 1; | ||
367 | |||
368 | if (cur_cfg->num_acc_frames != user_cfg->num_acc_frames) | ||
369 | return 1; | ||
370 | |||
371 | if (cur_cfg->hist_bins != user_cfg->hist_bins) | ||
372 | return 1; | ||
373 | |||
374 | for (c = 0; c < OMAP3ISP_HIST_MAX_WG; c++) { | ||
375 | if (c == 3 && user_cfg->cfa == OMAP3ISP_HIST_CFA_FOVEONX3) | ||
376 | break; | ||
377 | else if (cur_cfg->wg[c] != user_cfg->wg[c]) | ||
378 | return 1; | ||
379 | } | ||
380 | |||
381 | if (cur_cfg->num_regions != user_cfg->num_regions) | ||
382 | return 1; | ||
383 | |||
384 | /* Regions */ | ||
385 | for (c = 0; c < user_cfg->num_regions; c++) { | ||
386 | if (cur_cfg->region[c].h_start != user_cfg->region[c].h_start) | ||
387 | return 1; | ||
388 | if (cur_cfg->region[c].h_end != user_cfg->region[c].h_end) | ||
389 | return 1; | ||
390 | if (cur_cfg->region[c].v_start != user_cfg->region[c].v_start) | ||
391 | return 1; | ||
392 | if (cur_cfg->region[c].v_end != user_cfg->region[c].v_end) | ||
393 | return 1; | ||
394 | } | ||
395 | |||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * hist_update_params - Helper function to check and store user given params. | ||
401 | * @new_conf: Pointer to user configuration structure. | ||
402 | */ | ||
403 | static void hist_set_params(struct ispstat *hist, void *new_conf) | ||
404 | { | ||
405 | struct omap3isp_hist_config *user_cfg = new_conf; | ||
406 | struct omap3isp_hist_config *cur_cfg = hist->priv; | ||
407 | |||
408 | if (!hist->configured || hist_comp_params(hist, user_cfg)) { | ||
409 | memcpy(cur_cfg, user_cfg, sizeof(*user_cfg)); | ||
410 | if (user_cfg->num_acc_frames == 0) | ||
411 | user_cfg->num_acc_frames = 1; | ||
412 | hist->inc_config++; | ||
413 | hist->update = 1; | ||
414 | /* | ||
415 | * User might be asked for a bigger buffer than necessary for | ||
416 | * this configuration. In order to return the right amount of | ||
417 | * data during buffer request, let's calculate the size here | ||
418 | * instead of stick with user_cfg->buf_size. | ||
419 | */ | ||
420 | cur_cfg->buf_size = hist_get_buf_size(cur_cfg); | ||
421 | |||
422 | } | ||
423 | } | ||
424 | |||
425 | static long hist_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
426 | { | ||
427 | struct ispstat *stat = v4l2_get_subdevdata(sd); | ||
428 | |||
429 | switch (cmd) { | ||
430 | case VIDIOC_OMAP3ISP_HIST_CFG: | ||
431 | return omap3isp_stat_config(stat, arg); | ||
432 | case VIDIOC_OMAP3ISP_STAT_REQ: | ||
433 | return omap3isp_stat_request_statistics(stat, arg); | ||
434 | case VIDIOC_OMAP3ISP_STAT_EN: { | ||
435 | int *en = arg; | ||
436 | return omap3isp_stat_enable(stat, !!*en); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | return -ENOIOCTLCMD; | ||
441 | |||
442 | } | ||
443 | |||
444 | static const struct ispstat_ops hist_ops = { | ||
445 | .validate_params = hist_validate_params, | ||
446 | .set_params = hist_set_params, | ||
447 | .setup_regs = hist_setup_regs, | ||
448 | .enable = hist_enable, | ||
449 | .busy = hist_busy, | ||
450 | .buf_process = hist_buf_process, | ||
451 | }; | ||
452 | |||
453 | static const struct v4l2_subdev_core_ops hist_subdev_core_ops = { | ||
454 | .ioctl = hist_ioctl, | ||
455 | .subscribe_event = omap3isp_stat_subscribe_event, | ||
456 | .unsubscribe_event = omap3isp_stat_unsubscribe_event, | ||
457 | }; | ||
458 | |||
459 | static const struct v4l2_subdev_video_ops hist_subdev_video_ops = { | ||
460 | .s_stream = omap3isp_stat_s_stream, | ||
461 | }; | ||
462 | |||
463 | static const struct v4l2_subdev_ops hist_subdev_ops = { | ||
464 | .core = &hist_subdev_core_ops, | ||
465 | .video = &hist_subdev_video_ops, | ||
466 | }; | ||
467 | |||
468 | /* | ||
469 | * omap3isp_hist_init - Module Initialization. | ||
470 | */ | ||
471 | int omap3isp_hist_init(struct isp_device *isp) | ||
472 | { | ||
473 | struct ispstat *hist = &isp->isp_hist; | ||
474 | struct omap3isp_hist_config *hist_cfg; | ||
475 | int ret = -1; | ||
476 | |||
477 | hist_cfg = kzalloc(sizeof(*hist_cfg), GFP_KERNEL); | ||
478 | if (hist_cfg == NULL) | ||
479 | return -ENOMEM; | ||
480 | |||
481 | memset(hist, 0, sizeof(*hist)); | ||
482 | if (HIST_CONFIG_DMA) | ||
483 | ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "DMA_ISP_HIST", | ||
484 | hist_dma_cb, hist, &hist->dma_ch); | ||
485 | if (ret) { | ||
486 | if (HIST_CONFIG_DMA) | ||
487 | dev_warn(isp->dev, "hist: DMA request channel failed. " | ||
488 | "Using PIO only.\n"); | ||
489 | hist->dma_ch = -1; | ||
490 | } else { | ||
491 | dev_dbg(isp->dev, "hist: DMA channel = %d\n", hist->dma_ch); | ||
492 | hist_dma_config(hist); | ||
493 | omap_enable_dma_irq(hist->dma_ch, OMAP_DMA_BLOCK_IRQ); | ||
494 | } | ||
495 | |||
496 | hist->ops = &hist_ops; | ||
497 | hist->priv = hist_cfg; | ||
498 | hist->event_type = V4L2_EVENT_OMAP3ISP_HIST; | ||
499 | hist->isp = isp; | ||
500 | |||
501 | ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops); | ||
502 | if (ret) { | ||
503 | kfree(hist_cfg); | ||
504 | if (HIST_USING_DMA(hist)) | ||
505 | omap_free_dma(hist->dma_ch); | ||
506 | } | ||
507 | |||
508 | return ret; | ||
509 | } | ||
510 | |||
511 | /* | ||
512 | * omap3isp_hist_cleanup - Module cleanup. | ||
513 | */ | ||
514 | void omap3isp_hist_cleanup(struct isp_device *isp) | ||
515 | { | ||
516 | if (HIST_USING_DMA(&isp->isp_hist)) | ||
517 | omap_free_dma(isp->isp_hist.dma_ch); | ||
518 | kfree(isp->isp_hist.priv); | ||
519 | omap3isp_stat_free(&isp->isp_hist); | ||
520 | } | ||