linux/drivers/media/video/omap3isp/isph3a_af.c
<<
>>
Prefs
   1/*
   2 * isph3a_af.c
   3 *
   4 * TI OMAP3 ISP - H3A AF 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/* Linux specific include files */
  29#include <linux/device.h>
  30#include <linux/slab.h>
  31
  32#include "isp.h"
  33#include "isph3a.h"
  34#include "ispstat.h"
  35
  36#define IS_OUT_OF_BOUNDS(value, min, max)               \
  37        (((value) < (min)) || ((value) > (max)))
  38
  39static void h3a_af_setup_regs(struct ispstat *af, void *priv)
  40{
  41        struct omap3isp_h3a_af_config *conf = priv;
  42        u32 pcr;
  43        u32 pax1;
  44        u32 pax2;
  45        u32 paxstart;
  46        u32 coef;
  47        u32 base_coef_set0;
  48        u32 base_coef_set1;
  49        int index;
  50
  51        if (af->state == ISPSTAT_DISABLED)
  52                return;
  53
  54        isp_reg_writel(af->isp, af->active_buf->iommu_addr, OMAP3_ISP_IOMEM_H3A,
  55                       ISPH3A_AFBUFST);
  56
  57        if (!af->update)
  58                return;
  59
  60        /* Configure Hardware Registers */
  61        pax1 = ((conf->paxel.width >> 1) - 1) << AF_PAXW_SHIFT;
  62        /* Set height in AFPAX1 */
  63        pax1 |= (conf->paxel.height >> 1) - 1;
  64        isp_reg_writel(af->isp, pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1);
  65
  66        /* Configure AFPAX2 Register */
  67        /* Set Line Increment in AFPAX2 Register */
  68        pax2 = ((conf->paxel.line_inc >> 1) - 1) << AF_LINE_INCR_SHIFT;
  69        /* Set Vertical Count */
  70        pax2 |= (conf->paxel.v_cnt - 1) << AF_VT_COUNT_SHIFT;
  71        /* Set Horizontal Count */
  72        pax2 |= (conf->paxel.h_cnt - 1);
  73        isp_reg_writel(af->isp, pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2);
  74
  75        /* Configure PAXSTART Register */
  76        /*Configure Horizontal Start */
  77        paxstart = conf->paxel.h_start << AF_HZ_START_SHIFT;
  78        /* Configure Vertical Start */
  79        paxstart |= conf->paxel.v_start;
  80        isp_reg_writel(af->isp, paxstart, OMAP3_ISP_IOMEM_H3A,
  81                       ISPH3A_AFPAXSTART);
  82
  83        /*SetIIRSH Register */
  84        isp_reg_writel(af->isp, conf->iir.h_start,
  85                       OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH);
  86
  87        base_coef_set0 = ISPH3A_AFCOEF010;
  88        base_coef_set1 = ISPH3A_AFCOEF110;
  89        for (index = 0; index <= 8; index += 2) {
  90                /*Set IIR Filter0 Coefficients */
  91                coef = 0;
  92                coef |= conf->iir.coeff_set0[index];
  93                coef |= conf->iir.coeff_set0[index + 1] <<
  94                        AF_COEF_SHIFT;
  95                isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A,
  96                               base_coef_set0);
  97                base_coef_set0 += AFCOEF_OFFSET;
  98
  99                /*Set IIR Filter1 Coefficients */
 100                coef = 0;
 101                coef |= conf->iir.coeff_set1[index];
 102                coef |= conf->iir.coeff_set1[index + 1] <<
 103                        AF_COEF_SHIFT;
 104                isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A,
 105                               base_coef_set1);
 106                base_coef_set1 += AFCOEF_OFFSET;
 107        }
 108        /* set AFCOEF0010 Register */
 109        isp_reg_writel(af->isp, conf->iir.coeff_set0[10],
 110                       OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010);
 111        /* set AFCOEF1010 Register */
 112        isp_reg_writel(af->isp, conf->iir.coeff_set1[10],
 113                       OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010);
 114
 115        /* PCR Register */
 116        /* Set RGB Position */
 117        pcr = conf->rgb_pos << AF_RGBPOS_SHIFT;
 118        /* Set Accumulator Mode */
 119        if (conf->fvmode == OMAP3ISP_AF_MODE_PEAK)
 120                pcr |= AF_FVMODE;
 121        /* Set A-law */
 122        if (conf->alaw_enable)
 123                pcr |= AF_ALAW_EN;
 124        /* HMF Configurations */
 125        if (conf->hmf.enable) {
 126                /* Enable HMF */
 127                pcr |= AF_MED_EN;
 128                /* Set Median Threshold */
 129                pcr |= conf->hmf.threshold << AF_MED_TH_SHIFT;
 130        }
 131        /* Set PCR Register */
 132        isp_reg_clr_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
 133                        AF_PCR_MASK, pcr);
 134
 135        af->update = 0;
 136        af->config_counter += af->inc_config;
 137        af->inc_config = 0;
 138        af->buf_size = conf->buf_size;
 139}
 140
 141static void h3a_af_enable(struct ispstat *af, int enable)
 142{
 143        if (enable) {
 144                isp_reg_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
 145                            ISPH3A_PCR_AF_EN);
 146                /* This bit is already set if AEWB is enabled */
 147                if (af->isp->isp_aewb.state != ISPSTAT_ENABLED)
 148                        isp_reg_set(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
 149                                    ISPCTRL_H3A_CLK_EN);
 150        } else {
 151                isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
 152                            ISPH3A_PCR_AF_EN);
 153                /* This bit can't be cleared if AEWB is enabled */
 154                if (af->isp->isp_aewb.state != ISPSTAT_ENABLED)
 155                        isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
 156                                    ISPCTRL_H3A_CLK_EN);
 157        }
 158}
 159
 160static int h3a_af_busy(struct ispstat *af)
 161{
 162        return isp_reg_readl(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR)
 163                                                & ISPH3A_PCR_BUSYAF;
 164}
 165
 166static u32 h3a_af_get_buf_size(struct omap3isp_h3a_af_config *conf)
 167{
 168        return conf->paxel.h_cnt * conf->paxel.v_cnt * OMAP3ISP_AF_PAXEL_SIZE;
 169}
 170
 171/* Function to check paxel parameters */
 172static int h3a_af_validate_params(struct ispstat *af, void *new_conf)
 173{
 174        struct omap3isp_h3a_af_config *user_cfg = new_conf;
 175        struct omap3isp_h3a_af_paxel *paxel_cfg = &user_cfg->paxel;
 176        struct omap3isp_h3a_af_iir *iir_cfg = &user_cfg->iir;
 177        int index;
 178        u32 buf_size;
 179
 180        /* Check horizontal Count */
 181        if (IS_OUT_OF_BOUNDS(paxel_cfg->h_cnt,
 182                             OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN,
 183                             OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MAX))
 184                return -EINVAL;
 185
 186        /* Check Vertical Count */
 187        if (IS_OUT_OF_BOUNDS(paxel_cfg->v_cnt,
 188                             OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN,
 189                             OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MAX))
 190                return -EINVAL;
 191
 192        if (IS_OUT_OF_BOUNDS(paxel_cfg->height, OMAP3ISP_AF_PAXEL_HEIGHT_MIN,
 193                             OMAP3ISP_AF_PAXEL_HEIGHT_MAX) ||
 194            paxel_cfg->height % 2)
 195                return -EINVAL;
 196
 197        /* Check width */
 198        if (IS_OUT_OF_BOUNDS(paxel_cfg->width, OMAP3ISP_AF_PAXEL_WIDTH_MIN,
 199                             OMAP3ISP_AF_PAXEL_WIDTH_MAX) ||
 200            paxel_cfg->width % 2)
 201                return -EINVAL;
 202
 203        /* Check Line Increment */
 204        if (IS_OUT_OF_BOUNDS(paxel_cfg->line_inc,
 205                             OMAP3ISP_AF_PAXEL_INCREMENT_MIN,
 206                             OMAP3ISP_AF_PAXEL_INCREMENT_MAX) ||
 207            paxel_cfg->line_inc % 2)
 208                return -EINVAL;
 209
 210        /* Check Horizontal Start */
 211        if ((paxel_cfg->h_start < iir_cfg->h_start) ||
 212            IS_OUT_OF_BOUNDS(paxel_cfg->h_start,
 213                             OMAP3ISP_AF_PAXEL_HZSTART_MIN,
 214                             OMAP3ISP_AF_PAXEL_HZSTART_MAX))
 215                return -EINVAL;
 216
 217        /* Check IIR */
 218        for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) {
 219                if ((iir_cfg->coeff_set0[index]) > OMAP3ISP_AF_COEF_MAX)
 220                        return -EINVAL;
 221
 222                if ((iir_cfg->coeff_set1[index]) > OMAP3ISP_AF_COEF_MAX)
 223                        return -EINVAL;
 224        }
 225
 226        if (IS_OUT_OF_BOUNDS(iir_cfg->h_start, OMAP3ISP_AF_IIRSH_MIN,
 227                             OMAP3ISP_AF_IIRSH_MAX))
 228                return -EINVAL;
 229
 230        /* Hack: If paxel size is 12, the 10th AF window may be corrupted */
 231        if ((paxel_cfg->h_cnt * paxel_cfg->v_cnt > 9) &&
 232            (paxel_cfg->width * paxel_cfg->height == 12))
 233                return -EINVAL;
 234
 235        buf_size = h3a_af_get_buf_size(user_cfg);
 236        if (buf_size > user_cfg->buf_size)
 237                /* User buf_size request wasn't enough */
 238                user_cfg->buf_size = buf_size;
 239        else if (user_cfg->buf_size > OMAP3ISP_AF_MAX_BUF_SIZE)
 240                user_cfg->buf_size = OMAP3ISP_AF_MAX_BUF_SIZE;
 241
 242        return 0;
 243}
 244
 245/* Update local parameters */
 246static void h3a_af_set_params(struct ispstat *af, void *new_conf)
 247{
 248        struct omap3isp_h3a_af_config *user_cfg = new_conf;
 249        struct omap3isp_h3a_af_config *cur_cfg = af->priv;
 250        int update = 0;
 251        int index;
 252
 253        /* alaw */
 254        if (cur_cfg->alaw_enable != user_cfg->alaw_enable) {
 255                update = 1;
 256                goto out;
 257        }
 258
 259        /* hmf */
 260        if (cur_cfg->hmf.enable != user_cfg->hmf.enable) {
 261                update = 1;
 262                goto out;
 263        }
 264        if (cur_cfg->hmf.threshold != user_cfg->hmf.threshold) {
 265                update = 1;
 266                goto out;
 267        }
 268
 269        /* rgbpos */
 270        if (cur_cfg->rgb_pos != user_cfg->rgb_pos) {
 271                update = 1;
 272                goto out;
 273        }
 274
 275        /* iir */
 276        if (cur_cfg->iir.h_start != user_cfg->iir.h_start) {
 277                update = 1;
 278                goto out;
 279        }
 280        for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) {
 281                if (cur_cfg->iir.coeff_set0[index] !=
 282                                user_cfg->iir.coeff_set0[index]) {
 283                        update = 1;
 284                        goto out;
 285                }
 286                if (cur_cfg->iir.coeff_set1[index] !=
 287                                user_cfg->iir.coeff_set1[index]) {
 288                        update = 1;
 289                        goto out;
 290                }
 291        }
 292
 293        /* paxel */
 294        if ((cur_cfg->paxel.width != user_cfg->paxel.width) ||
 295            (cur_cfg->paxel.height != user_cfg->paxel.height) ||
 296            (cur_cfg->paxel.h_start != user_cfg->paxel.h_start) ||
 297            (cur_cfg->paxel.v_start != user_cfg->paxel.v_start) ||
 298            (cur_cfg->paxel.h_cnt != user_cfg->paxel.h_cnt) ||
 299            (cur_cfg->paxel.v_cnt != user_cfg->paxel.v_cnt) ||
 300            (cur_cfg->paxel.line_inc != user_cfg->paxel.line_inc)) {
 301                update = 1;
 302                goto out;
 303        }
 304
 305        /* af_mode */
 306        if (cur_cfg->fvmode != user_cfg->fvmode)
 307                update = 1;
 308
 309out:
 310        if (update || !af->configured) {
 311                memcpy(cur_cfg, user_cfg, sizeof(*cur_cfg));
 312                af->inc_config++;
 313                af->update = 1;
 314                /*
 315                 * User might be asked for a bigger buffer than necessary for
 316                 * this configuration. In order to return the right amount of
 317                 * data during buffer request, let's calculate the size here
 318                 * instead of stick with user_cfg->buf_size.
 319                 */
 320                cur_cfg->buf_size = h3a_af_get_buf_size(cur_cfg);
 321        }
 322}
 323
 324static long h3a_af_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
 325{
 326        struct ispstat *stat = v4l2_get_subdevdata(sd);
 327
 328        switch (cmd) {
 329        case VIDIOC_OMAP3ISP_AF_CFG:
 330                return omap3isp_stat_config(stat, arg);
 331        case VIDIOC_OMAP3ISP_STAT_REQ:
 332                return omap3isp_stat_request_statistics(stat, arg);
 333        case VIDIOC_OMAP3ISP_STAT_EN: {
 334                int *en = arg;
 335                return omap3isp_stat_enable(stat, !!*en);
 336        }
 337        }
 338
 339        return -ENOIOCTLCMD;
 340
 341}
 342
 343static const struct ispstat_ops h3a_af_ops = {
 344        .validate_params        = h3a_af_validate_params,
 345        .set_params             = h3a_af_set_params,
 346        .setup_regs             = h3a_af_setup_regs,
 347        .enable                 = h3a_af_enable,
 348        .busy                   = h3a_af_busy,
 349};
 350
 351static const struct v4l2_subdev_core_ops h3a_af_subdev_core_ops = {
 352        .ioctl = h3a_af_ioctl,
 353        .subscribe_event = omap3isp_stat_subscribe_event,
 354        .unsubscribe_event = omap3isp_stat_unsubscribe_event,
 355};
 356
 357static const struct v4l2_subdev_video_ops h3a_af_subdev_video_ops = {
 358        .s_stream = omap3isp_stat_s_stream,
 359};
 360
 361static const struct v4l2_subdev_ops h3a_af_subdev_ops = {
 362        .core = &h3a_af_subdev_core_ops,
 363        .video = &h3a_af_subdev_video_ops,
 364};
 365
 366/* Function to register the AF character device driver. */
 367int omap3isp_h3a_af_init(struct isp_device *isp)
 368{
 369        struct ispstat *af = &isp->isp_af;
 370        struct omap3isp_h3a_af_config *af_cfg;
 371        struct omap3isp_h3a_af_config *af_recover_cfg;
 372        int ret;
 373
 374        af_cfg = kzalloc(sizeof(*af_cfg), GFP_KERNEL);
 375        if (af_cfg == NULL)
 376                return -ENOMEM;
 377
 378        memset(af, 0, sizeof(*af));
 379        af->ops = &h3a_af_ops;
 380        af->priv = af_cfg;
 381        af->dma_ch = -1;
 382        af->event_type = V4L2_EVENT_OMAP3ISP_AF;
 383        af->isp = isp;
 384
 385        /* Set recover state configuration */
 386        af_recover_cfg = kzalloc(sizeof(*af_recover_cfg), GFP_KERNEL);
 387        if (!af_recover_cfg) {
 388                dev_err(af->isp->dev, "AF: cannot allocate memory for recover "
 389                                      "configuration.\n");
 390                ret = -ENOMEM;
 391                goto err_recover_alloc;
 392        }
 393
 394        af_recover_cfg->paxel.h_start = OMAP3ISP_AF_PAXEL_HZSTART_MIN;
 395        af_recover_cfg->paxel.width = OMAP3ISP_AF_PAXEL_WIDTH_MIN;
 396        af_recover_cfg->paxel.height = OMAP3ISP_AF_PAXEL_HEIGHT_MIN;
 397        af_recover_cfg->paxel.h_cnt = OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN;
 398        af_recover_cfg->paxel.v_cnt = OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN;
 399        af_recover_cfg->paxel.line_inc = OMAP3ISP_AF_PAXEL_INCREMENT_MIN;
 400        if (h3a_af_validate_params(af, af_recover_cfg)) {
 401                dev_err(af->isp->dev, "AF: recover configuration is "
 402                                      "invalid.\n");
 403                ret = -EINVAL;
 404                goto err_conf;
 405        }
 406
 407        af_recover_cfg->buf_size = h3a_af_get_buf_size(af_recover_cfg);
 408        af->recover_priv = af_recover_cfg;
 409
 410        ret = omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops);
 411        if (ret)
 412                goto err_conf;
 413
 414        return 0;
 415
 416err_conf:
 417        kfree(af_recover_cfg);
 418err_recover_alloc:
 419        kfree(af_cfg);
 420
 421        return ret;
 422}
 423
 424void omap3isp_h3a_af_cleanup(struct isp_device *isp)
 425{
 426        kfree(isp->isp_af.priv);
 427        kfree(isp->isp_af.recover_priv);
 428        omap3isp_stat_cleanup(&isp->isp_af);
 429}
 430