1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * isph3a.c 4 * 5 * TI OMAP3 ISP - H3A 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 #include <linux/slab.h> 16 #include <linux/uaccess.h> 17 18 #include "isp.h" 19 #include "isph3a.h" 20 #include "ispstat.h" 21 22 /* 23 * h3a_aewb_update_regs - Helper function to update h3a registers. 24 */ 25 static void h3a_aewb_setup_regs(struct ispstat *aewb, void *priv) 26 { 27 struct omap3isp_h3a_aewb_config *conf = priv; 28 u32 pcr; 29 u32 win1; 30 u32 start; 31 u32 blk; 32 u32 subwin; 33 34 if (aewb->state == ISPSTAT_DISABLED) 35 return; 36 37 isp_reg_writel(aewb->isp, aewb->active_buf->dma_addr, 38 OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST); 39 40 if (!aewb->update) 41 return; 42 43 /* Converting config metadata into reg values */ 44 pcr = conf->saturation_limit << ISPH3A_PCR_AEW_AVE2LMT_SHIFT; 45 pcr |= !!conf->alaw_enable << ISPH3A_PCR_AEW_ALAW_EN_SHIFT; 46 47 win1 = ((conf->win_height >> 1) - 1) << ISPH3A_AEWWIN1_WINH_SHIFT; 48 win1 |= ((conf->win_width >> 1) - 1) << ISPH3A_AEWWIN1_WINW_SHIFT; 49 win1 |= (conf->ver_win_count - 1) << ISPH3A_AEWWIN1_WINVC_SHIFT; 50 win1 |= (conf->hor_win_count - 1) << ISPH3A_AEWWIN1_WINHC_SHIFT; 51 52 start = conf->hor_win_start << ISPH3A_AEWINSTART_WINSH_SHIFT; 53 start |= conf->ver_win_start << ISPH3A_AEWINSTART_WINSV_SHIFT; 54 55 blk = conf->blk_ver_win_start << ISPH3A_AEWINBLK_WINSV_SHIFT; 56 blk |= ((conf->blk_win_height >> 1) - 1) << ISPH3A_AEWINBLK_WINH_SHIFT; 57 58 subwin = ((conf->subsample_ver_inc >> 1) - 1) << 59 ISPH3A_AEWSUBWIN_AEWINCV_SHIFT; 60 subwin |= ((conf->subsample_hor_inc >> 1) - 1) << 61 ISPH3A_AEWSUBWIN_AEWINCH_SHIFT; 62 63 isp_reg_writel(aewb->isp, win1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1); 64 isp_reg_writel(aewb->isp, start, OMAP3_ISP_IOMEM_H3A, 65 ISPH3A_AEWINSTART); 66 isp_reg_writel(aewb->isp, blk, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK); 67 isp_reg_writel(aewb->isp, subwin, OMAP3_ISP_IOMEM_H3A, 68 ISPH3A_AEWSUBWIN); 69 isp_reg_clr_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, 70 ISPH3A_PCR_AEW_MASK, pcr); 71 72 aewb->update = 0; 73 aewb->config_counter += aewb->inc_config; 74 aewb->inc_config = 0; 75 aewb->buf_size = conf->buf_size; 76 } 77 78 static void h3a_aewb_enable(struct ispstat *aewb, int enable) 79 { 80 if (enable) { 81 isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, 82 ISPH3A_PCR_AEW_EN); 83 omap3isp_subclk_enable(aewb->isp, OMAP3_ISP_SUBCLK_AEWB); 84 } else { 85 isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, 86 ISPH3A_PCR_AEW_EN); 87 omap3isp_subclk_disable(aewb->isp, OMAP3_ISP_SUBCLK_AEWB); 88 } 89 } 90 91 static int h3a_aewb_busy(struct ispstat *aewb) 92 { 93 return isp_reg_readl(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) 94 & ISPH3A_PCR_BUSYAEAWB; 95 } 96 97 static u32 h3a_aewb_get_buf_size(struct omap3isp_h3a_aewb_config *conf) 98 { 99 /* Number of configured windows + extra row for black data */ 100 u32 win_count = (conf->ver_win_count + 1) * conf->hor_win_count; 101 102 /* 103 * Unsaturated block counts for each 8 windows. 104 * 1 extra for the last (win_count % 8) windows if win_count is not 105 * divisible by 8. 106 */ 107 win_count += (win_count + 7) / 8; 108 109 return win_count * AEWB_PACKET_SIZE; 110 } 111 112 static int h3a_aewb_validate_params(struct ispstat *aewb, void *new_conf) 113 { 114 struct omap3isp_h3a_aewb_config *user_cfg = new_conf; 115 u32 buf_size; 116 117 if (unlikely(user_cfg->saturation_limit > 118 OMAP3ISP_AEWB_MAX_SATURATION_LIM)) 119 return -EINVAL; 120 121 if (unlikely(user_cfg->win_height < OMAP3ISP_AEWB_MIN_WIN_H || 122 user_cfg->win_height > OMAP3ISP_AEWB_MAX_WIN_H || 123 user_cfg->win_height & 0x01)) 124 return -EINVAL; 125 126 if (unlikely(user_cfg->win_width < OMAP3ISP_AEWB_MIN_WIN_W || 127 user_cfg->win_width > OMAP3ISP_AEWB_MAX_WIN_W || 128 user_cfg->win_width & 0x01)) 129 return -EINVAL; 130 131 if (unlikely(user_cfg->ver_win_count < OMAP3ISP_AEWB_MIN_WINVC || 132 user_cfg->ver_win_count > OMAP3ISP_AEWB_MAX_WINVC)) 133 return -EINVAL; 134 135 if (unlikely(user_cfg->hor_win_count < OMAP3ISP_AEWB_MIN_WINHC || 136 user_cfg->hor_win_count > OMAP3ISP_AEWB_MAX_WINHC)) 137 return -EINVAL; 138 139 if (unlikely(user_cfg->ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) 140 return -EINVAL; 141 142 if (unlikely(user_cfg->hor_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) 143 return -EINVAL; 144 145 if (unlikely(user_cfg->blk_ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) 146 return -EINVAL; 147 148 if (unlikely(user_cfg->blk_win_height < OMAP3ISP_AEWB_MIN_WIN_H || 149 user_cfg->blk_win_height > OMAP3ISP_AEWB_MAX_WIN_H || 150 user_cfg->blk_win_height & 0x01)) 151 return -EINVAL; 152 153 if (unlikely(user_cfg->subsample_ver_inc < OMAP3ISP_AEWB_MIN_SUB_INC || 154 user_cfg->subsample_ver_inc > OMAP3ISP_AEWB_MAX_SUB_INC || 155 user_cfg->subsample_ver_inc & 0x01)) 156 return -EINVAL; 157 158 if (unlikely(user_cfg->subsample_hor_inc < OMAP3ISP_AEWB_MIN_SUB_INC || 159 user_cfg->subsample_hor_inc > OMAP3ISP_AEWB_MAX_SUB_INC || 160 user_cfg->subsample_hor_inc & 0x01)) 161 return -EINVAL; 162 163 buf_size = h3a_aewb_get_buf_size(user_cfg); 164 if (buf_size > user_cfg->buf_size) 165 user_cfg->buf_size = buf_size; 166 else if (user_cfg->buf_size > OMAP3ISP_AEWB_MAX_BUF_SIZE) 167 user_cfg->buf_size = OMAP3ISP_AEWB_MAX_BUF_SIZE; 168 169 return 0; 170 } 171 172 /* 173 * h3a_aewb_set_params - Helper function to check & store user given params. 174 * @new_conf: Pointer to AE and AWB parameters struct. 175 * 176 * As most of them are busy-lock registers, need to wait until AEW_BUSY = 0 to 177 * program them during ISR. 178 */ 179 static void h3a_aewb_set_params(struct ispstat *aewb, void *new_conf) 180 { 181 struct omap3isp_h3a_aewb_config *user_cfg = new_conf; 182 struct omap3isp_h3a_aewb_config *cur_cfg = aewb->priv; 183 int update = 0; 184 185 if (cur_cfg->saturation_limit != user_cfg->saturation_limit) { 186 cur_cfg->saturation_limit = user_cfg->saturation_limit; 187 update = 1; 188 } 189 if (cur_cfg->alaw_enable != user_cfg->alaw_enable) { 190 cur_cfg->alaw_enable = user_cfg->alaw_enable; 191 update = 1; 192 } 193 if (cur_cfg->win_height != user_cfg->win_height) { 194 cur_cfg->win_height = user_cfg->win_height; 195 update = 1; 196 } 197 if (cur_cfg->win_width != user_cfg->win_width) { 198 cur_cfg->win_width = user_cfg->win_width; 199 update = 1; 200 } 201 if (cur_cfg->ver_win_count != user_cfg->ver_win_count) { 202 cur_cfg->ver_win_count = user_cfg->ver_win_count; 203 update = 1; 204 } 205 if (cur_cfg->hor_win_count != user_cfg->hor_win_count) { 206 cur_cfg->hor_win_count = user_cfg->hor_win_count; 207 update = 1; 208 } 209 if (cur_cfg->ver_win_start != user_cfg->ver_win_start) { 210 cur_cfg->ver_win_start = user_cfg->ver_win_start; 211 update = 1; 212 } 213 if (cur_cfg->hor_win_start != user_cfg->hor_win_start) { 214 cur_cfg->hor_win_start = user_cfg->hor_win_start; 215 update = 1; 216 } 217 if (cur_cfg->blk_ver_win_start != user_cfg->blk_ver_win_start) { 218 cur_cfg->blk_ver_win_start = user_cfg->blk_ver_win_start; 219 update = 1; 220 } 221 if (cur_cfg->blk_win_height != user_cfg->blk_win_height) { 222 cur_cfg->blk_win_height = user_cfg->blk_win_height; 223 update = 1; 224 } 225 if (cur_cfg->subsample_ver_inc != user_cfg->subsample_ver_inc) { 226 cur_cfg->subsample_ver_inc = user_cfg->subsample_ver_inc; 227 update = 1; 228 } 229 if (cur_cfg->subsample_hor_inc != user_cfg->subsample_hor_inc) { 230 cur_cfg->subsample_hor_inc = user_cfg->subsample_hor_inc; 231 update = 1; 232 } 233 234 if (update || !aewb->configured) { 235 aewb->inc_config++; 236 aewb->update = 1; 237 cur_cfg->buf_size = h3a_aewb_get_buf_size(cur_cfg); 238 } 239 } 240 241 static long h3a_aewb_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) 242 { 243 struct ispstat *stat = v4l2_get_subdevdata(sd); 244 245 switch (cmd) { 246 case VIDIOC_OMAP3ISP_AEWB_CFG: 247 return omap3isp_stat_config(stat, arg); 248 case VIDIOC_OMAP3ISP_STAT_REQ: 249 return omap3isp_stat_request_statistics(stat, arg); 250 case VIDIOC_OMAP3ISP_STAT_REQ_TIME32: 251 return omap3isp_stat_request_statistics_time32(stat, arg); 252 case VIDIOC_OMAP3ISP_STAT_EN: { 253 unsigned long *en = arg; 254 return omap3isp_stat_enable(stat, !!*en); 255 } 256 } 257 258 return -ENOIOCTLCMD; 259 } 260 261 static const struct ispstat_ops h3a_aewb_ops = { 262 .validate_params = h3a_aewb_validate_params, 263 .set_params = h3a_aewb_set_params, 264 .setup_regs = h3a_aewb_setup_regs, 265 .enable = h3a_aewb_enable, 266 .busy = h3a_aewb_busy, 267 }; 268 269 static const struct v4l2_subdev_core_ops h3a_aewb_subdev_core_ops = { 270 .ioctl = h3a_aewb_ioctl, 271 .subscribe_event = omap3isp_stat_subscribe_event, 272 .unsubscribe_event = omap3isp_stat_unsubscribe_event, 273 }; 274 275 static const struct v4l2_subdev_video_ops h3a_aewb_subdev_video_ops = { 276 .s_stream = omap3isp_stat_s_stream, 277 }; 278 279 static const struct v4l2_subdev_ops h3a_aewb_subdev_ops = { 280 .core = &h3a_aewb_subdev_core_ops, 281 .video = &h3a_aewb_subdev_video_ops, 282 }; 283 284 /* 285 * omap3isp_h3a_aewb_init - Module Initialisation. 286 */ 287 int omap3isp_h3a_aewb_init(struct isp_device *isp) 288 { 289 struct ispstat *aewb = &isp->isp_aewb; 290 struct omap3isp_h3a_aewb_config *aewb_cfg; 291 struct omap3isp_h3a_aewb_config *aewb_recover_cfg = NULL; 292 int ret; 293 294 aewb_cfg = kzalloc(sizeof(*aewb_cfg), GFP_KERNEL); 295 if (!aewb_cfg) 296 return -ENOMEM; 297 298 aewb->ops = &h3a_aewb_ops; 299 aewb->priv = aewb_cfg; 300 aewb->event_type = V4L2_EVENT_OMAP3ISP_AEWB; 301 aewb->isp = isp; 302 303 /* Set recover state configuration */ 304 aewb_recover_cfg = kzalloc(sizeof(*aewb_recover_cfg), GFP_KERNEL); 305 if (!aewb_recover_cfg) { 306 dev_err(aewb->isp->dev, 307 "AEWB: cannot allocate memory for recover configuration.\n"); 308 ret = -ENOMEM; 309 goto err; 310 } 311 312 aewb_recover_cfg->saturation_limit = OMAP3ISP_AEWB_MAX_SATURATION_LIM; 313 aewb_recover_cfg->win_height = OMAP3ISP_AEWB_MIN_WIN_H; 314 aewb_recover_cfg->win_width = OMAP3ISP_AEWB_MIN_WIN_W; 315 aewb_recover_cfg->ver_win_count = OMAP3ISP_AEWB_MIN_WINVC; 316 aewb_recover_cfg->hor_win_count = OMAP3ISP_AEWB_MIN_WINHC; 317 aewb_recover_cfg->blk_ver_win_start = aewb_recover_cfg->ver_win_start + 318 aewb_recover_cfg->win_height * aewb_recover_cfg->ver_win_count; 319 aewb_recover_cfg->blk_win_height = OMAP3ISP_AEWB_MIN_WIN_H; 320 aewb_recover_cfg->subsample_ver_inc = OMAP3ISP_AEWB_MIN_SUB_INC; 321 aewb_recover_cfg->subsample_hor_inc = OMAP3ISP_AEWB_MIN_SUB_INC; 322 323 if (h3a_aewb_validate_params(aewb, aewb_recover_cfg)) { 324 dev_err(aewb->isp->dev, 325 "AEWB: recover configuration is invalid.\n"); 326 ret = -EINVAL; 327 goto err; 328 } 329 330 aewb_recover_cfg->buf_size = h3a_aewb_get_buf_size(aewb_recover_cfg); 331 aewb->recover_priv = aewb_recover_cfg; 332 333 ret = omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops); 334 335 err: 336 if (ret) { 337 kfree(aewb_cfg); 338 kfree(aewb_recover_cfg); 339 } 340 341 return ret; 342 } 343 344 /* 345 * omap3isp_h3a_aewb_cleanup - Module exit. 346 */ 347 void omap3isp_h3a_aewb_cleanup(struct isp_device *isp) 348 { 349 omap3isp_stat_cleanup(&isp->isp_aewb); 350 } 351