1/* 2 * Register interface file for Samsung Camera Interface (FIMC) driver 3 * 4 * Copyright (c) 2010 Samsung Electronics 5 * 6 * Sylwester Nawrocki, s.nawrocki@samsung.com 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11*/ 12 13#include <linux/io.h> 14#include <linux/delay.h> 15#include <mach/map.h> 16 17#include "fimc-core.h" 18 19 20void fimc_hw_reset(struct fimc_dev *dev) 21{ 22 u32 cfg; 23 24 cfg = readl(dev->regs + S5P_CISRCFMT); 25 cfg |= S5P_CISRCFMT_ITU601_8BIT; 26 writel(cfg, dev->regs + S5P_CISRCFMT); 27 28 /* Software reset. */ 29 cfg = readl(dev->regs + S5P_CIGCTRL); 30 cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL); 31 writel(cfg, dev->regs + S5P_CIGCTRL); 32 msleep(1); 33 34 cfg = readl(dev->regs + S5P_CIGCTRL); 35 cfg &= ~S5P_CIGCTRL_SWRST; 36 writel(cfg, dev->regs + S5P_CIGCTRL); 37 38} 39 40void fimc_hw_set_rotation(struct fimc_ctx *ctx) 41{ 42 u32 cfg, flip; 43 struct fimc_dev *dev = ctx->fimc_dev; 44 45 cfg = readl(dev->regs + S5P_CITRGFMT); 46 cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90); 47 48 flip = readl(dev->regs + S5P_MSCTRL); 49 flip &= ~S5P_MSCTRL_FLIP_MASK; 50 51 /* 52 * The input and output rotator cannot work simultaneously. 53 * Use the output rotator in output DMA mode or the input rotator 54 * in direct fifo output mode. 55 */ 56 if (ctx->rotation == 90 || ctx->rotation == 270) { 57 if (ctx->out_path == FIMC_LCDFIFO) { 58 cfg |= S5P_CITRGFMT_INROT90; 59 if (ctx->rotation == 270) 60 flip |= S5P_MSCTRL_FLIP_180; 61 } else { 62 cfg |= S5P_CITRGFMT_OUTROT90; 63 if (ctx->rotation == 270) 64 cfg |= S5P_CITRGFMT_FLIP_180; 65 } 66 } else if (ctx->rotation == 180) { 67 if (ctx->out_path == FIMC_LCDFIFO) 68 flip |= S5P_MSCTRL_FLIP_180; 69 else 70 cfg |= S5P_CITRGFMT_FLIP_180; 71 } 72 if (ctx->rotation == 180 || ctx->rotation == 270) 73 writel(flip, dev->regs + S5P_MSCTRL); 74 writel(cfg, dev->regs + S5P_CITRGFMT); 75} 76 77static u32 fimc_hw_get_in_flip(u32 ctx_flip) 78{ 79 u32 flip = S5P_MSCTRL_FLIP_NORMAL; 80 81 switch (ctx_flip) { 82 case FLIP_X_AXIS: 83 flip = S5P_MSCTRL_FLIP_X_MIRROR; 84 break; 85 case FLIP_Y_AXIS: 86 flip = S5P_MSCTRL_FLIP_Y_MIRROR; 87 break; 88 case FLIP_XY_AXIS: 89 flip = S5P_MSCTRL_FLIP_180; 90 break; 91 } 92 93 return flip; 94} 95 96static u32 fimc_hw_get_target_flip(u32 ctx_flip) 97{ 98 u32 flip = S5P_CITRGFMT_FLIP_NORMAL; 99 100 switch (ctx_flip) { 101 case FLIP_X_AXIS: 102 flip = S5P_CITRGFMT_FLIP_X_MIRROR; 103 break; 104 case FLIP_Y_AXIS: 105 flip = S5P_CITRGFMT_FLIP_Y_MIRROR; 106 break; 107 case FLIP_XY_AXIS: 108 flip = S5P_CITRGFMT_FLIP_180; 109 break; 110 case FLIP_NONE: 111 break; 112 113 } 114 return flip; 115} 116 117void fimc_hw_set_target_format(struct fimc_ctx *ctx) 118{ 119 u32 cfg; 120 struct fimc_dev *dev = ctx->fimc_dev; 121 struct fimc_frame *frame = &ctx->d_frame; 122 123 dbg("w= %d, h= %d color: %d", frame->width, 124 frame->height, frame->fmt->color); 125 126 cfg = readl(dev->regs + S5P_CITRGFMT); 127 cfg &= ~(S5P_CITRGFMT_FMT_MASK | S5P_CITRGFMT_HSIZE_MASK | 128 S5P_CITRGFMT_VSIZE_MASK); 129 130 switch (frame->fmt->color) { 131 case S5P_FIMC_RGB565: 132 case S5P_FIMC_RGB666: 133 case S5P_FIMC_RGB888: 134 cfg |= S5P_CITRGFMT_RGB; 135 break; 136 case S5P_FIMC_YCBCR420: 137 cfg |= S5P_CITRGFMT_YCBCR420; 138 break; 139 case S5P_FIMC_YCBYCR422: 140 case S5P_FIMC_YCRYCB422: 141 case S5P_FIMC_CBYCRY422: 142 case S5P_FIMC_CRYCBY422: 143 if (frame->fmt->planes_cnt == 1) 144 cfg |= S5P_CITRGFMT_YCBCR422_1P; 145 else 146 cfg |= S5P_CITRGFMT_YCBCR422; 147 break; 148 default: 149 break; 150 } 151 152 cfg |= S5P_CITRGFMT_HSIZE(frame->width); 153 cfg |= S5P_CITRGFMT_VSIZE(frame->height); 154 155 if (ctx->rotation == 0) { 156 cfg &= ~S5P_CITRGFMT_FLIP_MASK; 157 cfg |= fimc_hw_get_target_flip(ctx->flip); 158 } 159 writel(cfg, dev->regs + S5P_CITRGFMT); 160 161 cfg = readl(dev->regs + S5P_CITAREA) & ~S5P_CITAREA_MASK; 162 cfg |= (frame->width * frame->height); 163 writel(cfg, dev->regs + S5P_CITAREA); 164} 165 166static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx) 167{ 168 struct fimc_dev *dev = ctx->fimc_dev; 169 struct fimc_frame *frame = &ctx->d_frame; 170 u32 cfg = 0; 171 172 if (ctx->rotation == 90 || ctx->rotation == 270) { 173 cfg |= S5P_ORIG_SIZE_HOR(frame->f_height); 174 cfg |= S5P_ORIG_SIZE_VER(frame->f_width); 175 } else { 176 cfg |= S5P_ORIG_SIZE_HOR(frame->f_width); 177 cfg |= S5P_ORIG_SIZE_VER(frame->f_height); 178 } 179 writel(cfg, dev->regs + S5P_ORGOSIZE); 180} 181 182void fimc_hw_set_out_dma(struct fimc_ctx *ctx) 183{ 184 u32 cfg; 185 struct fimc_dev *dev = ctx->fimc_dev; 186 struct fimc_frame *frame = &ctx->d_frame; 187 struct fimc_dma_offset *offset = &frame->dma_offset; 188 189 /* Set the input dma offsets. */ 190 cfg = 0; 191 cfg |= S5P_CIO_OFFS_HOR(offset->y_h); 192 cfg |= S5P_CIO_OFFS_VER(offset->y_v); 193 writel(cfg, dev->regs + S5P_CIOYOFF); 194 195 cfg = 0; 196 cfg |= S5P_CIO_OFFS_HOR(offset->cb_h); 197 cfg |= S5P_CIO_OFFS_VER(offset->cb_v); 198 writel(cfg, dev->regs + S5P_CIOCBOFF); 199 200 cfg = 0; 201 cfg |= S5P_CIO_OFFS_HOR(offset->cr_h); 202 cfg |= S5P_CIO_OFFS_VER(offset->cr_v); 203 writel(cfg, dev->regs + S5P_CIOCROFF); 204 205 fimc_hw_set_out_dma_size(ctx); 206 207 /* Configure chroma components order. */ 208 cfg = readl(dev->regs + S5P_CIOCTRL); 209 210 cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK | 211 S5P_CIOCTRL_YCBCR_PLANE_MASK); 212 213 if (frame->fmt->planes_cnt == 1) 214 cfg |= ctx->out_order_1p; 215 else if (frame->fmt->planes_cnt == 2) 216 cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE; 217 else if (frame->fmt->planes_cnt == 3) 218 cfg |= S5P_CIOCTRL_YCBCR_3PLANE; 219 220 writel(cfg, dev->regs + S5P_CIOCTRL); 221} 222 223static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable) 224{ 225 u32 cfg = readl(dev->regs + S5P_ORGISIZE); 226 if (enable) 227 cfg |= S5P_CIREAL_ISIZE_AUTOLOAD_EN; 228 else 229 cfg &= ~S5P_CIREAL_ISIZE_AUTOLOAD_EN; 230 writel(cfg, dev->regs + S5P_ORGISIZE); 231} 232 233void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable) 234{ 235 unsigned long flags; 236 u32 cfg; 237 238 spin_lock_irqsave(&dev->slock, flags); 239 240 cfg = readl(dev->regs + S5P_CIOCTRL); 241 if (enable) 242 cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE; 243 else 244 cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE; 245 writel(cfg, dev->regs + S5P_CIOCTRL); 246 247 spin_unlock_irqrestore(&dev->slock, flags); 248} 249 250void fimc_hw_set_prescaler(struct fimc_ctx *ctx) 251{ 252 struct fimc_dev *dev = ctx->fimc_dev; 253 struct fimc_scaler *sc = &ctx->scaler; 254 u32 cfg = 0, shfactor; 255 256 shfactor = 10 - (sc->hfactor + sc->vfactor); 257 258 cfg |= S5P_CISCPRERATIO_SHFACTOR(shfactor); 259 cfg |= S5P_CISCPRERATIO_HOR(sc->pre_hratio); 260 cfg |= S5P_CISCPRERATIO_VER(sc->pre_vratio); 261 writel(cfg, dev->regs + S5P_CISCPRERATIO); 262 263 cfg = 0; 264 cfg |= S5P_CISCPREDST_WIDTH(sc->pre_dst_width); 265 cfg |= S5P_CISCPREDST_HEIGHT(sc->pre_dst_height); 266 writel(cfg, dev->regs + S5P_CISCPREDST); 267} 268 269void fimc_hw_set_scaler(struct fimc_ctx *ctx) 270{ 271 struct fimc_dev *dev = ctx->fimc_dev; 272 struct fimc_scaler *sc = &ctx->scaler; 273 struct fimc_frame *src_frame = &ctx->s_frame; 274 struct fimc_frame *dst_frame = &ctx->d_frame; 275 u32 cfg = 0; 276 277 if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW)) 278 cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE); 279 280 if (!sc->enabled) 281 cfg |= S5P_CISCCTRL_SCALERBYPASS; 282 283 if (sc->scaleup_h) 284 cfg |= S5P_CISCCTRL_SCALEUP_H; 285 286 if (sc->scaleup_v) 287 cfg |= S5P_CISCCTRL_SCALEUP_V; 288 289 if (sc->copy_mode) 290 cfg |= S5P_CISCCTRL_ONE2ONE; 291 292 293 if (ctx->in_path == FIMC_DMA) { 294 if (src_frame->fmt->color == S5P_FIMC_RGB565) 295 cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565; 296 else if (src_frame->fmt->color == S5P_FIMC_RGB666) 297 cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666; 298 else if (src_frame->fmt->color == S5P_FIMC_RGB888) 299 cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888; 300 } 301 302 if (ctx->out_path == FIMC_DMA) { 303 if (dst_frame->fmt->color == S5P_FIMC_RGB565) 304 cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565; 305 else if (dst_frame->fmt->color == S5P_FIMC_RGB666) 306 cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666; 307 else if (dst_frame->fmt->color == S5P_FIMC_RGB888) 308 cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888; 309 } else { 310 cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888; 311 312 if (ctx->flags & FIMC_SCAN_MODE_INTERLACED) 313 cfg |= S5P_CISCCTRL_INTERLACE; 314 } 315 316 dbg("main_hratio= 0x%X main_vratio= 0x%X", 317 sc->main_hratio, sc->main_vratio); 318 319 cfg |= S5P_CISCCTRL_SC_HORRATIO(sc->main_hratio); 320 cfg |= S5P_CISCCTRL_SC_VERRATIO(sc->main_vratio); 321 322 writel(cfg, dev->regs + S5P_CISCCTRL); 323} 324 325void fimc_hw_en_capture(struct fimc_ctx *ctx) 326{ 327 struct fimc_dev *dev = ctx->fimc_dev; 328 u32 cfg; 329 330 cfg = readl(dev->regs + S5P_CIIMGCPT); 331 /* One shot mode for output DMA or freerun for FIFO. */ 332 if (ctx->out_path == FIMC_DMA) 333 cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE; 334 else 335 cfg &= ~S5P_CIIMGCPT_CPT_FREN_ENABLE; 336 337 if (ctx->scaler.enabled) 338 cfg |= S5P_CIIMGCPT_IMGCPTEN_SC; 339 340 writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT); 341} 342 343void fimc_hw_set_effect(struct fimc_ctx *ctx) 344{ 345 struct fimc_dev *dev = ctx->fimc_dev; 346 struct fimc_effect *effect = &ctx->effect; 347 u32 cfg = (S5P_CIIMGEFF_IE_ENABLE | S5P_CIIMGEFF_IE_SC_AFTER); 348 349 cfg |= effect->type; 350 351 if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) { 352 cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb); 353 cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr); 354 } 355 356 writel(cfg, dev->regs + S5P_CIIMGEFF); 357} 358 359static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx) 360{ 361 struct fimc_dev *dev = ctx->fimc_dev; 362 struct fimc_frame *frame = &ctx->s_frame; 363 u32 cfg_o = 0; 364 u32 cfg_r = 0; 365 366 if (FIMC_LCDFIFO == ctx->out_path) 367 cfg_r |= S5P_CIREAL_ISIZE_AUTOLOAD_EN; 368 369 cfg_o |= S5P_ORIG_SIZE_HOR(frame->f_width); 370 cfg_o |= S5P_ORIG_SIZE_VER(frame->f_height); 371 cfg_r |= S5P_CIREAL_ISIZE_WIDTH(frame->width); 372 cfg_r |= S5P_CIREAL_ISIZE_HEIGHT(frame->height); 373 374 writel(cfg_o, dev->regs + S5P_ORGISIZE); 375 writel(cfg_r, dev->regs + S5P_CIREAL_ISIZE); 376} 377 378void fimc_hw_set_in_dma(struct fimc_ctx *ctx) 379{ 380 struct fimc_dev *dev = ctx->fimc_dev; 381 struct fimc_frame *frame = &ctx->s_frame; 382 struct fimc_dma_offset *offset = &frame->dma_offset; 383 u32 cfg = 0; 384 385 /* Set the pixel offsets. */ 386 cfg |= S5P_CIO_OFFS_HOR(offset->y_h); 387 cfg |= S5P_CIO_OFFS_VER(offset->y_v); 388 writel(cfg, dev->regs + S5P_CIIYOFF); 389 390 cfg = 0; 391 cfg |= S5P_CIO_OFFS_HOR(offset->cb_h); 392 cfg |= S5P_CIO_OFFS_VER(offset->cb_v); 393 writel(cfg, dev->regs + S5P_CIICBOFF); 394 395 cfg = 0; 396 cfg |= S5P_CIO_OFFS_HOR(offset->cr_h); 397 cfg |= S5P_CIO_OFFS_VER(offset->cr_v); 398 writel(cfg, dev->regs + S5P_CIICROFF); 399 400 /* Input original and real size. */ 401 fimc_hw_set_in_dma_size(ctx); 402 403 /* Autoload is used currently only in FIFO mode. */ 404 fimc_hw_en_autoload(dev, ctx->out_path == FIMC_LCDFIFO); 405 406 /* Set the input DMA to process single frame only. */ 407 cfg = readl(dev->regs + S5P_MSCTRL); 408 cfg &= ~(S5P_MSCTRL_FLIP_MASK 409 | S5P_MSCTRL_INFORMAT_MASK 410 | S5P_MSCTRL_IN_BURST_COUNT_MASK 411 | S5P_MSCTRL_INPUT_MASK 412 | S5P_MSCTRL_C_INT_IN_MASK 413 | S5P_MSCTRL_2P_IN_ORDER_MASK); 414 415 cfg |= (S5P_MSCTRL_FRAME_COUNT(1) | S5P_MSCTRL_INPUT_MEMORY); 416 417 switch (frame->fmt->color) { 418 case S5P_FIMC_RGB565: 419 case S5P_FIMC_RGB666: 420 case S5P_FIMC_RGB888: 421 cfg |= S5P_MSCTRL_INFORMAT_RGB; 422 break; 423 case S5P_FIMC_YCBCR420: 424 cfg |= S5P_MSCTRL_INFORMAT_YCBCR420; 425 426 if (frame->fmt->planes_cnt == 2) 427 cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE; 428 else 429 cfg |= S5P_MSCTRL_C_INT_IN_3PLANE; 430 431 break; 432 case S5P_FIMC_YCBYCR422: 433 case S5P_FIMC_YCRYCB422: 434 case S5P_FIMC_CBYCRY422: 435 case S5P_FIMC_CRYCBY422: 436 if (frame->fmt->planes_cnt == 1) { 437 cfg |= ctx->in_order_1p 438 | S5P_MSCTRL_INFORMAT_YCBCR422_1P; 439 } else { 440 cfg |= S5P_MSCTRL_INFORMAT_YCBCR422; 441 442 if (frame->fmt->planes_cnt == 2) 443 cfg |= ctx->in_order_2p 444 | S5P_MSCTRL_C_INT_IN_2PLANE; 445 else 446 cfg |= S5P_MSCTRL_C_INT_IN_3PLANE; 447 } 448 break; 449 default: 450 break; 451 } 452 453 /* 454 * Input DMA flip mode (and rotation). 455 * Do not allow simultaneous rotation and flipping. 456 */ 457 if (!ctx->rotation && ctx->out_path == FIMC_LCDFIFO) 458 cfg |= fimc_hw_get_in_flip(ctx->flip); 459 460 writel(cfg, dev->regs + S5P_MSCTRL); 461 462 /* Input/output DMA linear/tiled mode. */ 463 cfg = readl(dev->regs + S5P_CIDMAPARAM); 464 cfg &= ~S5P_CIDMAPARAM_TILE_MASK; 465 466 if (tiled_fmt(ctx->s_frame.fmt)) 467 cfg |= S5P_CIDMAPARAM_R_64X32; 468 469 if (tiled_fmt(ctx->d_frame.fmt)) 470 cfg |= S5P_CIDMAPARAM_W_64X32; 471 472 writel(cfg, dev->regs + S5P_CIDMAPARAM); 473} 474 475 476void fimc_hw_set_input_path(struct fimc_ctx *ctx) 477{ 478 struct fimc_dev *dev = ctx->fimc_dev; 479 480 u32 cfg = readl(dev->regs + S5P_MSCTRL); 481 cfg &= ~S5P_MSCTRL_INPUT_MASK; 482 483 if (ctx->in_path == FIMC_DMA) 484 cfg |= S5P_MSCTRL_INPUT_MEMORY; 485 else 486 cfg |= S5P_MSCTRL_INPUT_EXTCAM; 487 488 writel(cfg, dev->regs + S5P_MSCTRL); 489} 490 491void fimc_hw_set_output_path(struct fimc_ctx *ctx) 492{ 493 struct fimc_dev *dev = ctx->fimc_dev; 494 495 u32 cfg = readl(dev->regs + S5P_CISCCTRL); 496 cfg &= ~S5P_CISCCTRL_LCDPATHEN_FIFO; 497 if (ctx->out_path == FIMC_LCDFIFO) 498 cfg |= S5P_CISCCTRL_LCDPATHEN_FIFO; 499 writel(cfg, dev->regs + S5P_CISCCTRL); 500} 501 502void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr) 503{ 504 u32 cfg = 0; 505 506 cfg = readl(dev->regs + S5P_CIREAL_ISIZE); 507 cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DIS; 508 writel(cfg, dev->regs + S5P_CIREAL_ISIZE); 509 510 writel(paddr->y, dev->regs + S5P_CIIYSA0); 511 writel(paddr->cb, dev->regs + S5P_CIICBSA0); 512 writel(paddr->cr, dev->regs + S5P_CIICRSA0); 513 514 cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DIS; 515 writel(cfg, dev->regs + S5P_CIREAL_ISIZE); 516} 517 518void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr) 519{ 520 int i; 521 /* Set all the output register sets to point to single video buffer. */ 522 for (i = 0; i < FIMC_MAX_OUT_BUFS; i++) { 523 writel(paddr->y, dev->regs + S5P_CIOYSA(i)); 524 writel(paddr->cb, dev->regs + S5P_CIOCBSA(i)); 525 writel(paddr->cr, dev->regs + S5P_CIOCRSA(i)); 526 } 527} 528