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