diff options
Diffstat (limited to 'drivers/media/platform/omap3isp/isphist.c')
-rw-r--r-- | drivers/media/platform/omap3isp/isphist.c | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c new file mode 100644 index 000000000000..d1a8dee5e1ca --- /dev/null +++ b/drivers/media/platform/omap3isp/isphist.c | |||
@@ -0,0 +1,518 @@ | |||
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 | omap3isp_subclk_enable(hist->isp, OMAP3_ISP_SUBCLK_HIST); | ||
171 | } else { | ||
172 | isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, | ||
173 | ISPHIST_PCR_ENABLE); | ||
174 | omap3isp_subclk_disable(hist->isp, OMAP3_ISP_SUBCLK_HIST); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | static int hist_busy(struct ispstat *hist) | ||
179 | { | ||
180 | return isp_reg_readl(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR) | ||
181 | & ISPHIST_PCR_BUSY; | ||
182 | } | ||
183 | |||
184 | static void hist_dma_cb(int lch, u16 ch_status, void *data) | ||
185 | { | ||
186 | struct ispstat *hist = data; | ||
187 | |||
188 | if (ch_status & ~OMAP_DMA_BLOCK_IRQ) { | ||
189 | dev_dbg(hist->isp->dev, "hist: DMA error. status = 0x%04x\n", | ||
190 | ch_status); | ||
191 | omap_stop_dma(lch); | ||
192 | hist_reset_mem(hist); | ||
193 | atomic_set(&hist->buf_err, 1); | ||
194 | } | ||
195 | isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, | ||
196 | ISPHIST_CNT_CLEAR); | ||
197 | |||
198 | omap3isp_stat_dma_isr(hist); | ||
199 | if (hist->state != ISPSTAT_DISABLED) | ||
200 | omap3isp_hist_dma_done(hist->isp); | ||
201 | } | ||
202 | |||
203 | static int hist_buf_dma(struct ispstat *hist) | ||
204 | { | ||
205 | dma_addr_t dma_addr = hist->active_buf->dma_addr; | ||
206 | |||
207 | if (unlikely(!dma_addr)) { | ||
208 | dev_dbg(hist->isp->dev, "hist: invalid DMA buffer address\n"); | ||
209 | hist_reset_mem(hist); | ||
210 | return STAT_NO_BUF; | ||
211 | } | ||
212 | |||
213 | isp_reg_writel(hist->isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); | ||
214 | isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, | ||
215 | ISPHIST_CNT_CLEAR); | ||
216 | omap3isp_flush(hist->isp); | ||
217 | hist->dma_config.dst_start = dma_addr; | ||
218 | hist->dma_config.elem_count = hist->buf_size / sizeof(u32); | ||
219 | omap_set_dma_params(hist->dma_ch, &hist->dma_config); | ||
220 | |||
221 | omap_start_dma(hist->dma_ch); | ||
222 | |||
223 | return STAT_BUF_WAITING_DMA; | ||
224 | } | ||
225 | |||
226 | static int hist_buf_pio(struct ispstat *hist) | ||
227 | { | ||
228 | struct isp_device *isp = hist->isp; | ||
229 | u32 *buf = hist->active_buf->virt_addr; | ||
230 | unsigned int i; | ||
231 | |||
232 | if (!buf) { | ||
233 | dev_dbg(isp->dev, "hist: invalid PIO buffer address\n"); | ||
234 | hist_reset_mem(hist); | ||
235 | return STAT_NO_BUF; | ||
236 | } | ||
237 | |||
238 | isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); | ||
239 | |||
240 | /* | ||
241 | * By setting it, the histogram internal buffer is being cleared at the | ||
242 | * same time it's being read. This bit must be cleared just after all | ||
243 | * data is acquired. | ||
244 | */ | ||
245 | isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); | ||
246 | |||
247 | /* | ||
248 | * We'll read 4 times a 4-bytes-word at each iteration for | ||
249 | * optimization. It avoids 3/4 of the jumps. We also know buf_size is | ||
250 | * divisible by 16. | ||
251 | */ | ||
252 | for (i = hist->buf_size / 16; i > 0; i--) { | ||
253 | *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
254 | *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); | ||
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 | } | ||
258 | isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, | ||
259 | ISPHIST_CNT_CLEAR); | ||
260 | |||
261 | return STAT_BUF_DONE; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * hist_buf_process - Callback from ISP driver for HIST interrupt. | ||
266 | */ | ||
267 | static int hist_buf_process(struct ispstat *hist) | ||
268 | { | ||
269 | struct omap3isp_hist_config *user_cfg = hist->priv; | ||
270 | int ret; | ||
271 | |||
272 | if (atomic_read(&hist->buf_err) || hist->state != ISPSTAT_ENABLED) { | ||
273 | hist_reset_mem(hist); | ||
274 | return STAT_NO_BUF; | ||
275 | } | ||
276 | |||
277 | if (--(hist->wait_acc_frames)) | ||
278 | return STAT_NO_BUF; | ||
279 | |||
280 | if (HIST_USING_DMA(hist)) | ||
281 | ret = hist_buf_dma(hist); | ||
282 | else | ||
283 | ret = hist_buf_pio(hist); | ||
284 | |||
285 | hist->wait_acc_frames = user_cfg->num_acc_frames; | ||
286 | |||
287 | return ret; | ||
288 | } | ||
289 | |||
290 | static u32 hist_get_buf_size(struct omap3isp_hist_config *conf) | ||
291 | { | ||
292 | return OMAP3ISP_HIST_MEM_SIZE_BINS(conf->hist_bins) * conf->num_regions; | ||
293 | } | ||
294 | |||
295 | /* | ||
296 | * hist_validate_params - Helper function to check user given params. | ||
297 | * @user_cfg: Pointer to user configuration structure. | ||
298 | * | ||
299 | * Returns 0 on success configuration. | ||
300 | */ | ||
301 | static int hist_validate_params(struct ispstat *hist, void *new_conf) | ||
302 | { | ||
303 | struct omap3isp_hist_config *user_cfg = new_conf; | ||
304 | int c; | ||
305 | u32 buf_size; | ||
306 | |||
307 | if (user_cfg->cfa > OMAP3ISP_HIST_CFA_FOVEONX3) | ||
308 | return -EINVAL; | ||
309 | |||
310 | /* Regions size and position */ | ||
311 | |||
312 | if ((user_cfg->num_regions < OMAP3ISP_HIST_MIN_REGIONS) || | ||
313 | (user_cfg->num_regions > OMAP3ISP_HIST_MAX_REGIONS)) | ||
314 | return -EINVAL; | ||
315 | |||
316 | /* Regions */ | ||
317 | for (c = 0; c < user_cfg->num_regions; c++) { | ||
318 | if (user_cfg->region[c].h_start & ~ISPHIST_REG_START_END_MASK) | ||
319 | return -EINVAL; | ||
320 | if (user_cfg->region[c].h_end & ~ISPHIST_REG_START_END_MASK) | ||
321 | return -EINVAL; | ||
322 | if (user_cfg->region[c].v_start & ~ISPHIST_REG_START_END_MASK) | ||
323 | return -EINVAL; | ||
324 | if (user_cfg->region[c].v_end & ~ISPHIST_REG_START_END_MASK) | ||
325 | return -EINVAL; | ||
326 | if (user_cfg->region[c].h_start > user_cfg->region[c].h_end) | ||
327 | return -EINVAL; | ||
328 | if (user_cfg->region[c].v_start > user_cfg->region[c].v_end) | ||
329 | return -EINVAL; | ||
330 | } | ||
331 | |||
332 | switch (user_cfg->num_regions) { | ||
333 | case 1: | ||
334 | if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_256) | ||
335 | return -EINVAL; | ||
336 | break; | ||
337 | case 2: | ||
338 | if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_128) | ||
339 | return -EINVAL; | ||
340 | break; | ||
341 | default: /* 3 or 4 */ | ||
342 | if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_64) | ||
343 | return -EINVAL; | ||
344 | break; | ||
345 | } | ||
346 | |||
347 | buf_size = hist_get_buf_size(user_cfg); | ||
348 | if (buf_size > user_cfg->buf_size) | ||
349 | /* User's buf_size request wasn't enoght */ | ||
350 | user_cfg->buf_size = buf_size; | ||
351 | else if (user_cfg->buf_size > OMAP3ISP_HIST_MAX_BUF_SIZE) | ||
352 | user_cfg->buf_size = OMAP3ISP_HIST_MAX_BUF_SIZE; | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | static int hist_comp_params(struct ispstat *hist, | ||
358 | struct omap3isp_hist_config *user_cfg) | ||
359 | { | ||
360 | struct omap3isp_hist_config *cur_cfg = hist->priv; | ||
361 | int c; | ||
362 | |||
363 | if (cur_cfg->cfa != user_cfg->cfa) | ||
364 | return 1; | ||
365 | |||
366 | if (cur_cfg->num_acc_frames != user_cfg->num_acc_frames) | ||
367 | return 1; | ||
368 | |||
369 | if (cur_cfg->hist_bins != user_cfg->hist_bins) | ||
370 | return 1; | ||
371 | |||
372 | for (c = 0; c < OMAP3ISP_HIST_MAX_WG; c++) { | ||
373 | if (c == 3 && user_cfg->cfa == OMAP3ISP_HIST_CFA_FOVEONX3) | ||
374 | break; | ||
375 | else if (cur_cfg->wg[c] != user_cfg->wg[c]) | ||
376 | return 1; | ||
377 | } | ||
378 | |||
379 | if (cur_cfg->num_regions != user_cfg->num_regions) | ||
380 | return 1; | ||
381 | |||
382 | /* Regions */ | ||
383 | for (c = 0; c < user_cfg->num_regions; c++) { | ||
384 | if (cur_cfg->region[c].h_start != user_cfg->region[c].h_start) | ||
385 | return 1; | ||
386 | if (cur_cfg->region[c].h_end != user_cfg->region[c].h_end) | ||
387 | return 1; | ||
388 | if (cur_cfg->region[c].v_start != user_cfg->region[c].v_start) | ||
389 | return 1; | ||
390 | if (cur_cfg->region[c].v_end != user_cfg->region[c].v_end) | ||
391 | return 1; | ||
392 | } | ||
393 | |||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | /* | ||
398 | * hist_update_params - Helper function to check and store user given params. | ||
399 | * @new_conf: Pointer to user configuration structure. | ||
400 | */ | ||
401 | static void hist_set_params(struct ispstat *hist, void *new_conf) | ||
402 | { | ||
403 | struct omap3isp_hist_config *user_cfg = new_conf; | ||
404 | struct omap3isp_hist_config *cur_cfg = hist->priv; | ||
405 | |||
406 | if (!hist->configured || hist_comp_params(hist, user_cfg)) { | ||
407 | memcpy(cur_cfg, user_cfg, sizeof(*user_cfg)); | ||
408 | if (user_cfg->num_acc_frames == 0) | ||
409 | user_cfg->num_acc_frames = 1; | ||
410 | hist->inc_config++; | ||
411 | hist->update = 1; | ||
412 | /* | ||
413 | * User might be asked for a bigger buffer than necessary for | ||
414 | * this configuration. In order to return the right amount of | ||
415 | * data during buffer request, let's calculate the size here | ||
416 | * instead of stick with user_cfg->buf_size. | ||
417 | */ | ||
418 | cur_cfg->buf_size = hist_get_buf_size(cur_cfg); | ||
419 | |||
420 | } | ||
421 | } | ||
422 | |||
423 | static long hist_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
424 | { | ||
425 | struct ispstat *stat = v4l2_get_subdevdata(sd); | ||
426 | |||
427 | switch (cmd) { | ||
428 | case VIDIOC_OMAP3ISP_HIST_CFG: | ||
429 | return omap3isp_stat_config(stat, arg); | ||
430 | case VIDIOC_OMAP3ISP_STAT_REQ: | ||
431 | return omap3isp_stat_request_statistics(stat, arg); | ||
432 | case VIDIOC_OMAP3ISP_STAT_EN: { | ||
433 | int *en = arg; | ||
434 | return omap3isp_stat_enable(stat, !!*en); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | return -ENOIOCTLCMD; | ||
439 | |||
440 | } | ||
441 | |||
442 | static const struct ispstat_ops hist_ops = { | ||
443 | .validate_params = hist_validate_params, | ||
444 | .set_params = hist_set_params, | ||
445 | .setup_regs = hist_setup_regs, | ||
446 | .enable = hist_enable, | ||
447 | .busy = hist_busy, | ||
448 | .buf_process = hist_buf_process, | ||
449 | }; | ||
450 | |||
451 | static const struct v4l2_subdev_core_ops hist_subdev_core_ops = { | ||
452 | .ioctl = hist_ioctl, | ||
453 | .subscribe_event = omap3isp_stat_subscribe_event, | ||
454 | .unsubscribe_event = omap3isp_stat_unsubscribe_event, | ||
455 | }; | ||
456 | |||
457 | static const struct v4l2_subdev_video_ops hist_subdev_video_ops = { | ||
458 | .s_stream = omap3isp_stat_s_stream, | ||
459 | }; | ||
460 | |||
461 | static const struct v4l2_subdev_ops hist_subdev_ops = { | ||
462 | .core = &hist_subdev_core_ops, | ||
463 | .video = &hist_subdev_video_ops, | ||
464 | }; | ||
465 | |||
466 | /* | ||
467 | * omap3isp_hist_init - Module Initialization. | ||
468 | */ | ||
469 | int omap3isp_hist_init(struct isp_device *isp) | ||
470 | { | ||
471 | struct ispstat *hist = &isp->isp_hist; | ||
472 | struct omap3isp_hist_config *hist_cfg; | ||
473 | int ret = -1; | ||
474 | |||
475 | hist_cfg = kzalloc(sizeof(*hist_cfg), GFP_KERNEL); | ||
476 | if (hist_cfg == NULL) | ||
477 | return -ENOMEM; | ||
478 | |||
479 | memset(hist, 0, sizeof(*hist)); | ||
480 | if (HIST_CONFIG_DMA) | ||
481 | ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "DMA_ISP_HIST", | ||
482 | hist_dma_cb, hist, &hist->dma_ch); | ||
483 | if (ret) { | ||
484 | if (HIST_CONFIG_DMA) | ||
485 | dev_warn(isp->dev, "hist: DMA request channel failed. " | ||
486 | "Using PIO only.\n"); | ||
487 | hist->dma_ch = -1; | ||
488 | } else { | ||
489 | dev_dbg(isp->dev, "hist: DMA channel = %d\n", hist->dma_ch); | ||
490 | hist_dma_config(hist); | ||
491 | omap_enable_dma_irq(hist->dma_ch, OMAP_DMA_BLOCK_IRQ); | ||
492 | } | ||
493 | |||
494 | hist->ops = &hist_ops; | ||
495 | hist->priv = hist_cfg; | ||
496 | hist->event_type = V4L2_EVENT_OMAP3ISP_HIST; | ||
497 | hist->isp = isp; | ||
498 | |||
499 | ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops); | ||
500 | if (ret) { | ||
501 | kfree(hist_cfg); | ||
502 | if (HIST_USING_DMA(hist)) | ||
503 | omap_free_dma(hist->dma_ch); | ||
504 | } | ||
505 | |||
506 | return ret; | ||
507 | } | ||
508 | |||
509 | /* | ||
510 | * omap3isp_hist_cleanup - Module cleanup. | ||
511 | */ | ||
512 | void omap3isp_hist_cleanup(struct isp_device *isp) | ||
513 | { | ||
514 | if (HIST_USING_DMA(&isp->isp_hist)) | ||
515 | omap_free_dma(isp->isp_hist.dma_ch); | ||
516 | kfree(isp->isp_hist.priv); | ||
517 | omap3isp_stat_cleanup(&isp->isp_hist); | ||
518 | } | ||