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