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