1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
43
44static 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
54
55
56 isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR);
57
58
59
60
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
73static 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
86
87static 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
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:
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
165static 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
180static 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
186static 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
205static 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
228static 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
244
245
246
247 isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR);
248
249
250
251
252
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
268
269static 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
292static 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
299
300
301
302
303static 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
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
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:
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
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
359static 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
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
401
402
403static 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
416
417
418
419
420 cur_cfg->buf_size = hist_get_buf_size(cur_cfg);
421
422 }
423}
424
425static 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
444static 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
453static 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
459static const struct v4l2_subdev_video_ops hist_subdev_video_ops = {
460 .s_stream = omap3isp_stat_s_stream,
461};
462
463static const struct v4l2_subdev_ops hist_subdev_ops = {
464 .core = &hist_subdev_core_ops,
465 .video = &hist_subdev_video_ops,
466};
467
468
469
470
471int 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
513
514void 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_cleanup(&isp->isp_hist);
520}
521