linux/drivers/media/video/omap3isp/isphist.c
<<
>>
Prefs
   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 */
  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         * 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
  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 * hist_setup_regs - Helper function to update Histogram registers.
  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        /* 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
 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         * 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 */
 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 * 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 */
 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        /* 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
 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        /* 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 */
 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                 * 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
 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 * omap3isp_hist_init - Module Initialization.
 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 * omap3isp_hist_cleanup - Module cleanup.
 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