1 /* 2 * linux/drivers/video/omap2/dss/dispc.c 3 * 4 * Copyright (C) 2009 Nokia Corporation 5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> 6 * 7 * Some code and ideas taken from drivers/video/omap/ driver 8 * by Imre Deak. 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License version 2 as published by 12 * the Free Software Foundation. 13 * 14 * This program is distributed in the hope that it will be useful, but WITHOUT 15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 * more details. 18 * 19 * You should have received a copy of the GNU General Public License along with 20 * this program. If not, see <http://www.gnu.org/licenses/>. 21 */ 22 23 #define DSS_SUBSYS_NAME "DISPC" 24 25 #include <linux/kernel.h> 26 #include <linux/dma-mapping.h> 27 #include <linux/vmalloc.h> 28 #include <linux/export.h> 29 #include <linux/clk.h> 30 #include <linux/io.h> 31 #include <linux/jiffies.h> 32 #include <linux/seq_file.h> 33 #include <linux/delay.h> 34 #include <linux/workqueue.h> 35 #include <linux/hardirq.h> 36 #include <linux/platform_device.h> 37 #include <linux/pm_runtime.h> 38 #include <linux/sizes.h> 39 #include <linux/mfd/syscon.h> 40 #include <linux/regmap.h> 41 #include <linux/of.h> 42 #include <linux/component.h> 43 44 #include "omapdss.h" 45 #include "dss.h" 46 #include "dss_features.h" 47 #include "dispc.h" 48 49 /* DISPC */ 50 #define DISPC_SZ_REGS SZ_4K 51 52 enum omap_burst_size { 53 BURST_SIZE_X2 = 0, 54 BURST_SIZE_X4 = 1, 55 BURST_SIZE_X8 = 2, 56 }; 57 58 #define REG_GET(idx, start, end) \ 59 FLD_GET(dispc_read_reg(idx), start, end) 60 61 #define REG_FLD_MOD(idx, val, start, end) \ 62 dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end)) 63 64 struct dispc_features { 65 u8 sw_start; 66 u8 fp_start; 67 u8 bp_start; 68 u16 sw_max; 69 u16 vp_max; 70 u16 hp_max; 71 u8 mgr_width_start; 72 u8 mgr_height_start; 73 u16 mgr_width_max; 74 u16 mgr_height_max; 75 unsigned long max_lcd_pclk; 76 unsigned long max_tv_pclk; 77 int (*calc_scaling) (unsigned long pclk, unsigned long lclk, 78 const struct videomode *vm, 79 u16 width, u16 height, u16 out_width, u16 out_height, 80 enum omap_color_mode color_mode, bool *five_taps, 81 int *x_predecim, int *y_predecim, int *decim_x, int *decim_y, 82 u16 pos_x, unsigned long *core_clk, bool mem_to_mem); 83 unsigned long (*calc_core_clk) (unsigned long pclk, 84 u16 width, u16 height, u16 out_width, u16 out_height, 85 bool mem_to_mem); 86 u8 num_fifos; 87 88 /* swap GFX & WB fifos */ 89 bool gfx_fifo_workaround:1; 90 91 /* no DISPC_IRQ_FRAMEDONETV on this SoC */ 92 bool no_framedone_tv:1; 93 94 /* revert to the OMAP4 mechanism of DISPC Smart Standby operation */ 95 bool mstandby_workaround:1; 96 97 bool set_max_preload:1; 98 99 /* PIXEL_INC is not added to the last pixel of a line */ 100 bool last_pixel_inc_missing:1; 101 102 /* POL_FREQ has ALIGN bit */ 103 bool supports_sync_align:1; 104 105 bool has_writeback:1; 106 107 bool supports_double_pixel:1; 108 109 /* 110 * Field order for VENC is different than HDMI. We should handle this in 111 * some intelligent manner, but as the SoCs have either HDMI or VENC, 112 * never both, we can just use this flag for now. 113 */ 114 bool reverse_ilace_field_order:1; 115 116 bool has_gamma_table:1; 117 118 bool has_gamma_i734_bug:1; 119 }; 120 121 #define DISPC_MAX_NR_FIFOS 5 122 #define DISPC_MAX_CHANNEL_GAMMA 4 123 124 static struct { 125 struct platform_device *pdev; 126 void __iomem *base; 127 128 int irq; 129 irq_handler_t user_handler; 130 void *user_data; 131 132 unsigned long core_clk_rate; 133 unsigned long tv_pclk_rate; 134 135 u32 fifo_size[DISPC_MAX_NR_FIFOS]; 136 /* maps which plane is using a fifo. fifo-id -> plane-id */ 137 int fifo_assignment[DISPC_MAX_NR_FIFOS]; 138 139 bool ctx_valid; 140 u32 ctx[DISPC_SZ_REGS / sizeof(u32)]; 141 142 u32 *gamma_table[DISPC_MAX_CHANNEL_GAMMA]; 143 144 const struct dispc_features *feat; 145 146 bool is_enabled; 147 148 struct regmap *syscon_pol; 149 u32 syscon_pol_offset; 150 151 /* DISPC_CONTROL & DISPC_CONFIG lock*/ 152 spinlock_t control_lock; 153 } dispc; 154 155 enum omap_color_component { 156 /* used for all color formats for OMAP3 and earlier 157 * and for RGB and Y color component on OMAP4 158 */ 159 DISPC_COLOR_COMPONENT_RGB_Y = 1 << 0, 160 /* used for UV component for 161 * OMAP_DSS_COLOR_YUV2, OMAP_DSS_COLOR_UYVY, OMAP_DSS_COLOR_NV12 162 * color formats on OMAP4 163 */ 164 DISPC_COLOR_COMPONENT_UV = 1 << 1, 165 }; 166 167 enum mgr_reg_fields { 168 DISPC_MGR_FLD_ENABLE, 169 DISPC_MGR_FLD_STNTFT, 170 DISPC_MGR_FLD_GO, 171 DISPC_MGR_FLD_TFTDATALINES, 172 DISPC_MGR_FLD_STALLMODE, 173 DISPC_MGR_FLD_TCKENABLE, 174 DISPC_MGR_FLD_TCKSELECTION, 175 DISPC_MGR_FLD_CPR, 176 DISPC_MGR_FLD_FIFOHANDCHECK, 177 /* used to maintain a count of the above fields */ 178 DISPC_MGR_FLD_NUM, 179 }; 180 181 struct dispc_reg_field { 182 u16 reg; 183 u8 high; 184 u8 low; 185 }; 186 187 struct dispc_gamma_desc { 188 u32 len; 189 u32 bits; 190 u16 reg; 191 bool has_index; 192 }; 193 194 static const struct { 195 const char *name; 196 u32 vsync_irq; 197 u32 framedone_irq; 198 u32 sync_lost_irq; 199 struct dispc_gamma_desc gamma; 200 struct dispc_reg_field reg_desc[DISPC_MGR_FLD_NUM]; 201 } mgr_desc[] = { 202 [OMAP_DSS_CHANNEL_LCD] = { 203 .name = "LCD", 204 .vsync_irq = DISPC_IRQ_VSYNC, 205 .framedone_irq = DISPC_IRQ_FRAMEDONE, 206 .sync_lost_irq = DISPC_IRQ_SYNC_LOST, 207 .gamma = { 208 .len = 256, 209 .bits = 8, 210 .reg = DISPC_GAMMA_TABLE0, 211 .has_index = true, 212 }, 213 .reg_desc = { 214 [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL, 0, 0 }, 215 [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL, 3, 3 }, 216 [DISPC_MGR_FLD_GO] = { DISPC_CONTROL, 5, 5 }, 217 [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL, 9, 8 }, 218 [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL, 11, 11 }, 219 [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG, 10, 10 }, 220 [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG, 11, 11 }, 221 [DISPC_MGR_FLD_CPR] = { DISPC_CONFIG, 15, 15 }, 222 [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG, 16, 16 }, 223 }, 224 }, 225 [OMAP_DSS_CHANNEL_DIGIT] = { 226 .name = "DIGIT", 227 .vsync_irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN, 228 .framedone_irq = DISPC_IRQ_FRAMEDONETV, 229 .sync_lost_irq = DISPC_IRQ_SYNC_LOST_DIGIT, 230 .gamma = { 231 .len = 1024, 232 .bits = 10, 233 .reg = DISPC_GAMMA_TABLE2, 234 .has_index = false, 235 }, 236 .reg_desc = { 237 [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL, 1, 1 }, 238 [DISPC_MGR_FLD_STNTFT] = { }, 239 [DISPC_MGR_FLD_GO] = { DISPC_CONTROL, 6, 6 }, 240 [DISPC_MGR_FLD_TFTDATALINES] = { }, 241 [DISPC_MGR_FLD_STALLMODE] = { }, 242 [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG, 12, 12 }, 243 [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG, 13, 13 }, 244 [DISPC_MGR_FLD_CPR] = { }, 245 [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG, 16, 16 }, 246 }, 247 }, 248 [OMAP_DSS_CHANNEL_LCD2] = { 249 .name = "LCD2", 250 .vsync_irq = DISPC_IRQ_VSYNC2, 251 .framedone_irq = DISPC_IRQ_FRAMEDONE2, 252 .sync_lost_irq = DISPC_IRQ_SYNC_LOST2, 253 .gamma = { 254 .len = 256, 255 .bits = 8, 256 .reg = DISPC_GAMMA_TABLE1, 257 .has_index = true, 258 }, 259 .reg_desc = { 260 [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL2, 0, 0 }, 261 [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL2, 3, 3 }, 262 [DISPC_MGR_FLD_GO] = { DISPC_CONTROL2, 5, 5 }, 263 [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL2, 9, 8 }, 264 [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL2, 11, 11 }, 265 [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG2, 10, 10 }, 266 [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG2, 11, 11 }, 267 [DISPC_MGR_FLD_CPR] = { DISPC_CONFIG2, 15, 15 }, 268 [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG2, 16, 16 }, 269 }, 270 }, 271 [OMAP_DSS_CHANNEL_LCD3] = { 272 .name = "LCD3", 273 .vsync_irq = DISPC_IRQ_VSYNC3, 274 .framedone_irq = DISPC_IRQ_FRAMEDONE3, 275 .sync_lost_irq = DISPC_IRQ_SYNC_LOST3, 276 .gamma = { 277 .len = 256, 278 .bits = 8, 279 .reg = DISPC_GAMMA_TABLE3, 280 .has_index = true, 281 }, 282 .reg_desc = { 283 [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL3, 0, 0 }, 284 [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL3, 3, 3 }, 285 [DISPC_MGR_FLD_GO] = { DISPC_CONTROL3, 5, 5 }, 286 [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL3, 9, 8 }, 287 [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL3, 11, 11 }, 288 [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG3, 10, 10 }, 289 [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG3, 11, 11 }, 290 [DISPC_MGR_FLD_CPR] = { DISPC_CONFIG3, 15, 15 }, 291 [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG3, 16, 16 }, 292 }, 293 }, 294 }; 295 296 struct color_conv_coef { 297 int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb; 298 int full_range; 299 }; 300 301 static unsigned long dispc_fclk_rate(void); 302 static unsigned long dispc_core_clk_rate(void); 303 static unsigned long dispc_mgr_lclk_rate(enum omap_channel channel); 304 static unsigned long dispc_mgr_pclk_rate(enum omap_channel channel); 305 306 static unsigned long dispc_plane_pclk_rate(enum omap_plane plane); 307 static unsigned long dispc_plane_lclk_rate(enum omap_plane plane); 308 309 static inline void dispc_write_reg(const u16 idx, u32 val) 310 { 311 __raw_writel(val, dispc.base + idx); 312 } 313 314 static inline u32 dispc_read_reg(const u16 idx) 315 { 316 return __raw_readl(dispc.base + idx); 317 } 318 319 static u32 mgr_fld_read(enum omap_channel channel, enum mgr_reg_fields regfld) 320 { 321 const struct dispc_reg_field rfld = mgr_desc[channel].reg_desc[regfld]; 322 return REG_GET(rfld.reg, rfld.high, rfld.low); 323 } 324 325 static void mgr_fld_write(enum omap_channel channel, 326 enum mgr_reg_fields regfld, int val) { 327 const struct dispc_reg_field rfld = mgr_desc[channel].reg_desc[regfld]; 328 const bool need_lock = rfld.reg == DISPC_CONTROL || rfld.reg == DISPC_CONFIG; 329 unsigned long flags; 330 331 if (need_lock) 332 spin_lock_irqsave(&dispc.control_lock, flags); 333 334 REG_FLD_MOD(rfld.reg, val, rfld.high, rfld.low); 335 336 if (need_lock) 337 spin_unlock_irqrestore(&dispc.control_lock, flags); 338 } 339 340 #define SR(reg) \ 341 dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg) 342 #define RR(reg) \ 343 dispc_write_reg(DISPC_##reg, dispc.ctx[DISPC_##reg / sizeof(u32)]) 344 345 static void dispc_save_context(void) 346 { 347 int i, j; 348 349 DSSDBG("dispc_save_context\n"); 350 351 SR(IRQENABLE); 352 SR(CONTROL); 353 SR(CONFIG); 354 SR(LINE_NUMBER); 355 if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) || 356 dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) 357 SR(GLOBAL_ALPHA); 358 if (dss_has_feature(FEAT_MGR_LCD2)) { 359 SR(CONTROL2); 360 SR(CONFIG2); 361 } 362 if (dss_has_feature(FEAT_MGR_LCD3)) { 363 SR(CONTROL3); 364 SR(CONFIG3); 365 } 366 367 for (i = 0; i < dss_feat_get_num_mgrs(); i++) { 368 SR(DEFAULT_COLOR(i)); 369 SR(TRANS_COLOR(i)); 370 SR(SIZE_MGR(i)); 371 if (i == OMAP_DSS_CHANNEL_DIGIT) 372 continue; 373 SR(TIMING_H(i)); 374 SR(TIMING_V(i)); 375 SR(POL_FREQ(i)); 376 SR(DIVISORo(i)); 377 378 SR(DATA_CYCLE1(i)); 379 SR(DATA_CYCLE2(i)); 380 SR(DATA_CYCLE3(i)); 381 382 if (dss_has_feature(FEAT_CPR)) { 383 SR(CPR_COEF_R(i)); 384 SR(CPR_COEF_G(i)); 385 SR(CPR_COEF_B(i)); 386 } 387 } 388 389 for (i = 0; i < dss_feat_get_num_ovls(); i++) { 390 SR(OVL_BA0(i)); 391 SR(OVL_BA1(i)); 392 SR(OVL_POSITION(i)); 393 SR(OVL_SIZE(i)); 394 SR(OVL_ATTRIBUTES(i)); 395 SR(OVL_FIFO_THRESHOLD(i)); 396 SR(OVL_ROW_INC(i)); 397 SR(OVL_PIXEL_INC(i)); 398 if (dss_has_feature(FEAT_PRELOAD)) 399 SR(OVL_PRELOAD(i)); 400 if (i == OMAP_DSS_GFX) { 401 SR(OVL_WINDOW_SKIP(i)); 402 SR(OVL_TABLE_BA(i)); 403 continue; 404 } 405 SR(OVL_FIR(i)); 406 SR(OVL_PICTURE_SIZE(i)); 407 SR(OVL_ACCU0(i)); 408 SR(OVL_ACCU1(i)); 409 410 for (j = 0; j < 8; j++) 411 SR(OVL_FIR_COEF_H(i, j)); 412 413 for (j = 0; j < 8; j++) 414 SR(OVL_FIR_COEF_HV(i, j)); 415 416 for (j = 0; j < 5; j++) 417 SR(OVL_CONV_COEF(i, j)); 418 419 if (dss_has_feature(FEAT_FIR_COEF_V)) { 420 for (j = 0; j < 8; j++) 421 SR(OVL_FIR_COEF_V(i, j)); 422 } 423 424 if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { 425 SR(OVL_BA0_UV(i)); 426 SR(OVL_BA1_UV(i)); 427 SR(OVL_FIR2(i)); 428 SR(OVL_ACCU2_0(i)); 429 SR(OVL_ACCU2_1(i)); 430 431 for (j = 0; j < 8; j++) 432 SR(OVL_FIR_COEF_H2(i, j)); 433 434 for (j = 0; j < 8; j++) 435 SR(OVL_FIR_COEF_HV2(i, j)); 436 437 for (j = 0; j < 8; j++) 438 SR(OVL_FIR_COEF_V2(i, j)); 439 } 440 if (dss_has_feature(FEAT_ATTR2)) 441 SR(OVL_ATTRIBUTES2(i)); 442 } 443 444 if (dss_has_feature(FEAT_CORE_CLK_DIV)) 445 SR(DIVISOR); 446 447 dispc.ctx_valid = true; 448 449 DSSDBG("context saved\n"); 450 } 451 452 static void dispc_restore_context(void) 453 { 454 int i, j; 455 456 DSSDBG("dispc_restore_context\n"); 457 458 if (!dispc.ctx_valid) 459 return; 460 461 /*RR(IRQENABLE);*/ 462 /*RR(CONTROL);*/ 463 RR(CONFIG); 464 RR(LINE_NUMBER); 465 if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) || 466 dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) 467 RR(GLOBAL_ALPHA); 468 if (dss_has_feature(FEAT_MGR_LCD2)) 469 RR(CONFIG2); 470 if (dss_has_feature(FEAT_MGR_LCD3)) 471 RR(CONFIG3); 472 473 for (i = 0; i < dss_feat_get_num_mgrs(); i++) { 474 RR(DEFAULT_COLOR(i)); 475 RR(TRANS_COLOR(i)); 476 RR(SIZE_MGR(i)); 477 if (i == OMAP_DSS_CHANNEL_DIGIT) 478 continue; 479 RR(TIMING_H(i)); 480 RR(TIMING_V(i)); 481 RR(POL_FREQ(i)); 482 RR(DIVISORo(i)); 483 484 RR(DATA_CYCLE1(i)); 485 RR(DATA_CYCLE2(i)); 486 RR(DATA_CYCLE3(i)); 487 488 if (dss_has_feature(FEAT_CPR)) { 489 RR(CPR_COEF_R(i)); 490 RR(CPR_COEF_G(i)); 491 RR(CPR_COEF_B(i)); 492 } 493 } 494 495 for (i = 0; i < dss_feat_get_num_ovls(); i++) { 496 RR(OVL_BA0(i)); 497 RR(OVL_BA1(i)); 498 RR(OVL_POSITION(i)); 499 RR(OVL_SIZE(i)); 500 RR(OVL_ATTRIBUTES(i)); 501 RR(OVL_FIFO_THRESHOLD(i)); 502 RR(OVL_ROW_INC(i)); 503 RR(OVL_PIXEL_INC(i)); 504 if (dss_has_feature(FEAT_PRELOAD)) 505 RR(OVL_PRELOAD(i)); 506 if (i == OMAP_DSS_GFX) { 507 RR(OVL_WINDOW_SKIP(i)); 508 RR(OVL_TABLE_BA(i)); 509 continue; 510 } 511 RR(OVL_FIR(i)); 512 RR(OVL_PICTURE_SIZE(i)); 513 RR(OVL_ACCU0(i)); 514 RR(OVL_ACCU1(i)); 515 516 for (j = 0; j < 8; j++) 517 RR(OVL_FIR_COEF_H(i, j)); 518 519 for (j = 0; j < 8; j++) 520 RR(OVL_FIR_COEF_HV(i, j)); 521 522 for (j = 0; j < 5; j++) 523 RR(OVL_CONV_COEF(i, j)); 524 525 if (dss_has_feature(FEAT_FIR_COEF_V)) { 526 for (j = 0; j < 8; j++) 527 RR(OVL_FIR_COEF_V(i, j)); 528 } 529 530 if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { 531 RR(OVL_BA0_UV(i)); 532 RR(OVL_BA1_UV(i)); 533 RR(OVL_FIR2(i)); 534 RR(OVL_ACCU2_0(i)); 535 RR(OVL_ACCU2_1(i)); 536 537 for (j = 0; j < 8; j++) 538 RR(OVL_FIR_COEF_H2(i, j)); 539 540 for (j = 0; j < 8; j++) 541 RR(OVL_FIR_COEF_HV2(i, j)); 542 543 for (j = 0; j < 8; j++) 544 RR(OVL_FIR_COEF_V2(i, j)); 545 } 546 if (dss_has_feature(FEAT_ATTR2)) 547 RR(OVL_ATTRIBUTES2(i)); 548 } 549 550 if (dss_has_feature(FEAT_CORE_CLK_DIV)) 551 RR(DIVISOR); 552 553 /* enable last, because LCD & DIGIT enable are here */ 554 RR(CONTROL); 555 if (dss_has_feature(FEAT_MGR_LCD2)) 556 RR(CONTROL2); 557 if (dss_has_feature(FEAT_MGR_LCD3)) 558 RR(CONTROL3); 559 /* clear spurious SYNC_LOST_DIGIT interrupts */ 560 dispc_clear_irqstatus(DISPC_IRQ_SYNC_LOST_DIGIT); 561 562 /* 563 * enable last so IRQs won't trigger before 564 * the context is fully restored 565 */ 566 RR(IRQENABLE); 567 568 DSSDBG("context restored\n"); 569 } 570 571 #undef SR 572 #undef RR 573 574 int dispc_runtime_get(void) 575 { 576 int r; 577 578 DSSDBG("dispc_runtime_get\n"); 579 580 r = pm_runtime_get_sync(&dispc.pdev->dev); 581 WARN_ON(r < 0); 582 return r < 0 ? r : 0; 583 } 584 EXPORT_SYMBOL(dispc_runtime_get); 585 586 void dispc_runtime_put(void) 587 { 588 int r; 589 590 DSSDBG("dispc_runtime_put\n"); 591 592 r = pm_runtime_put_sync(&dispc.pdev->dev); 593 WARN_ON(r < 0 && r != -ENOSYS); 594 } 595 EXPORT_SYMBOL(dispc_runtime_put); 596 597 u32 dispc_mgr_get_vsync_irq(enum omap_channel channel) 598 { 599 return mgr_desc[channel].vsync_irq; 600 } 601 EXPORT_SYMBOL(dispc_mgr_get_vsync_irq); 602 603 u32 dispc_mgr_get_framedone_irq(enum omap_channel channel) 604 { 605 if (channel == OMAP_DSS_CHANNEL_DIGIT && dispc.feat->no_framedone_tv) 606 return 0; 607 608 return mgr_desc[channel].framedone_irq; 609 } 610 EXPORT_SYMBOL(dispc_mgr_get_framedone_irq); 611 612 u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel) 613 { 614 return mgr_desc[channel].sync_lost_irq; 615 } 616 EXPORT_SYMBOL(dispc_mgr_get_sync_lost_irq); 617 618 u32 dispc_wb_get_framedone_irq(void) 619 { 620 return DISPC_IRQ_FRAMEDONEWB; 621 } 622 623 bool dispc_mgr_go_busy(enum omap_channel channel) 624 { 625 return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1; 626 } 627 EXPORT_SYMBOL(dispc_mgr_go_busy); 628 629 void dispc_mgr_go(enum omap_channel channel) 630 { 631 WARN_ON(!dispc_mgr_is_enabled(channel)); 632 WARN_ON(dispc_mgr_go_busy(channel)); 633 634 DSSDBG("GO %s\n", mgr_desc[channel].name); 635 636 mgr_fld_write(channel, DISPC_MGR_FLD_GO, 1); 637 } 638 EXPORT_SYMBOL(dispc_mgr_go); 639 640 bool dispc_wb_go_busy(void) 641 { 642 return REG_GET(DISPC_CONTROL2, 6, 6) == 1; 643 } 644 645 void dispc_wb_go(void) 646 { 647 enum omap_plane plane = OMAP_DSS_WB; 648 bool enable, go; 649 650 enable = REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0) == 1; 651 652 if (!enable) 653 return; 654 655 go = REG_GET(DISPC_CONTROL2, 6, 6) == 1; 656 if (go) { 657 DSSERR("GO bit not down for WB\n"); 658 return; 659 } 660 661 REG_FLD_MOD(DISPC_CONTROL2, 1, 6, 6); 662 } 663 664 static void dispc_ovl_write_firh_reg(enum omap_plane plane, int reg, u32 value) 665 { 666 dispc_write_reg(DISPC_OVL_FIR_COEF_H(plane, reg), value); 667 } 668 669 static void dispc_ovl_write_firhv_reg(enum omap_plane plane, int reg, u32 value) 670 { 671 dispc_write_reg(DISPC_OVL_FIR_COEF_HV(plane, reg), value); 672 } 673 674 static void dispc_ovl_write_firv_reg(enum omap_plane plane, int reg, u32 value) 675 { 676 dispc_write_reg(DISPC_OVL_FIR_COEF_V(plane, reg), value); 677 } 678 679 static void dispc_ovl_write_firh2_reg(enum omap_plane plane, int reg, u32 value) 680 { 681 BUG_ON(plane == OMAP_DSS_GFX); 682 683 dispc_write_reg(DISPC_OVL_FIR_COEF_H2(plane, reg), value); 684 } 685 686 static void dispc_ovl_write_firhv2_reg(enum omap_plane plane, int reg, 687 u32 value) 688 { 689 BUG_ON(plane == OMAP_DSS_GFX); 690 691 dispc_write_reg(DISPC_OVL_FIR_COEF_HV2(plane, reg), value); 692 } 693 694 static void dispc_ovl_write_firv2_reg(enum omap_plane plane, int reg, u32 value) 695 { 696 BUG_ON(plane == OMAP_DSS_GFX); 697 698 dispc_write_reg(DISPC_OVL_FIR_COEF_V2(plane, reg), value); 699 } 700 701 static void dispc_ovl_set_scale_coef(enum omap_plane plane, int fir_hinc, 702 int fir_vinc, int five_taps, 703 enum omap_color_component color_comp) 704 { 705 const struct dispc_coef *h_coef, *v_coef; 706 int i; 707 708 h_coef = dispc_ovl_get_scale_coef(fir_hinc, true); 709 v_coef = dispc_ovl_get_scale_coef(fir_vinc, five_taps); 710 711 for (i = 0; i < 8; i++) { 712 u32 h, hv; 713 714 h = FLD_VAL(h_coef[i].hc0_vc00, 7, 0) 715 | FLD_VAL(h_coef[i].hc1_vc0, 15, 8) 716 | FLD_VAL(h_coef[i].hc2_vc1, 23, 16) 717 | FLD_VAL(h_coef[i].hc3_vc2, 31, 24); 718 hv = FLD_VAL(h_coef[i].hc4_vc22, 7, 0) 719 | FLD_VAL(v_coef[i].hc1_vc0, 15, 8) 720 | FLD_VAL(v_coef[i].hc2_vc1, 23, 16) 721 | FLD_VAL(v_coef[i].hc3_vc2, 31, 24); 722 723 if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) { 724 dispc_ovl_write_firh_reg(plane, i, h); 725 dispc_ovl_write_firhv_reg(plane, i, hv); 726 } else { 727 dispc_ovl_write_firh2_reg(plane, i, h); 728 dispc_ovl_write_firhv2_reg(plane, i, hv); 729 } 730 731 } 732 733 if (five_taps) { 734 for (i = 0; i < 8; i++) { 735 u32 v; 736 v = FLD_VAL(v_coef[i].hc0_vc00, 7, 0) 737 | FLD_VAL(v_coef[i].hc4_vc22, 15, 8); 738 if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) 739 dispc_ovl_write_firv_reg(plane, i, v); 740 else 741 dispc_ovl_write_firv2_reg(plane, i, v); 742 } 743 } 744 } 745 746 747 static void dispc_ovl_write_color_conv_coef(enum omap_plane plane, 748 const struct color_conv_coef *ct) 749 { 750 #define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0)) 751 752 dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 0), CVAL(ct->rcr, ct->ry)); 753 dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 1), CVAL(ct->gy, ct->rcb)); 754 dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 2), CVAL(ct->gcb, ct->gcr)); 755 dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 3), CVAL(ct->bcr, ct->by)); 756 dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 4), CVAL(0, ct->bcb)); 757 758 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), ct->full_range, 11, 11); 759 760 #undef CVAL 761 } 762 763 static void dispc_setup_color_conv_coef(void) 764 { 765 int i; 766 int num_ovl = dss_feat_get_num_ovls(); 767 const struct color_conv_coef ctbl_bt601_5_ovl = { 768 /* YUV -> RGB */ 769 298, 409, 0, 298, -208, -100, 298, 0, 517, 0, 770 }; 771 const struct color_conv_coef ctbl_bt601_5_wb = { 772 /* RGB -> YUV */ 773 66, 129, 25, 112, -94, -18, -38, -74, 112, 0, 774 }; 775 776 for (i = 1; i < num_ovl; i++) 777 dispc_ovl_write_color_conv_coef(i, &ctbl_bt601_5_ovl); 778 779 if (dispc.feat->has_writeback) 780 dispc_ovl_write_color_conv_coef(OMAP_DSS_WB, &ctbl_bt601_5_wb); 781 } 782 783 static void dispc_ovl_set_ba0(enum omap_plane plane, u32 paddr) 784 { 785 dispc_write_reg(DISPC_OVL_BA0(plane), paddr); 786 } 787 788 static void dispc_ovl_set_ba1(enum omap_plane plane, u32 paddr) 789 { 790 dispc_write_reg(DISPC_OVL_BA1(plane), paddr); 791 } 792 793 static void dispc_ovl_set_ba0_uv(enum omap_plane plane, u32 paddr) 794 { 795 dispc_write_reg(DISPC_OVL_BA0_UV(plane), paddr); 796 } 797 798 static void dispc_ovl_set_ba1_uv(enum omap_plane plane, u32 paddr) 799 { 800 dispc_write_reg(DISPC_OVL_BA1_UV(plane), paddr); 801 } 802 803 static void dispc_ovl_set_pos(enum omap_plane plane, 804 enum omap_overlay_caps caps, int x, int y) 805 { 806 u32 val; 807 808 if ((caps & OMAP_DSS_OVL_CAP_POS) == 0) 809 return; 810 811 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0); 812 813 dispc_write_reg(DISPC_OVL_POSITION(plane), val); 814 } 815 816 static void dispc_ovl_set_input_size(enum omap_plane plane, int width, 817 int height) 818 { 819 u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); 820 821 if (plane == OMAP_DSS_GFX || plane == OMAP_DSS_WB) 822 dispc_write_reg(DISPC_OVL_SIZE(plane), val); 823 else 824 dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val); 825 } 826 827 static void dispc_ovl_set_output_size(enum omap_plane plane, int width, 828 int height) 829 { 830 u32 val; 831 832 BUG_ON(plane == OMAP_DSS_GFX); 833 834 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); 835 836 if (plane == OMAP_DSS_WB) 837 dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val); 838 else 839 dispc_write_reg(DISPC_OVL_SIZE(plane), val); 840 } 841 842 static void dispc_ovl_set_zorder(enum omap_plane plane, 843 enum omap_overlay_caps caps, u8 zorder) 844 { 845 if ((caps & OMAP_DSS_OVL_CAP_ZORDER) == 0) 846 return; 847 848 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), zorder, 27, 26); 849 } 850 851 static void dispc_ovl_enable_zorder_planes(void) 852 { 853 int i; 854 855 if (!dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) 856 return; 857 858 for (i = 0; i < dss_feat_get_num_ovls(); i++) 859 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(i), 1, 25, 25); 860 } 861 862 static void dispc_ovl_set_pre_mult_alpha(enum omap_plane plane, 863 enum omap_overlay_caps caps, bool enable) 864 { 865 if ((caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0) 866 return; 867 868 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28); 869 } 870 871 static void dispc_ovl_setup_global_alpha(enum omap_plane plane, 872 enum omap_overlay_caps caps, u8 global_alpha) 873 { 874 static const unsigned shifts[] = { 0, 8, 16, 24, }; 875 int shift; 876 877 if ((caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0) 878 return; 879 880 shift = shifts[plane]; 881 REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, shift + 7, shift); 882 } 883 884 static void dispc_ovl_set_pix_inc(enum omap_plane plane, s32 inc) 885 { 886 dispc_write_reg(DISPC_OVL_PIXEL_INC(plane), inc); 887 } 888 889 static void dispc_ovl_set_row_inc(enum omap_plane plane, s32 inc) 890 { 891 dispc_write_reg(DISPC_OVL_ROW_INC(plane), inc); 892 } 893 894 static void dispc_ovl_set_color_mode(enum omap_plane plane, 895 enum omap_color_mode color_mode) 896 { 897 u32 m = 0; 898 if (plane != OMAP_DSS_GFX) { 899 switch (color_mode) { 900 case OMAP_DSS_COLOR_NV12: 901 m = 0x0; break; 902 case OMAP_DSS_COLOR_RGBX16: 903 m = 0x1; break; 904 case OMAP_DSS_COLOR_RGBA16: 905 m = 0x2; break; 906 case OMAP_DSS_COLOR_RGB12U: 907 m = 0x4; break; 908 case OMAP_DSS_COLOR_ARGB16: 909 m = 0x5; break; 910 case OMAP_DSS_COLOR_RGB16: 911 m = 0x6; break; 912 case OMAP_DSS_COLOR_ARGB16_1555: 913 m = 0x7; break; 914 case OMAP_DSS_COLOR_RGB24U: 915 m = 0x8; break; 916 case OMAP_DSS_COLOR_RGB24P: 917 m = 0x9; break; 918 case OMAP_DSS_COLOR_YUV2: 919 m = 0xa; break; 920 case OMAP_DSS_COLOR_UYVY: 921 m = 0xb; break; 922 case OMAP_DSS_COLOR_ARGB32: 923 m = 0xc; break; 924 case OMAP_DSS_COLOR_RGBA32: 925 m = 0xd; break; 926 case OMAP_DSS_COLOR_RGBX32: 927 m = 0xe; break; 928 case OMAP_DSS_COLOR_XRGB16_1555: 929 m = 0xf; break; 930 default: 931 BUG(); return; 932 } 933 } else { 934 switch (color_mode) { 935 case OMAP_DSS_COLOR_CLUT1: 936 m = 0x0; break; 937 case OMAP_DSS_COLOR_CLUT2: 938 m = 0x1; break; 939 case OMAP_DSS_COLOR_CLUT4: 940 m = 0x2; break; 941 case OMAP_DSS_COLOR_CLUT8: 942 m = 0x3; break; 943 case OMAP_DSS_COLOR_RGB12U: 944 m = 0x4; break; 945 case OMAP_DSS_COLOR_ARGB16: 946 m = 0x5; break; 947 case OMAP_DSS_COLOR_RGB16: 948 m = 0x6; break; 949 case OMAP_DSS_COLOR_ARGB16_1555: 950 m = 0x7; break; 951 case OMAP_DSS_COLOR_RGB24U: 952 m = 0x8; break; 953 case OMAP_DSS_COLOR_RGB24P: 954 m = 0x9; break; 955 case OMAP_DSS_COLOR_RGBX16: 956 m = 0xa; break; 957 case OMAP_DSS_COLOR_RGBA16: 958 m = 0xb; break; 959 case OMAP_DSS_COLOR_ARGB32: 960 m = 0xc; break; 961 case OMAP_DSS_COLOR_RGBA32: 962 m = 0xd; break; 963 case OMAP_DSS_COLOR_RGBX32: 964 m = 0xe; break; 965 case OMAP_DSS_COLOR_XRGB16_1555: 966 m = 0xf; break; 967 default: 968 BUG(); return; 969 } 970 } 971 972 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1); 973 } 974 975 static void dispc_ovl_configure_burst_type(enum omap_plane plane, 976 enum omap_dss_rotation_type rotation_type) 977 { 978 if (dss_has_feature(FEAT_BURST_2D) == 0) 979 return; 980 981 if (rotation_type == OMAP_DSS_ROT_TILER) 982 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 1, 29, 29); 983 else 984 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 0, 29, 29); 985 } 986 987 void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel) 988 { 989 int shift; 990 u32 val; 991 int chan = 0, chan2 = 0; 992 993 switch (plane) { 994 case OMAP_DSS_GFX: 995 shift = 8; 996 break; 997 case OMAP_DSS_VIDEO1: 998 case OMAP_DSS_VIDEO2: 999 case OMAP_DSS_VIDEO3: 1000 shift = 16; 1001 break; 1002 default: 1003 BUG(); 1004 return; 1005 } 1006 1007 val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); 1008 if (dss_has_feature(FEAT_MGR_LCD2)) { 1009 switch (channel) { 1010 case OMAP_DSS_CHANNEL_LCD: 1011 chan = 0; 1012 chan2 = 0; 1013 break; 1014 case OMAP_DSS_CHANNEL_DIGIT: 1015 chan = 1; 1016 chan2 = 0; 1017 break; 1018 case OMAP_DSS_CHANNEL_LCD2: 1019 chan = 0; 1020 chan2 = 1; 1021 break; 1022 case OMAP_DSS_CHANNEL_LCD3: 1023 if (dss_has_feature(FEAT_MGR_LCD3)) { 1024 chan = 0; 1025 chan2 = 2; 1026 } else { 1027 BUG(); 1028 return; 1029 } 1030 break; 1031 case OMAP_DSS_CHANNEL_WB: 1032 chan = 0; 1033 chan2 = 3; 1034 break; 1035 default: 1036 BUG(); 1037 return; 1038 } 1039 1040 val = FLD_MOD(val, chan, shift, shift); 1041 val = FLD_MOD(val, chan2, 31, 30); 1042 } else { 1043 val = FLD_MOD(val, channel, shift, shift); 1044 } 1045 dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val); 1046 } 1047 EXPORT_SYMBOL(dispc_ovl_set_channel_out); 1048 1049 static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane) 1050 { 1051 int shift; 1052 u32 val; 1053 1054 switch (plane) { 1055 case OMAP_DSS_GFX: 1056 shift = 8; 1057 break; 1058 case OMAP_DSS_VIDEO1: 1059 case OMAP_DSS_VIDEO2: 1060 case OMAP_DSS_VIDEO3: 1061 shift = 16; 1062 break; 1063 default: 1064 BUG(); 1065 return 0; 1066 } 1067 1068 val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); 1069 1070 if (FLD_GET(val, shift, shift) == 1) 1071 return OMAP_DSS_CHANNEL_DIGIT; 1072 1073 if (!dss_has_feature(FEAT_MGR_LCD2)) 1074 return OMAP_DSS_CHANNEL_LCD; 1075 1076 switch (FLD_GET(val, 31, 30)) { 1077 case 0: 1078 default: 1079 return OMAP_DSS_CHANNEL_LCD; 1080 case 1: 1081 return OMAP_DSS_CHANNEL_LCD2; 1082 case 2: 1083 return OMAP_DSS_CHANNEL_LCD3; 1084 case 3: 1085 return OMAP_DSS_CHANNEL_WB; 1086 } 1087 } 1088 1089 void dispc_wb_set_channel_in(enum dss_writeback_channel channel) 1090 { 1091 enum omap_plane plane = OMAP_DSS_WB; 1092 1093 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), channel, 18, 16); 1094 } 1095 1096 static void dispc_ovl_set_burst_size(enum omap_plane plane, 1097 enum omap_burst_size burst_size) 1098 { 1099 static const unsigned shifts[] = { 6, 14, 14, 14, 14, }; 1100 int shift; 1101 1102 shift = shifts[plane]; 1103 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), burst_size, shift + 1, shift); 1104 } 1105 1106 static void dispc_configure_burst_sizes(void) 1107 { 1108 int i; 1109 const int burst_size = BURST_SIZE_X8; 1110 1111 /* Configure burst size always to maximum size */ 1112 for (i = 0; i < dss_feat_get_num_ovls(); ++i) 1113 dispc_ovl_set_burst_size(i, burst_size); 1114 if (dispc.feat->has_writeback) 1115 dispc_ovl_set_burst_size(OMAP_DSS_WB, burst_size); 1116 } 1117 1118 static u32 dispc_ovl_get_burst_size(enum omap_plane plane) 1119 { 1120 unsigned unit = dss_feat_get_burst_size_unit(); 1121 /* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */ 1122 return unit * 8; 1123 } 1124 1125 static void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable) 1126 { 1127 if (channel == OMAP_DSS_CHANNEL_DIGIT) 1128 return; 1129 1130 mgr_fld_write(channel, DISPC_MGR_FLD_CPR, enable); 1131 } 1132 1133 static void dispc_mgr_set_cpr_coef(enum omap_channel channel, 1134 const struct omap_dss_cpr_coefs *coefs) 1135 { 1136 u32 coef_r, coef_g, coef_b; 1137 1138 if (!dss_mgr_is_lcd(channel)) 1139 return; 1140 1141 coef_r = FLD_VAL(coefs->rr, 31, 22) | FLD_VAL(coefs->rg, 20, 11) | 1142 FLD_VAL(coefs->rb, 9, 0); 1143 coef_g = FLD_VAL(coefs->gr, 31, 22) | FLD_VAL(coefs->gg, 20, 11) | 1144 FLD_VAL(coefs->gb, 9, 0); 1145 coef_b = FLD_VAL(coefs->br, 31, 22) | FLD_VAL(coefs->bg, 20, 11) | 1146 FLD_VAL(coefs->bb, 9, 0); 1147 1148 dispc_write_reg(DISPC_CPR_COEF_R(channel), coef_r); 1149 dispc_write_reg(DISPC_CPR_COEF_G(channel), coef_g); 1150 dispc_write_reg(DISPC_CPR_COEF_B(channel), coef_b); 1151 } 1152 1153 static void dispc_ovl_set_vid_color_conv(enum omap_plane plane, bool enable) 1154 { 1155 u32 val; 1156 1157 BUG_ON(plane == OMAP_DSS_GFX); 1158 1159 val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); 1160 val = FLD_MOD(val, enable, 9, 9); 1161 dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val); 1162 } 1163 1164 static void dispc_ovl_enable_replication(enum omap_plane plane, 1165 enum omap_overlay_caps caps, bool enable) 1166 { 1167 static const unsigned shifts[] = { 5, 10, 10, 10 }; 1168 int shift; 1169 1170 if ((caps & OMAP_DSS_OVL_CAP_REPLICATION) == 0) 1171 return; 1172 1173 shift = shifts[plane]; 1174 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, shift, shift); 1175 } 1176 1177 static void dispc_mgr_set_size(enum omap_channel channel, u16 width, 1178 u16 height) 1179 { 1180 u32 val; 1181 1182 val = FLD_VAL(height - 1, dispc.feat->mgr_height_start, 16) | 1183 FLD_VAL(width - 1, dispc.feat->mgr_width_start, 0); 1184 1185 dispc_write_reg(DISPC_SIZE_MGR(channel), val); 1186 } 1187 1188 static void dispc_init_fifos(void) 1189 { 1190 u32 size; 1191 int fifo; 1192 u8 start, end; 1193 u32 unit; 1194 int i; 1195 1196 unit = dss_feat_get_buffer_size_unit(); 1197 1198 dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end); 1199 1200 for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) { 1201 size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(fifo), start, end); 1202 size *= unit; 1203 dispc.fifo_size[fifo] = size; 1204 1205 /* 1206 * By default fifos are mapped directly to overlays, fifo 0 to 1207 * ovl 0, fifo 1 to ovl 1, etc. 1208 */ 1209 dispc.fifo_assignment[fifo] = fifo; 1210 } 1211 1212 /* 1213 * The GFX fifo on OMAP4 is smaller than the other fifos. The small fifo 1214 * causes problems with certain use cases, like using the tiler in 2D 1215 * mode. The below hack swaps the fifos of GFX and WB planes, thus 1216 * giving GFX plane a larger fifo. WB but should work fine with a 1217 * smaller fifo. 1218 */ 1219 if (dispc.feat->gfx_fifo_workaround) { 1220 u32 v; 1221 1222 v = dispc_read_reg(DISPC_GLOBAL_BUFFER); 1223 1224 v = FLD_MOD(v, 4, 2, 0); /* GFX BUF top to WB */ 1225 v = FLD_MOD(v, 4, 5, 3); /* GFX BUF bottom to WB */ 1226 v = FLD_MOD(v, 0, 26, 24); /* WB BUF top to GFX */ 1227 v = FLD_MOD(v, 0, 29, 27); /* WB BUF bottom to GFX */ 1228 1229 dispc_write_reg(DISPC_GLOBAL_BUFFER, v); 1230 1231 dispc.fifo_assignment[OMAP_DSS_GFX] = OMAP_DSS_WB; 1232 dispc.fifo_assignment[OMAP_DSS_WB] = OMAP_DSS_GFX; 1233 } 1234 1235 /* 1236 * Setup default fifo thresholds. 1237 */ 1238 for (i = 0; i < dss_feat_get_num_ovls(); ++i) { 1239 u32 low, high; 1240 const bool use_fifomerge = false; 1241 const bool manual_update = false; 1242 1243 dispc_ovl_compute_fifo_thresholds(i, &low, &high, 1244 use_fifomerge, manual_update); 1245 1246 dispc_ovl_set_fifo_threshold(i, low, high); 1247 } 1248 1249 if (dispc.feat->has_writeback) { 1250 u32 low, high; 1251 const bool use_fifomerge = false; 1252 const bool manual_update = false; 1253 1254 dispc_ovl_compute_fifo_thresholds(OMAP_DSS_WB, &low, &high, 1255 use_fifomerge, manual_update); 1256 1257 dispc_ovl_set_fifo_threshold(OMAP_DSS_WB, low, high); 1258 } 1259 } 1260 1261 static u32 dispc_ovl_get_fifo_size(enum omap_plane plane) 1262 { 1263 int fifo; 1264 u32 size = 0; 1265 1266 for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) { 1267 if (dispc.fifo_assignment[fifo] == plane) 1268 size += dispc.fifo_size[fifo]; 1269 } 1270 1271 return size; 1272 } 1273 1274 void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high) 1275 { 1276 u8 hi_start, hi_end, lo_start, lo_end; 1277 u32 unit; 1278 1279 unit = dss_feat_get_buffer_size_unit(); 1280 1281 WARN_ON(low % unit != 0); 1282 WARN_ON(high % unit != 0); 1283 1284 low /= unit; 1285 high /= unit; 1286 1287 dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end); 1288 dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end); 1289 1290 DSSDBG("fifo(%d) threshold (bytes), old %u/%u, new %u/%u\n", 1291 plane, 1292 REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), 1293 lo_start, lo_end) * unit, 1294 REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), 1295 hi_start, hi_end) * unit, 1296 low * unit, high * unit); 1297 1298 dispc_write_reg(DISPC_OVL_FIFO_THRESHOLD(plane), 1299 FLD_VAL(high, hi_start, hi_end) | 1300 FLD_VAL(low, lo_start, lo_end)); 1301 1302 /* 1303 * configure the preload to the pipeline's high threhold, if HT it's too 1304 * large for the preload field, set the threshold to the maximum value 1305 * that can be held by the preload register 1306 */ 1307 if (dss_has_feature(FEAT_PRELOAD) && dispc.feat->set_max_preload && 1308 plane != OMAP_DSS_WB) 1309 dispc_write_reg(DISPC_OVL_PRELOAD(plane), min(high, 0xfffu)); 1310 } 1311 1312 void dispc_enable_fifomerge(bool enable) 1313 { 1314 if (!dss_has_feature(FEAT_FIFO_MERGE)) { 1315 WARN_ON(enable); 1316 return; 1317 } 1318 1319 DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled"); 1320 REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14); 1321 } 1322 1323 void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, 1324 u32 *fifo_low, u32 *fifo_high, bool use_fifomerge, 1325 bool manual_update) 1326 { 1327 /* 1328 * All sizes are in bytes. Both the buffer and burst are made of 1329 * buffer_units, and the fifo thresholds must be buffer_unit aligned. 1330 */ 1331 1332 unsigned buf_unit = dss_feat_get_buffer_size_unit(); 1333 unsigned ovl_fifo_size, total_fifo_size, burst_size; 1334 int i; 1335 1336 burst_size = dispc_ovl_get_burst_size(plane); 1337 ovl_fifo_size = dispc_ovl_get_fifo_size(plane); 1338 1339 if (use_fifomerge) { 1340 total_fifo_size = 0; 1341 for (i = 0; i < dss_feat_get_num_ovls(); ++i) 1342 total_fifo_size += dispc_ovl_get_fifo_size(i); 1343 } else { 1344 total_fifo_size = ovl_fifo_size; 1345 } 1346 1347 /* 1348 * We use the same low threshold for both fifomerge and non-fifomerge 1349 * cases, but for fifomerge we calculate the high threshold using the 1350 * combined fifo size 1351 */ 1352 1353 if (manual_update && dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) { 1354 *fifo_low = ovl_fifo_size - burst_size * 2; 1355 *fifo_high = total_fifo_size - burst_size; 1356 } else if (plane == OMAP_DSS_WB) { 1357 /* 1358 * Most optimal configuration for writeback is to push out data 1359 * to the interconnect the moment writeback pushes enough pixels 1360 * in the FIFO to form a burst 1361 */ 1362 *fifo_low = 0; 1363 *fifo_high = burst_size; 1364 } else { 1365 *fifo_low = ovl_fifo_size - burst_size; 1366 *fifo_high = total_fifo_size - buf_unit; 1367 } 1368 } 1369 1370 static void dispc_ovl_set_mflag(enum omap_plane plane, bool enable) 1371 { 1372 int bit; 1373 1374 if (plane == OMAP_DSS_GFX) 1375 bit = 14; 1376 else 1377 bit = 23; 1378 1379 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, bit, bit); 1380 } 1381 1382 static void dispc_ovl_set_mflag_threshold(enum omap_plane plane, 1383 int low, int high) 1384 { 1385 dispc_write_reg(DISPC_OVL_MFLAG_THRESHOLD(plane), 1386 FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0)); 1387 } 1388 1389 static void dispc_init_mflag(void) 1390 { 1391 int i; 1392 1393 /* 1394 * HACK: NV12 color format and MFLAG seem to have problems working 1395 * together: using two displays, and having an NV12 overlay on one of 1396 * the displays will cause underflows/synclosts when MFLAG_CTRL=2. 1397 * Changing MFLAG thresholds and PRELOAD to certain values seem to 1398 * remove the errors, but there doesn't seem to be a clear logic on 1399 * which values work and which not. 1400 * 1401 * As a work-around, set force MFLAG to always on. 1402 */ 1403 dispc_write_reg(DISPC_GLOBAL_MFLAG_ATTRIBUTE, 1404 (1 << 0) | /* MFLAG_CTRL = force always on */ 1405 (0 << 2)); /* MFLAG_START = disable */ 1406 1407 for (i = 0; i < dss_feat_get_num_ovls(); ++i) { 1408 u32 size = dispc_ovl_get_fifo_size(i); 1409 u32 unit = dss_feat_get_buffer_size_unit(); 1410 u32 low, high; 1411 1412 dispc_ovl_set_mflag(i, true); 1413 1414 /* 1415 * Simulation team suggests below thesholds: 1416 * HT = fifosize * 5 / 8; 1417 * LT = fifosize * 4 / 8; 1418 */ 1419 1420 low = size * 4 / 8 / unit; 1421 high = size * 5 / 8 / unit; 1422 1423 dispc_ovl_set_mflag_threshold(i, low, high); 1424 } 1425 1426 if (dispc.feat->has_writeback) { 1427 u32 size = dispc_ovl_get_fifo_size(OMAP_DSS_WB); 1428 u32 unit = dss_feat_get_buffer_size_unit(); 1429 u32 low, high; 1430 1431 dispc_ovl_set_mflag(OMAP_DSS_WB, true); 1432 1433 /* 1434 * Simulation team suggests below thesholds: 1435 * HT = fifosize * 5 / 8; 1436 * LT = fifosize * 4 / 8; 1437 */ 1438 1439 low = size * 4 / 8 / unit; 1440 high = size * 5 / 8 / unit; 1441 1442 dispc_ovl_set_mflag_threshold(OMAP_DSS_WB, low, high); 1443 } 1444 } 1445 1446 static void dispc_ovl_set_fir(enum omap_plane plane, 1447 int hinc, int vinc, 1448 enum omap_color_component color_comp) 1449 { 1450 u32 val; 1451 1452 if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) { 1453 u8 hinc_start, hinc_end, vinc_start, vinc_end; 1454 1455 dss_feat_get_reg_field(FEAT_REG_FIRHINC, 1456 &hinc_start, &hinc_end); 1457 dss_feat_get_reg_field(FEAT_REG_FIRVINC, 1458 &vinc_start, &vinc_end); 1459 val = FLD_VAL(vinc, vinc_start, vinc_end) | 1460 FLD_VAL(hinc, hinc_start, hinc_end); 1461 1462 dispc_write_reg(DISPC_OVL_FIR(plane), val); 1463 } else { 1464 val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0); 1465 dispc_write_reg(DISPC_OVL_FIR2(plane), val); 1466 } 1467 } 1468 1469 static void dispc_ovl_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu) 1470 { 1471 u32 val; 1472 u8 hor_start, hor_end, vert_start, vert_end; 1473 1474 dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end); 1475 dss_feat_get_reg_field(FEAT_REG_VERTICALACCU, &vert_start, &vert_end); 1476 1477 val = FLD_VAL(vaccu, vert_start, vert_end) | 1478 FLD_VAL(haccu, hor_start, hor_end); 1479 1480 dispc_write_reg(DISPC_OVL_ACCU0(plane), val); 1481 } 1482 1483 static void dispc_ovl_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu) 1484 { 1485 u32 val; 1486 u8 hor_start, hor_end, vert_start, vert_end; 1487 1488 dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end); 1489 dss_feat_get_reg_field(FEAT_REG_VERTICALACCU, &vert_start, &vert_end); 1490 1491 val = FLD_VAL(vaccu, vert_start, vert_end) | 1492 FLD_VAL(haccu, hor_start, hor_end); 1493 1494 dispc_write_reg(DISPC_OVL_ACCU1(plane), val); 1495 } 1496 1497 static void dispc_ovl_set_vid_accu2_0(enum omap_plane plane, int haccu, 1498 int vaccu) 1499 { 1500 u32 val; 1501 1502 val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); 1503 dispc_write_reg(DISPC_OVL_ACCU2_0(plane), val); 1504 } 1505 1506 static void dispc_ovl_set_vid_accu2_1(enum omap_plane plane, int haccu, 1507 int vaccu) 1508 { 1509 u32 val; 1510 1511 val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); 1512 dispc_write_reg(DISPC_OVL_ACCU2_1(plane), val); 1513 } 1514 1515 static void dispc_ovl_set_scale_param(enum omap_plane plane, 1516 u16 orig_width, u16 orig_height, 1517 u16 out_width, u16 out_height, 1518 bool five_taps, u8 rotation, 1519 enum omap_color_component color_comp) 1520 { 1521 int fir_hinc, fir_vinc; 1522 1523 fir_hinc = 1024 * orig_width / out_width; 1524 fir_vinc = 1024 * orig_height / out_height; 1525 1526 dispc_ovl_set_scale_coef(plane, fir_hinc, fir_vinc, five_taps, 1527 color_comp); 1528 dispc_ovl_set_fir(plane, fir_hinc, fir_vinc, color_comp); 1529 } 1530 1531 static void dispc_ovl_set_accu_uv(enum omap_plane plane, 1532 u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, 1533 bool ilace, enum omap_color_mode color_mode, u8 rotation) 1534 { 1535 int h_accu2_0, h_accu2_1; 1536 int v_accu2_0, v_accu2_1; 1537 int chroma_hinc, chroma_vinc; 1538 int idx; 1539 1540 struct accu { 1541 s8 h0_m, h0_n; 1542 s8 h1_m, h1_n; 1543 s8 v0_m, v0_n; 1544 s8 v1_m, v1_n; 1545 }; 1546 1547 const struct accu *accu_table; 1548 const struct accu *accu_val; 1549 1550 static const struct accu accu_nv12[4] = { 1551 { 0, 1, 0, 1 , -1, 2, 0, 1 }, 1552 { 1, 2, -3, 4 , 0, 1, 0, 1 }, 1553 { -1, 1, 0, 1 , -1, 2, 0, 1 }, 1554 { -1, 2, -1, 2 , -1, 1, 0, 1 }, 1555 }; 1556 1557 static const struct accu accu_nv12_ilace[4] = { 1558 { 0, 1, 0, 1 , -3, 4, -1, 4 }, 1559 { -1, 4, -3, 4 , 0, 1, 0, 1 }, 1560 { -1, 1, 0, 1 , -1, 4, -3, 4 }, 1561 { -3, 4, -3, 4 , -1, 1, 0, 1 }, 1562 }; 1563 1564 static const struct accu accu_yuv[4] = { 1565 { 0, 1, 0, 1, 0, 1, 0, 1 }, 1566 { 0, 1, 0, 1, 0, 1, 0, 1 }, 1567 { -1, 1, 0, 1, 0, 1, 0, 1 }, 1568 { 0, 1, 0, 1, -1, 1, 0, 1 }, 1569 }; 1570 1571 switch (rotation) { 1572 case OMAP_DSS_ROT_0: 1573 idx = 0; 1574 break; 1575 case OMAP_DSS_ROT_90: 1576 idx = 1; 1577 break; 1578 case OMAP_DSS_ROT_180: 1579 idx = 2; 1580 break; 1581 case OMAP_DSS_ROT_270: 1582 idx = 3; 1583 break; 1584 default: 1585 BUG(); 1586 return; 1587 } 1588 1589 switch (color_mode) { 1590 case OMAP_DSS_COLOR_NV12: 1591 if (ilace) 1592 accu_table = accu_nv12_ilace; 1593 else 1594 accu_table = accu_nv12; 1595 break; 1596 case OMAP_DSS_COLOR_YUV2: 1597 case OMAP_DSS_COLOR_UYVY: 1598 accu_table = accu_yuv; 1599 break; 1600 default: 1601 BUG(); 1602 return; 1603 } 1604 1605 accu_val = &accu_table[idx]; 1606 1607 chroma_hinc = 1024 * orig_width / out_width; 1608 chroma_vinc = 1024 * orig_height / out_height; 1609 1610 h_accu2_0 = (accu_val->h0_m * chroma_hinc / accu_val->h0_n) % 1024; 1611 h_accu2_1 = (accu_val->h1_m * chroma_hinc / accu_val->h1_n) % 1024; 1612 v_accu2_0 = (accu_val->v0_m * chroma_vinc / accu_val->v0_n) % 1024; 1613 v_accu2_1 = (accu_val->v1_m * chroma_vinc / accu_val->v1_n) % 1024; 1614 1615 dispc_ovl_set_vid_accu2_0(plane, h_accu2_0, v_accu2_0); 1616 dispc_ovl_set_vid_accu2_1(plane, h_accu2_1, v_accu2_1); 1617 } 1618 1619 static void dispc_ovl_set_scaling_common(enum omap_plane plane, 1620 u16 orig_width, u16 orig_height, 1621 u16 out_width, u16 out_height, 1622 bool ilace, bool five_taps, 1623 bool fieldmode, enum omap_color_mode color_mode, 1624 u8 rotation) 1625 { 1626 int accu0 = 0; 1627 int accu1 = 0; 1628 u32 l; 1629 1630 dispc_ovl_set_scale_param(plane, orig_width, orig_height, 1631 out_width, out_height, five_taps, 1632 rotation, DISPC_COLOR_COMPONENT_RGB_Y); 1633 l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); 1634 1635 /* RESIZEENABLE and VERTICALTAPS */ 1636 l &= ~((0x3 << 5) | (0x1 << 21)); 1637 l |= (orig_width != out_width) ? (1 << 5) : 0; 1638 l |= (orig_height != out_height) ? (1 << 6) : 0; 1639 l |= five_taps ? (1 << 21) : 0; 1640 1641 /* VRESIZECONF and HRESIZECONF */ 1642 if (dss_has_feature(FEAT_RESIZECONF)) { 1643 l &= ~(0x3 << 7); 1644 l |= (orig_width <= out_width) ? 0 : (1 << 7); 1645 l |= (orig_height <= out_height) ? 0 : (1 << 8); 1646 } 1647 1648 /* LINEBUFFERSPLIT */ 1649 if (dss_has_feature(FEAT_LINEBUFFERSPLIT)) { 1650 l &= ~(0x1 << 22); 1651 l |= five_taps ? (1 << 22) : 0; 1652 } 1653 1654 dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), l); 1655 1656 /* 1657 * field 0 = even field = bottom field 1658 * field 1 = odd field = top field 1659 */ 1660 if (ilace && !fieldmode) { 1661 accu1 = 0; 1662 accu0 = ((1024 * orig_height / out_height) / 2) & 0x3ff; 1663 if (accu0 >= 1024/2) { 1664 accu1 = 1024/2; 1665 accu0 -= accu1; 1666 } 1667 } 1668 1669 dispc_ovl_set_vid_accu0(plane, 0, accu0); 1670 dispc_ovl_set_vid_accu1(plane, 0, accu1); 1671 } 1672 1673 static void dispc_ovl_set_scaling_uv(enum omap_plane plane, 1674 u16 orig_width, u16 orig_height, 1675 u16 out_width, u16 out_height, 1676 bool ilace, bool five_taps, 1677 bool fieldmode, enum omap_color_mode color_mode, 1678 u8 rotation) 1679 { 1680 int scale_x = out_width != orig_width; 1681 int scale_y = out_height != orig_height; 1682 bool chroma_upscale = plane != OMAP_DSS_WB; 1683 1684 if (!dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) 1685 return; 1686 if ((color_mode != OMAP_DSS_COLOR_YUV2 && 1687 color_mode != OMAP_DSS_COLOR_UYVY && 1688 color_mode != OMAP_DSS_COLOR_NV12)) { 1689 /* reset chroma resampling for RGB formats */ 1690 if (plane != OMAP_DSS_WB) 1691 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8); 1692 return; 1693 } 1694 1695 dispc_ovl_set_accu_uv(plane, orig_width, orig_height, out_width, 1696 out_height, ilace, color_mode, rotation); 1697 1698 switch (color_mode) { 1699 case OMAP_DSS_COLOR_NV12: 1700 if (chroma_upscale) { 1701 /* UV is subsampled by 2 horizontally and vertically */ 1702 orig_height >>= 1; 1703 orig_width >>= 1; 1704 } else { 1705 /* UV is downsampled by 2 horizontally and vertically */ 1706 orig_height <<= 1; 1707 orig_width <<= 1; 1708 } 1709 1710 break; 1711 case OMAP_DSS_COLOR_YUV2: 1712 case OMAP_DSS_COLOR_UYVY: 1713 /* For YUV422 with 90/270 rotation, we don't upsample chroma */ 1714 if (rotation == OMAP_DSS_ROT_0 || 1715 rotation == OMAP_DSS_ROT_180) { 1716 if (chroma_upscale) 1717 /* UV is subsampled by 2 horizontally */ 1718 orig_width >>= 1; 1719 else 1720 /* UV is downsampled by 2 horizontally */ 1721 orig_width <<= 1; 1722 } 1723 1724 /* must use FIR for YUV422 if rotated */ 1725 if (rotation != OMAP_DSS_ROT_0) 1726 scale_x = scale_y = true; 1727 1728 break; 1729 default: 1730 BUG(); 1731 return; 1732 } 1733 1734 if (out_width != orig_width) 1735 scale_x = true; 1736 if (out_height != orig_height) 1737 scale_y = true; 1738 1739 dispc_ovl_set_scale_param(plane, orig_width, orig_height, 1740 out_width, out_height, five_taps, 1741 rotation, DISPC_COLOR_COMPONENT_UV); 1742 1743 if (plane != OMAP_DSS_WB) 1744 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 1745 (scale_x || scale_y) ? 1 : 0, 8, 8); 1746 1747 /* set H scaling */ 1748 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_x ? 1 : 0, 5, 5); 1749 /* set V scaling */ 1750 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_y ? 1 : 0, 6, 6); 1751 } 1752 1753 static void dispc_ovl_set_scaling(enum omap_plane plane, 1754 u16 orig_width, u16 orig_height, 1755 u16 out_width, u16 out_height, 1756 bool ilace, bool five_taps, 1757 bool fieldmode, enum omap_color_mode color_mode, 1758 u8 rotation) 1759 { 1760 BUG_ON(plane == OMAP_DSS_GFX); 1761 1762 dispc_ovl_set_scaling_common(plane, 1763 orig_width, orig_height, 1764 out_width, out_height, 1765 ilace, five_taps, 1766 fieldmode, color_mode, 1767 rotation); 1768 1769 dispc_ovl_set_scaling_uv(plane, 1770 orig_width, orig_height, 1771 out_width, out_height, 1772 ilace, five_taps, 1773 fieldmode, color_mode, 1774 rotation); 1775 } 1776 1777 static void dispc_ovl_set_rotation_attrs(enum omap_plane plane, u8 rotation, 1778 enum omap_dss_rotation_type rotation_type, 1779 bool mirroring, enum omap_color_mode color_mode) 1780 { 1781 bool row_repeat = false; 1782 int vidrot = 0; 1783 1784 if (color_mode == OMAP_DSS_COLOR_YUV2 || 1785 color_mode == OMAP_DSS_COLOR_UYVY) { 1786 1787 if (mirroring) { 1788 switch (rotation) { 1789 case OMAP_DSS_ROT_0: 1790 vidrot = 2; 1791 break; 1792 case OMAP_DSS_ROT_90: 1793 vidrot = 1; 1794 break; 1795 case OMAP_DSS_ROT_180: 1796 vidrot = 0; 1797 break; 1798 case OMAP_DSS_ROT_270: 1799 vidrot = 3; 1800 break; 1801 } 1802 } else { 1803 switch (rotation) { 1804 case OMAP_DSS_ROT_0: 1805 vidrot = 0; 1806 break; 1807 case OMAP_DSS_ROT_90: 1808 vidrot = 1; 1809 break; 1810 case OMAP_DSS_ROT_180: 1811 vidrot = 2; 1812 break; 1813 case OMAP_DSS_ROT_270: 1814 vidrot = 3; 1815 break; 1816 } 1817 } 1818 1819 if (rotation == OMAP_DSS_ROT_90 || rotation == OMAP_DSS_ROT_270) 1820 row_repeat = true; 1821 else 1822 row_repeat = false; 1823 } 1824 1825 /* 1826 * OMAP4/5 Errata i631: 1827 * NV12 in 1D mode must use ROTATION=1. Otherwise DSS will fetch extra 1828 * rows beyond the framebuffer, which may cause OCP error. 1829 */ 1830 if (color_mode == OMAP_DSS_COLOR_NV12 && 1831 rotation_type != OMAP_DSS_ROT_TILER) 1832 vidrot = 1; 1833 1834 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), vidrot, 13, 12); 1835 if (dss_has_feature(FEAT_ROWREPEATENABLE)) 1836 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 1837 row_repeat ? 1 : 0, 18, 18); 1838 1839 if (color_mode == OMAP_DSS_COLOR_NV12) { 1840 bool doublestride = (rotation_type == OMAP_DSS_ROT_TILER) && 1841 (rotation == OMAP_DSS_ROT_0 || 1842 rotation == OMAP_DSS_ROT_180); 1843 /* DOUBLESTRIDE */ 1844 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), doublestride, 22, 22); 1845 } 1846 1847 } 1848 1849 static int color_mode_to_bpp(enum omap_color_mode color_mode) 1850 { 1851 switch (color_mode) { 1852 case OMAP_DSS_COLOR_CLUT1: 1853 return 1; 1854 case OMAP_DSS_COLOR_CLUT2: 1855 return 2; 1856 case OMAP_DSS_COLOR_CLUT4: 1857 return 4; 1858 case OMAP_DSS_COLOR_CLUT8: 1859 case OMAP_DSS_COLOR_NV12: 1860 return 8; 1861 case OMAP_DSS_COLOR_RGB12U: 1862 case OMAP_DSS_COLOR_RGB16: 1863 case OMAP_DSS_COLOR_ARGB16: 1864 case OMAP_DSS_COLOR_YUV2: 1865 case OMAP_DSS_COLOR_UYVY: 1866 case OMAP_DSS_COLOR_RGBA16: 1867 case OMAP_DSS_COLOR_RGBX16: 1868 case OMAP_DSS_COLOR_ARGB16_1555: 1869 case OMAP_DSS_COLOR_XRGB16_1555: 1870 return 16; 1871 case OMAP_DSS_COLOR_RGB24P: 1872 return 24; 1873 case OMAP_DSS_COLOR_RGB24U: 1874 case OMAP_DSS_COLOR_ARGB32: 1875 case OMAP_DSS_COLOR_RGBA32: 1876 case OMAP_DSS_COLOR_RGBX32: 1877 return 32; 1878 default: 1879 BUG(); 1880 return 0; 1881 } 1882 } 1883 1884 static s32 pixinc(int pixels, u8 ps) 1885 { 1886 if (pixels == 1) 1887 return 1; 1888 else if (pixels > 1) 1889 return 1 + (pixels - 1) * ps; 1890 else if (pixels < 0) 1891 return 1 - (-pixels + 1) * ps; 1892 else 1893 BUG(); 1894 return 0; 1895 } 1896 1897 static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, 1898 u16 screen_width, 1899 u16 width, u16 height, 1900 enum omap_color_mode color_mode, bool fieldmode, 1901 unsigned int field_offset, 1902 unsigned *offset0, unsigned *offset1, 1903 s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) 1904 { 1905 u8 ps; 1906 1907 /* FIXME CLUT formats */ 1908 switch (color_mode) { 1909 case OMAP_DSS_COLOR_CLUT1: 1910 case OMAP_DSS_COLOR_CLUT2: 1911 case OMAP_DSS_COLOR_CLUT4: 1912 case OMAP_DSS_COLOR_CLUT8: 1913 BUG(); 1914 return; 1915 case OMAP_DSS_COLOR_YUV2: 1916 case OMAP_DSS_COLOR_UYVY: 1917 ps = 4; 1918 break; 1919 default: 1920 ps = color_mode_to_bpp(color_mode) / 8; 1921 break; 1922 } 1923 1924 DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width, 1925 width, height); 1926 1927 /* 1928 * field 0 = even field = bottom field 1929 * field 1 = odd field = top field 1930 */ 1931 switch (rotation + mirror * 4) { 1932 case OMAP_DSS_ROT_0: 1933 case OMAP_DSS_ROT_180: 1934 /* 1935 * If the pixel format is YUV or UYVY divide the width 1936 * of the image by 2 for 0 and 180 degree rotation. 1937 */ 1938 if (color_mode == OMAP_DSS_COLOR_YUV2 || 1939 color_mode == OMAP_DSS_COLOR_UYVY) 1940 width = width >> 1; 1941 case OMAP_DSS_ROT_90: 1942 case OMAP_DSS_ROT_270: 1943 *offset1 = 0; 1944 if (field_offset) 1945 *offset0 = field_offset * screen_width * ps; 1946 else 1947 *offset0 = 0; 1948 1949 *row_inc = pixinc(1 + 1950 (y_predecim * screen_width - x_predecim * width) + 1951 (fieldmode ? screen_width : 0), ps); 1952 *pix_inc = pixinc(x_predecim, ps); 1953 break; 1954 1955 case OMAP_DSS_ROT_0 + 4: 1956 case OMAP_DSS_ROT_180 + 4: 1957 /* If the pixel format is YUV or UYVY divide the width 1958 * of the image by 2 for 0 degree and 180 degree 1959 */ 1960 if (color_mode == OMAP_DSS_COLOR_YUV2 || 1961 color_mode == OMAP_DSS_COLOR_UYVY) 1962 width = width >> 1; 1963 case OMAP_DSS_ROT_90 + 4: 1964 case OMAP_DSS_ROT_270 + 4: 1965 *offset1 = 0; 1966 if (field_offset) 1967 *offset0 = field_offset * screen_width * ps; 1968 else 1969 *offset0 = 0; 1970 *row_inc = pixinc(1 - 1971 (y_predecim * screen_width + x_predecim * width) - 1972 (fieldmode ? screen_width : 0), ps); 1973 *pix_inc = pixinc(x_predecim, ps); 1974 break; 1975 1976 default: 1977 BUG(); 1978 return; 1979 } 1980 } 1981 1982 static void calc_dma_rotation_offset(u8 rotation, bool mirror, 1983 u16 screen_width, 1984 u16 width, u16 height, 1985 enum omap_color_mode color_mode, bool fieldmode, 1986 unsigned int field_offset, 1987 unsigned *offset0, unsigned *offset1, 1988 s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) 1989 { 1990 u8 ps; 1991 u16 fbw, fbh; 1992 1993 /* FIXME CLUT formats */ 1994 switch (color_mode) { 1995 case OMAP_DSS_COLOR_CLUT1: 1996 case OMAP_DSS_COLOR_CLUT2: 1997 case OMAP_DSS_COLOR_CLUT4: 1998 case OMAP_DSS_COLOR_CLUT8: 1999 BUG(); 2000 return; 2001 default: 2002 ps = color_mode_to_bpp(color_mode) / 8; 2003 break; 2004 } 2005 2006 DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width, 2007 width, height); 2008 2009 /* width & height are overlay sizes, convert to fb sizes */ 2010 2011 if (rotation == OMAP_DSS_ROT_0 || rotation == OMAP_DSS_ROT_180) { 2012 fbw = width; 2013 fbh = height; 2014 } else { 2015 fbw = height; 2016 fbh = width; 2017 } 2018 2019 /* 2020 * field 0 = even field = bottom field 2021 * field 1 = odd field = top field 2022 */ 2023 switch (rotation + mirror * 4) { 2024 case OMAP_DSS_ROT_0: 2025 *offset1 = 0; 2026 if (field_offset) 2027 *offset0 = *offset1 + field_offset * screen_width * ps; 2028 else 2029 *offset0 = *offset1; 2030 *row_inc = pixinc(1 + 2031 (y_predecim * screen_width - fbw * x_predecim) + 2032 (fieldmode ? screen_width : 0), ps); 2033 if (color_mode == OMAP_DSS_COLOR_YUV2 || 2034 color_mode == OMAP_DSS_COLOR_UYVY) 2035 *pix_inc = pixinc(x_predecim, 2 * ps); 2036 else 2037 *pix_inc = pixinc(x_predecim, ps); 2038 break; 2039 case OMAP_DSS_ROT_90: 2040 *offset1 = screen_width * (fbh - 1) * ps; 2041 if (field_offset) 2042 *offset0 = *offset1 + field_offset * ps; 2043 else 2044 *offset0 = *offset1; 2045 *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) + 2046 y_predecim + (fieldmode ? 1 : 0), ps); 2047 *pix_inc = pixinc(-x_predecim * screen_width, ps); 2048 break; 2049 case OMAP_DSS_ROT_180: 2050 *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; 2051 if (field_offset) 2052 *offset0 = *offset1 - field_offset * screen_width * ps; 2053 else 2054 *offset0 = *offset1; 2055 *row_inc = pixinc(-1 - 2056 (y_predecim * screen_width - fbw * x_predecim) - 2057 (fieldmode ? screen_width : 0), ps); 2058 if (color_mode == OMAP_DSS_COLOR_YUV2 || 2059 color_mode == OMAP_DSS_COLOR_UYVY) 2060 *pix_inc = pixinc(-x_predecim, 2 * ps); 2061 else 2062 *pix_inc = pixinc(-x_predecim, ps); 2063 break; 2064 case OMAP_DSS_ROT_270: 2065 *offset1 = (fbw - 1) * ps; 2066 if (field_offset) 2067 *offset0 = *offset1 - field_offset * ps; 2068 else 2069 *offset0 = *offset1; 2070 *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) - 2071 y_predecim - (fieldmode ? 1 : 0), ps); 2072 *pix_inc = pixinc(x_predecim * screen_width, ps); 2073 break; 2074 2075 /* mirroring */ 2076 case OMAP_DSS_ROT_0 + 4: 2077 *offset1 = (fbw - 1) * ps; 2078 if (field_offset) 2079 *offset0 = *offset1 + field_offset * screen_width * ps; 2080 else 2081 *offset0 = *offset1; 2082 *row_inc = pixinc(y_predecim * screen_width * 2 - 1 + 2083 (fieldmode ? screen_width : 0), 2084 ps); 2085 if (color_mode == OMAP_DSS_COLOR_YUV2 || 2086 color_mode == OMAP_DSS_COLOR_UYVY) 2087 *pix_inc = pixinc(-x_predecim, 2 * ps); 2088 else 2089 *pix_inc = pixinc(-x_predecim, ps); 2090 break; 2091 2092 case OMAP_DSS_ROT_90 + 4: 2093 *offset1 = 0; 2094 if (field_offset) 2095 *offset0 = *offset1 + field_offset * ps; 2096 else 2097 *offset0 = *offset1; 2098 *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) + 2099 y_predecim + (fieldmode ? 1 : 0), 2100 ps); 2101 *pix_inc = pixinc(x_predecim * screen_width, ps); 2102 break; 2103 2104 case OMAP_DSS_ROT_180 + 4: 2105 *offset1 = screen_width * (fbh - 1) * ps; 2106 if (field_offset) 2107 *offset0 = *offset1 - field_offset * screen_width * ps; 2108 else 2109 *offset0 = *offset1; 2110 *row_inc = pixinc(1 - y_predecim * screen_width * 2 - 2111 (fieldmode ? screen_width : 0), 2112 ps); 2113 if (color_mode == OMAP_DSS_COLOR_YUV2 || 2114 color_mode == OMAP_DSS_COLOR_UYVY) 2115 *pix_inc = pixinc(x_predecim, 2 * ps); 2116 else 2117 *pix_inc = pixinc(x_predecim, ps); 2118 break; 2119 2120 case OMAP_DSS_ROT_270 + 4: 2121 *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; 2122 if (field_offset) 2123 *offset0 = *offset1 - field_offset * ps; 2124 else 2125 *offset0 = *offset1; 2126 *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) - 2127 y_predecim - (fieldmode ? 1 : 0), 2128 ps); 2129 *pix_inc = pixinc(-x_predecim * screen_width, ps); 2130 break; 2131 2132 default: 2133 BUG(); 2134 return; 2135 } 2136 } 2137 2138 static void calc_tiler_rotation_offset(u16 screen_width, u16 width, 2139 enum omap_color_mode color_mode, bool fieldmode, 2140 unsigned int field_offset, unsigned *offset0, unsigned *offset1, 2141 s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) 2142 { 2143 u8 ps; 2144 2145 switch (color_mode) { 2146 case OMAP_DSS_COLOR_CLUT1: 2147 case OMAP_DSS_COLOR_CLUT2: 2148 case OMAP_DSS_COLOR_CLUT4: 2149 case OMAP_DSS_COLOR_CLUT8: 2150 BUG(); 2151 return; 2152 default: 2153 ps = color_mode_to_bpp(color_mode) / 8; 2154 break; 2155 } 2156 2157 DSSDBG("scrw %d, width %d\n", screen_width, width); 2158 2159 /* 2160 * field 0 = even field = bottom field 2161 * field 1 = odd field = top field 2162 */ 2163 *offset1 = 0; 2164 if (field_offset) 2165 *offset0 = *offset1 + field_offset * screen_width * ps; 2166 else 2167 *offset0 = *offset1; 2168 *row_inc = pixinc(1 + (y_predecim * screen_width - width * x_predecim) + 2169 (fieldmode ? screen_width : 0), ps); 2170 if (color_mode == OMAP_DSS_COLOR_YUV2 || 2171 color_mode == OMAP_DSS_COLOR_UYVY) 2172 *pix_inc = pixinc(x_predecim, 2 * ps); 2173 else 2174 *pix_inc = pixinc(x_predecim, ps); 2175 } 2176 2177 /* 2178 * This function is used to avoid synclosts in OMAP3, because of some 2179 * undocumented horizontal position and timing related limitations. 2180 */ 2181 static int check_horiz_timing_omap3(unsigned long pclk, unsigned long lclk, 2182 const struct videomode *vm, u16 pos_x, 2183 u16 width, u16 height, u16 out_width, u16 out_height, 2184 bool five_taps) 2185 { 2186 const int ds = DIV_ROUND_UP(height, out_height); 2187 unsigned long nonactive; 2188 static const u8 limits[3] = { 8, 10, 20 }; 2189 u64 val, blank; 2190 int i; 2191 2192 nonactive = vm->hactive + vm->hfront_porch + vm->hsync_len + 2193 vm->hback_porch - out_width; 2194 2195 i = 0; 2196 if (out_height < height) 2197 i++; 2198 if (out_width < width) 2199 i++; 2200 blank = div_u64((u64)(vm->hback_porch + vm->hsync_len + vm->hfront_porch) * 2201 lclk, pclk); 2202 DSSDBG("blanking period + ppl = %llu (limit = %u)\n", blank, limits[i]); 2203 if (blank <= limits[i]) 2204 return -EINVAL; 2205 2206 /* FIXME add checks for 3-tap filter once the limitations are known */ 2207 if (!five_taps) 2208 return 0; 2209 2210 /* 2211 * Pixel data should be prepared before visible display point starts. 2212 * So, atleast DS-2 lines must have already been fetched by DISPC 2213 * during nonactive - pos_x period. 2214 */ 2215 val = div_u64((u64)(nonactive - pos_x) * lclk, pclk); 2216 DSSDBG("(nonactive - pos_x) * pcd = %llu max(0, DS - 2) * width = %d\n", 2217 val, max(0, ds - 2) * width); 2218 if (val < max(0, ds - 2) * width) 2219 return -EINVAL; 2220 2221 /* 2222 * All lines need to be refilled during the nonactive period of which 2223 * only one line can be loaded during the active period. So, atleast 2224 * DS - 1 lines should be loaded during nonactive period. 2225 */ 2226 val = div_u64((u64)nonactive * lclk, pclk); 2227 DSSDBG("nonactive * pcd = %llu, max(0, DS - 1) * width = %d\n", 2228 val, max(0, ds - 1) * width); 2229 if (val < max(0, ds - 1) * width) 2230 return -EINVAL; 2231 2232 return 0; 2233 } 2234 2235 static unsigned long calc_core_clk_five_taps(unsigned long pclk, 2236 const struct videomode *vm, u16 width, 2237 u16 height, u16 out_width, u16 out_height, 2238 enum omap_color_mode color_mode) 2239 { 2240 u32 core_clk = 0; 2241 u64 tmp; 2242 2243 if (height <= out_height && width <= out_width) 2244 return (unsigned long) pclk; 2245 2246 if (height > out_height) { 2247 unsigned int ppl = vm->hactive; 2248 2249 tmp = (u64)pclk * height * out_width; 2250 do_div(tmp, 2 * out_height * ppl); 2251 core_clk = tmp; 2252 2253 if (height > 2 * out_height) { 2254 if (ppl == out_width) 2255 return 0; 2256 2257 tmp = (u64)pclk * (height - 2 * out_height) * out_width; 2258 do_div(tmp, 2 * out_height * (ppl - out_width)); 2259 core_clk = max_t(u32, core_clk, tmp); 2260 } 2261 } 2262 2263 if (width > out_width) { 2264 tmp = (u64)pclk * width; 2265 do_div(tmp, out_width); 2266 core_clk = max_t(u32, core_clk, tmp); 2267 2268 if (color_mode == OMAP_DSS_COLOR_RGB24U) 2269 core_clk <<= 1; 2270 } 2271 2272 return core_clk; 2273 } 2274 2275 static unsigned long calc_core_clk_24xx(unsigned long pclk, u16 width, 2276 u16 height, u16 out_width, u16 out_height, bool mem_to_mem) 2277 { 2278 if (height > out_height && width > out_width) 2279 return pclk * 4; 2280 else 2281 return pclk * 2; 2282 } 2283 2284 static unsigned long calc_core_clk_34xx(unsigned long pclk, u16 width, 2285 u16 height, u16 out_width, u16 out_height, bool mem_to_mem) 2286 { 2287 unsigned int hf, vf; 2288 2289 /* 2290 * FIXME how to determine the 'A' factor 2291 * for the no downscaling case ? 2292 */ 2293 2294 if (width > 3 * out_width) 2295 hf = 4; 2296 else if (width > 2 * out_width) 2297 hf = 3; 2298 else if (width > out_width) 2299 hf = 2; 2300 else 2301 hf = 1; 2302 if (height > out_height) 2303 vf = 2; 2304 else 2305 vf = 1; 2306 2307 return pclk * vf * hf; 2308 } 2309 2310 static unsigned long calc_core_clk_44xx(unsigned long pclk, u16 width, 2311 u16 height, u16 out_width, u16 out_height, bool mem_to_mem) 2312 { 2313 /* 2314 * If the overlay/writeback is in mem to mem mode, there are no 2315 * downscaling limitations with respect to pixel clock, return 1 as 2316 * required core clock to represent that we have sufficient enough 2317 * core clock to do maximum downscaling 2318 */ 2319 if (mem_to_mem) 2320 return 1; 2321 2322 if (width > out_width) 2323 return DIV_ROUND_UP(pclk, out_width) * width; 2324 else 2325 return pclk; 2326 } 2327 2328 static int dispc_ovl_calc_scaling_24xx(unsigned long pclk, unsigned long lclk, 2329 const struct videomode *vm, 2330 u16 width, u16 height, u16 out_width, u16 out_height, 2331 enum omap_color_mode color_mode, bool *five_taps, 2332 int *x_predecim, int *y_predecim, int *decim_x, int *decim_y, 2333 u16 pos_x, unsigned long *core_clk, bool mem_to_mem) 2334 { 2335 int error; 2336 u16 in_width, in_height; 2337 int min_factor = min(*decim_x, *decim_y); 2338 const int maxsinglelinewidth = 2339 dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH); 2340 2341 *five_taps = false; 2342 2343 do { 2344 in_height = height / *decim_y; 2345 in_width = width / *decim_x; 2346 *core_clk = dispc.feat->calc_core_clk(pclk, in_width, 2347 in_height, out_width, out_height, mem_to_mem); 2348 error = (in_width > maxsinglelinewidth || !*core_clk || 2349 *core_clk > dispc_core_clk_rate()); 2350 if (error) { 2351 if (*decim_x == *decim_y) { 2352 *decim_x = min_factor; 2353 ++*decim_y; 2354 } else { 2355 swap(*decim_x, *decim_y); 2356 if (*decim_x < *decim_y) 2357 ++*decim_x; 2358 } 2359 } 2360 } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error); 2361 2362 if (error) { 2363 DSSERR("failed to find scaling settings\n"); 2364 return -EINVAL; 2365 } 2366 2367 if (in_width > maxsinglelinewidth) { 2368 DSSERR("Cannot scale max input width exceeded"); 2369 return -EINVAL; 2370 } 2371 return 0; 2372 } 2373 2374 static int dispc_ovl_calc_scaling_34xx(unsigned long pclk, unsigned long lclk, 2375 const struct videomode *vm, 2376 u16 width, u16 height, u16 out_width, u16 out_height, 2377 enum omap_color_mode color_mode, bool *five_taps, 2378 int *x_predecim, int *y_predecim, int *decim_x, int *decim_y, 2379 u16 pos_x, unsigned long *core_clk, bool mem_to_mem) 2380 { 2381 int error; 2382 u16 in_width, in_height; 2383 const int maxsinglelinewidth = 2384 dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH); 2385 2386 do { 2387 in_height = height / *decim_y; 2388 in_width = width / *decim_x; 2389 *five_taps = in_height > out_height; 2390 2391 if (in_width > maxsinglelinewidth) 2392 if (in_height > out_height && 2393 in_height < out_height * 2) 2394 *five_taps = false; 2395 again: 2396 if (*five_taps) 2397 *core_clk = calc_core_clk_five_taps(pclk, vm, 2398 in_width, in_height, out_width, 2399 out_height, color_mode); 2400 else 2401 *core_clk = dispc.feat->calc_core_clk(pclk, in_width, 2402 in_height, out_width, out_height, 2403 mem_to_mem); 2404 2405 error = check_horiz_timing_omap3(pclk, lclk, vm, 2406 pos_x, in_width, in_height, out_width, 2407 out_height, *five_taps); 2408 if (error && *five_taps) { 2409 *five_taps = false; 2410 goto again; 2411 } 2412 2413 error = (error || in_width > maxsinglelinewidth * 2 || 2414 (in_width > maxsinglelinewidth && *five_taps) || 2415 !*core_clk || *core_clk > dispc_core_clk_rate()); 2416 2417 if (!error) { 2418 /* verify that we're inside the limits of scaler */ 2419 if (in_width / 4 > out_width) 2420 error = 1; 2421 2422 if (*five_taps) { 2423 if (in_height / 4 > out_height) 2424 error = 1; 2425 } else { 2426 if (in_height / 2 > out_height) 2427 error = 1; 2428 } 2429 } 2430 2431 if (error) 2432 ++*decim_y; 2433 } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error); 2434 2435 if (error) { 2436 DSSERR("failed to find scaling settings\n"); 2437 return -EINVAL; 2438 } 2439 2440 if (check_horiz_timing_omap3(pclk, lclk, vm, pos_x, in_width, 2441 in_height, out_width, out_height, *five_taps)) { 2442 DSSERR("horizontal timing too tight\n"); 2443 return -EINVAL; 2444 } 2445 2446 if (in_width > (maxsinglelinewidth * 2)) { 2447 DSSERR("Cannot setup scaling"); 2448 DSSERR("width exceeds maximum width possible"); 2449 return -EINVAL; 2450 } 2451 2452 if (in_width > maxsinglelinewidth && *five_taps) { 2453 DSSERR("cannot setup scaling with five taps"); 2454 return -EINVAL; 2455 } 2456 return 0; 2457 } 2458 2459 static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk, 2460 const struct videomode *vm, 2461 u16 width, u16 height, u16 out_width, u16 out_height, 2462 enum omap_color_mode color_mode, bool *five_taps, 2463 int *x_predecim, int *y_predecim, int *decim_x, int *decim_y, 2464 u16 pos_x, unsigned long *core_clk, bool mem_to_mem) 2465 { 2466 u16 in_width, in_width_max; 2467 int decim_x_min = *decim_x; 2468 u16 in_height = height / *decim_y; 2469 const int maxsinglelinewidth = 2470 dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH); 2471 const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE); 2472 2473 if (mem_to_mem) { 2474 in_width_max = out_width * maxdownscale; 2475 } else { 2476 in_width_max = dispc_core_clk_rate() / 2477 DIV_ROUND_UP(pclk, out_width); 2478 } 2479 2480 *decim_x = DIV_ROUND_UP(width, in_width_max); 2481 2482 *decim_x = *decim_x > decim_x_min ? *decim_x : decim_x_min; 2483 if (*decim_x > *x_predecim) 2484 return -EINVAL; 2485 2486 do { 2487 in_width = width / *decim_x; 2488 } while (*decim_x <= *x_predecim && 2489 in_width > maxsinglelinewidth && ++*decim_x); 2490 2491 if (in_width > maxsinglelinewidth) { 2492 DSSERR("Cannot scale width exceeds max line width"); 2493 return -EINVAL; 2494 } 2495 2496 *core_clk = dispc.feat->calc_core_clk(pclk, in_width, in_height, 2497 out_width, out_height, mem_to_mem); 2498 return 0; 2499 } 2500 2501 #define DIV_FRAC(dividend, divisor) \ 2502 ((dividend) * 100 / (divisor) - ((dividend) / (divisor) * 100)) 2503 2504 static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, 2505 enum omap_overlay_caps caps, 2506 const struct videomode *vm, 2507 u16 width, u16 height, u16 out_width, u16 out_height, 2508 enum omap_color_mode color_mode, bool *five_taps, 2509 int *x_predecim, int *y_predecim, u16 pos_x, 2510 enum omap_dss_rotation_type rotation_type, bool mem_to_mem) 2511 { 2512 const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE); 2513 const int max_decim_limit = 16; 2514 unsigned long core_clk = 0; 2515 int decim_x, decim_y, ret; 2516 2517 if (width == out_width && height == out_height) 2518 return 0; 2519 2520 if (!mem_to_mem && (pclk == 0 || vm->pixelclock == 0)) { 2521 DSSERR("cannot calculate scaling settings: pclk is zero\n"); 2522 return -EINVAL; 2523 } 2524 2525 if ((caps & OMAP_DSS_OVL_CAP_SCALE) == 0) 2526 return -EINVAL; 2527 2528 if (mem_to_mem) { 2529 *x_predecim = *y_predecim = 1; 2530 } else { 2531 *x_predecim = max_decim_limit; 2532 *y_predecim = (rotation_type == OMAP_DSS_ROT_TILER && 2533 dss_has_feature(FEAT_BURST_2D)) ? 2534 2 : max_decim_limit; 2535 } 2536 2537 if (color_mode == OMAP_DSS_COLOR_CLUT1 || 2538 color_mode == OMAP_DSS_COLOR_CLUT2 || 2539 color_mode == OMAP_DSS_COLOR_CLUT4 || 2540 color_mode == OMAP_DSS_COLOR_CLUT8) { 2541 *x_predecim = 1; 2542 *y_predecim = 1; 2543 *five_taps = false; 2544 return 0; 2545 } 2546 2547 decim_x = DIV_ROUND_UP(DIV_ROUND_UP(width, out_width), maxdownscale); 2548 decim_y = DIV_ROUND_UP(DIV_ROUND_UP(height, out_height), maxdownscale); 2549 2550 if (decim_x > *x_predecim || out_width > width * 8) 2551 return -EINVAL; 2552 2553 if (decim_y > *y_predecim || out_height > height * 8) 2554 return -EINVAL; 2555 2556 ret = dispc.feat->calc_scaling(pclk, lclk, vm, width, height, 2557 out_width, out_height, color_mode, five_taps, 2558 x_predecim, y_predecim, &decim_x, &decim_y, pos_x, &core_clk, 2559 mem_to_mem); 2560 if (ret) 2561 return ret; 2562 2563 DSSDBG("%dx%d -> %dx%d (%d.%02d x %d.%02d), decim %dx%d %dx%d (%d.%02d x %d.%02d), taps %d, req clk %lu, cur clk %lu\n", 2564 width, height, 2565 out_width, out_height, 2566 out_width / width, DIV_FRAC(out_width, width), 2567 out_height / height, DIV_FRAC(out_height, height), 2568 2569 decim_x, decim_y, 2570 width / decim_x, height / decim_y, 2571 out_width / (width / decim_x), DIV_FRAC(out_width, width / decim_x), 2572 out_height / (height / decim_y), DIV_FRAC(out_height, height / decim_y), 2573 2574 *five_taps ? 5 : 3, 2575 core_clk, dispc_core_clk_rate()); 2576 2577 if (!core_clk || core_clk > dispc_core_clk_rate()) { 2578 DSSERR("failed to set up scaling, " 2579 "required core clk rate = %lu Hz, " 2580 "current core clk rate = %lu Hz\n", 2581 core_clk, dispc_core_clk_rate()); 2582 return -EINVAL; 2583 } 2584 2585 *x_predecim = decim_x; 2586 *y_predecim = decim_y; 2587 return 0; 2588 } 2589 2590 static int dispc_ovl_setup_common(enum omap_plane plane, 2591 enum omap_overlay_caps caps, u32 paddr, u32 p_uv_addr, 2592 u16 screen_width, int pos_x, int pos_y, u16 width, u16 height, 2593 u16 out_width, u16 out_height, enum omap_color_mode color_mode, 2594 u8 rotation, bool mirror, u8 zorder, u8 pre_mult_alpha, 2595 u8 global_alpha, enum omap_dss_rotation_type rotation_type, 2596 bool replication, const struct videomode *vm, 2597 bool mem_to_mem) 2598 { 2599 bool five_taps = true; 2600 bool fieldmode = false; 2601 int r, cconv = 0; 2602 unsigned offset0, offset1; 2603 s32 row_inc; 2604 s32 pix_inc; 2605 u16 frame_width, frame_height; 2606 unsigned int field_offset = 0; 2607 u16 in_height = height; 2608 u16 in_width = width; 2609 int x_predecim = 1, y_predecim = 1; 2610 bool ilace = !!(vm->flags & DISPLAY_FLAGS_INTERLACED); 2611 unsigned long pclk = dispc_plane_pclk_rate(plane); 2612 unsigned long lclk = dispc_plane_lclk_rate(plane); 2613 2614 if (paddr == 0 && rotation_type != OMAP_DSS_ROT_TILER) 2615 return -EINVAL; 2616 2617 switch (color_mode) { 2618 case OMAP_DSS_COLOR_YUV2: 2619 case OMAP_DSS_COLOR_UYVY: 2620 case OMAP_DSS_COLOR_NV12: 2621 if (in_width & 1) { 2622 DSSERR("input width %d is not even for YUV format\n", 2623 in_width); 2624 return -EINVAL; 2625 } 2626 break; 2627 2628 default: 2629 break; 2630 } 2631 2632 out_width = out_width == 0 ? width : out_width; 2633 out_height = out_height == 0 ? height : out_height; 2634 2635 if (ilace && height == out_height) 2636 fieldmode = true; 2637 2638 if (ilace) { 2639 if (fieldmode) 2640 in_height /= 2; 2641 pos_y /= 2; 2642 out_height /= 2; 2643 2644 DSSDBG("adjusting for ilace: height %d, pos_y %d, " 2645 "out_height %d\n", in_height, pos_y, 2646 out_height); 2647 } 2648 2649 if (!dss_feat_color_mode_supported(plane, color_mode)) 2650 return -EINVAL; 2651 2652 r = dispc_ovl_calc_scaling(pclk, lclk, caps, vm, in_width, 2653 in_height, out_width, out_height, color_mode, 2654 &five_taps, &x_predecim, &y_predecim, pos_x, 2655 rotation_type, mem_to_mem); 2656 if (r) 2657 return r; 2658 2659 in_width = in_width / x_predecim; 2660 in_height = in_height / y_predecim; 2661 2662 if (x_predecim > 1 || y_predecim > 1) 2663 DSSDBG("predecimation %d x %x, new input size %d x %d\n", 2664 x_predecim, y_predecim, in_width, in_height); 2665 2666 switch (color_mode) { 2667 case OMAP_DSS_COLOR_YUV2: 2668 case OMAP_DSS_COLOR_UYVY: 2669 case OMAP_DSS_COLOR_NV12: 2670 if (in_width & 1) { 2671 DSSDBG("predecimated input width is not even for YUV format\n"); 2672 DSSDBG("adjusting input width %d -> %d\n", 2673 in_width, in_width & ~1); 2674 2675 in_width &= ~1; 2676 } 2677 break; 2678 2679 default: 2680 break; 2681 } 2682 2683 if (color_mode == OMAP_DSS_COLOR_YUV2 || 2684 color_mode == OMAP_DSS_COLOR_UYVY || 2685 color_mode == OMAP_DSS_COLOR_NV12) 2686 cconv = 1; 2687 2688 if (ilace && !fieldmode) { 2689 /* 2690 * when downscaling the bottom field may have to start several 2691 * source lines below the top field. Unfortunately ACCUI 2692 * registers will only hold the fractional part of the offset 2693 * so the integer part must be added to the base address of the 2694 * bottom field. 2695 */ 2696 if (!in_height || in_height == out_height) 2697 field_offset = 0; 2698 else 2699 field_offset = in_height / out_height / 2; 2700 } 2701 2702 /* Fields are independent but interleaved in memory. */ 2703 if (fieldmode) 2704 field_offset = 1; 2705 2706 offset0 = 0; 2707 offset1 = 0; 2708 row_inc = 0; 2709 pix_inc = 0; 2710 2711 if (plane == OMAP_DSS_WB) { 2712 frame_width = out_width; 2713 frame_height = out_height; 2714 } else { 2715 frame_width = in_width; 2716 frame_height = height; 2717 } 2718 2719 if (rotation_type == OMAP_DSS_ROT_TILER) 2720 calc_tiler_rotation_offset(screen_width, frame_width, 2721 color_mode, fieldmode, field_offset, 2722 &offset0, &offset1, &row_inc, &pix_inc, 2723 x_predecim, y_predecim); 2724 else if (rotation_type == OMAP_DSS_ROT_DMA) 2725 calc_dma_rotation_offset(rotation, mirror, screen_width, 2726 frame_width, frame_height, 2727 color_mode, fieldmode, field_offset, 2728 &offset0, &offset1, &row_inc, &pix_inc, 2729 x_predecim, y_predecim); 2730 else 2731 calc_vrfb_rotation_offset(rotation, mirror, 2732 screen_width, frame_width, frame_height, 2733 color_mode, fieldmode, field_offset, 2734 &offset0, &offset1, &row_inc, &pix_inc, 2735 x_predecim, y_predecim); 2736 2737 DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n", 2738 offset0, offset1, row_inc, pix_inc); 2739 2740 dispc_ovl_set_color_mode(plane, color_mode); 2741 2742 dispc_ovl_configure_burst_type(plane, rotation_type); 2743 2744 if (dispc.feat->reverse_ilace_field_order) 2745 swap(offset0, offset1); 2746 2747 dispc_ovl_set_ba0(plane, paddr + offset0); 2748 dispc_ovl_set_ba1(plane, paddr + offset1); 2749 2750 if (OMAP_DSS_COLOR_NV12 == color_mode) { 2751 dispc_ovl_set_ba0_uv(plane, p_uv_addr + offset0); 2752 dispc_ovl_set_ba1_uv(plane, p_uv_addr + offset1); 2753 } 2754 2755 if (dispc.feat->last_pixel_inc_missing) 2756 row_inc += pix_inc - 1; 2757 2758 dispc_ovl_set_row_inc(plane, row_inc); 2759 dispc_ovl_set_pix_inc(plane, pix_inc); 2760 2761 DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, in_width, 2762 in_height, out_width, out_height); 2763 2764 dispc_ovl_set_pos(plane, caps, pos_x, pos_y); 2765 2766 dispc_ovl_set_input_size(plane, in_width, in_height); 2767 2768 if (caps & OMAP_DSS_OVL_CAP_SCALE) { 2769 dispc_ovl_set_scaling(plane, in_width, in_height, out_width, 2770 out_height, ilace, five_taps, fieldmode, 2771 color_mode, rotation); 2772 dispc_ovl_set_output_size(plane, out_width, out_height); 2773 dispc_ovl_set_vid_color_conv(plane, cconv); 2774 } 2775 2776 dispc_ovl_set_rotation_attrs(plane, rotation, rotation_type, mirror, 2777 color_mode); 2778 2779 dispc_ovl_set_zorder(plane, caps, zorder); 2780 dispc_ovl_set_pre_mult_alpha(plane, caps, pre_mult_alpha); 2781 dispc_ovl_setup_global_alpha(plane, caps, global_alpha); 2782 2783 dispc_ovl_enable_replication(plane, caps, replication); 2784 2785 return 0; 2786 } 2787 2788 int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi, 2789 bool replication, const struct videomode *vm, 2790 bool mem_to_mem) 2791 { 2792 int r; 2793 enum omap_overlay_caps caps = dss_feat_get_overlay_caps(plane); 2794 enum omap_channel channel; 2795 2796 channel = dispc_ovl_get_channel_out(plane); 2797 2798 DSSDBG("dispc_ovl_setup %d, pa %pad, pa_uv %pad, sw %d, %d,%d, %dx%d ->" 2799 " %dx%d, cmode %x, rot %d, mir %d, chan %d repl %d\n", 2800 plane, &oi->paddr, &oi->p_uv_addr, oi->screen_width, oi->pos_x, 2801 oi->pos_y, oi->width, oi->height, oi->out_width, oi->out_height, 2802 oi->color_mode, oi->rotation, oi->mirror, channel, replication); 2803 2804 r = dispc_ovl_setup_common(plane, caps, oi->paddr, oi->p_uv_addr, 2805 oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height, 2806 oi->out_width, oi->out_height, oi->color_mode, oi->rotation, 2807 oi->mirror, oi->zorder, oi->pre_mult_alpha, oi->global_alpha, 2808 oi->rotation_type, replication, vm, mem_to_mem); 2809 2810 return r; 2811 } 2812 EXPORT_SYMBOL(dispc_ovl_setup); 2813 2814 int dispc_wb_setup(const struct omap_dss_writeback_info *wi, 2815 bool mem_to_mem, const struct videomode *vm) 2816 { 2817 int r; 2818 u32 l; 2819 enum omap_plane plane = OMAP_DSS_WB; 2820 const int pos_x = 0, pos_y = 0; 2821 const u8 zorder = 0, global_alpha = 0; 2822 const bool replication = false; 2823 bool truncation; 2824 int in_width = vm->hactive; 2825 int in_height = vm->vactive; 2826 enum omap_overlay_caps caps = 2827 OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA; 2828 2829 DSSDBG("dispc_wb_setup, pa %x, pa_uv %x, %d,%d -> %dx%d, cmode %x, " 2830 "rot %d, mir %d\n", wi->paddr, wi->p_uv_addr, in_width, 2831 in_height, wi->width, wi->height, wi->color_mode, wi->rotation, 2832 wi->mirror); 2833 2834 r = dispc_ovl_setup_common(plane, caps, wi->paddr, wi->p_uv_addr, 2835 wi->buf_width, pos_x, pos_y, in_width, in_height, wi->width, 2836 wi->height, wi->color_mode, wi->rotation, wi->mirror, zorder, 2837 wi->pre_mult_alpha, global_alpha, wi->rotation_type, 2838 replication, vm, mem_to_mem); 2839 2840 switch (wi->color_mode) { 2841 case OMAP_DSS_COLOR_RGB16: 2842 case OMAP_DSS_COLOR_RGB24P: 2843 case OMAP_DSS_COLOR_ARGB16: 2844 case OMAP_DSS_COLOR_RGBA16: 2845 case OMAP_DSS_COLOR_RGB12U: 2846 case OMAP_DSS_COLOR_ARGB16_1555: 2847 case OMAP_DSS_COLOR_XRGB16_1555: 2848 case OMAP_DSS_COLOR_RGBX16: 2849 truncation = true; 2850 break; 2851 default: 2852 truncation = false; 2853 break; 2854 } 2855 2856 /* setup extra DISPC_WB_ATTRIBUTES */ 2857 l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); 2858 l = FLD_MOD(l, truncation, 10, 10); /* TRUNCATIONENABLE */ 2859 l = FLD_MOD(l, mem_to_mem, 19, 19); /* WRITEBACKMODE */ 2860 if (mem_to_mem) 2861 l = FLD_MOD(l, 1, 26, 24); /* CAPTUREMODE */ 2862 else 2863 l = FLD_MOD(l, 0, 26, 24); /* CAPTUREMODE */ 2864 dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), l); 2865 2866 if (mem_to_mem) { 2867 /* WBDELAYCOUNT */ 2868 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 7, 0); 2869 } else { 2870 int wbdelay; 2871 2872 wbdelay = min(vm->vfront_porch + 2873 vm->vsync_len + vm->vback_porch, (u32)255); 2874 2875 /* WBDELAYCOUNT */ 2876 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), wbdelay, 7, 0); 2877 } 2878 2879 return r; 2880 } 2881 2882 int dispc_ovl_enable(enum omap_plane plane, bool enable) 2883 { 2884 DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); 2885 2886 REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 0, 0); 2887 2888 return 0; 2889 } 2890 EXPORT_SYMBOL(dispc_ovl_enable); 2891 2892 bool dispc_ovl_enabled(enum omap_plane plane) 2893 { 2894 return REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0); 2895 } 2896 EXPORT_SYMBOL(dispc_ovl_enabled); 2897 2898 enum omap_dss_output_id dispc_mgr_get_supported_outputs(enum omap_channel channel) 2899 { 2900 return dss_feat_get_supported_outputs(channel); 2901 } 2902 EXPORT_SYMBOL(dispc_mgr_get_supported_outputs); 2903 2904 void dispc_mgr_enable(enum omap_channel channel, bool enable) 2905 { 2906 mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable); 2907 /* flush posted write */ 2908 mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE); 2909 } 2910 EXPORT_SYMBOL(dispc_mgr_enable); 2911 2912 bool dispc_mgr_is_enabled(enum omap_channel channel) 2913 { 2914 return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE); 2915 } 2916 EXPORT_SYMBOL(dispc_mgr_is_enabled); 2917 2918 void dispc_wb_enable(bool enable) 2919 { 2920 dispc_ovl_enable(OMAP_DSS_WB, enable); 2921 } 2922 2923 bool dispc_wb_is_enabled(void) 2924 { 2925 return dispc_ovl_enabled(OMAP_DSS_WB); 2926 } 2927 2928 static void dispc_lcd_enable_signal_polarity(bool act_high) 2929 { 2930 if (!dss_has_feature(FEAT_LCDENABLEPOL)) 2931 return; 2932 2933 REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29); 2934 } 2935 2936 void dispc_lcd_enable_signal(bool enable) 2937 { 2938 if (!dss_has_feature(FEAT_LCDENABLESIGNAL)) 2939 return; 2940 2941 REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28); 2942 } 2943 2944 void dispc_pck_free_enable(bool enable) 2945 { 2946 if (!dss_has_feature(FEAT_PCKFREEENABLE)) 2947 return; 2948 2949 REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27); 2950 } 2951 2952 static void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable) 2953 { 2954 mgr_fld_write(channel, DISPC_MGR_FLD_FIFOHANDCHECK, enable); 2955 } 2956 2957 2958 static void dispc_mgr_set_lcd_type_tft(enum omap_channel channel) 2959 { 2960 mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, 1); 2961 } 2962 2963 static void dispc_set_loadmode(enum omap_dss_load_mode mode) 2964 { 2965 REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1); 2966 } 2967 2968 2969 static void dispc_mgr_set_default_color(enum omap_channel channel, u32 color) 2970 { 2971 dispc_write_reg(DISPC_DEFAULT_COLOR(channel), color); 2972 } 2973 2974 static void dispc_mgr_set_trans_key(enum omap_channel ch, 2975 enum omap_dss_trans_key_type type, 2976 u32 trans_key) 2977 { 2978 mgr_fld_write(ch, DISPC_MGR_FLD_TCKSELECTION, type); 2979 2980 dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key); 2981 } 2982 2983 static void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable) 2984 { 2985 mgr_fld_write(ch, DISPC_MGR_FLD_TCKENABLE, enable); 2986 } 2987 2988 static void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch, 2989 bool enable) 2990 { 2991 if (!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) 2992 return; 2993 2994 if (ch == OMAP_DSS_CHANNEL_LCD) 2995 REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18); 2996 else if (ch == OMAP_DSS_CHANNEL_DIGIT) 2997 REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19); 2998 } 2999 3000 void dispc_mgr_setup(enum omap_channel channel, 3001 const struct omap_overlay_manager_info *info) 3002 { 3003 dispc_mgr_set_default_color(channel, info->default_color); 3004 dispc_mgr_set_trans_key(channel, info->trans_key_type, info->trans_key); 3005 dispc_mgr_enable_trans_key(channel, info->trans_enabled); 3006 dispc_mgr_enable_alpha_fixed_zorder(channel, 3007 info->partial_alpha_enabled); 3008 if (dss_has_feature(FEAT_CPR)) { 3009 dispc_mgr_enable_cpr(channel, info->cpr_enable); 3010 dispc_mgr_set_cpr_coef(channel, &info->cpr_coefs); 3011 } 3012 } 3013 EXPORT_SYMBOL(dispc_mgr_setup); 3014 3015 static void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines) 3016 { 3017 int code; 3018 3019 switch (data_lines) { 3020 case 12: 3021 code = 0; 3022 break; 3023 case 16: 3024 code = 1; 3025 break; 3026 case 18: 3027 code = 2; 3028 break; 3029 case 24: 3030 code = 3; 3031 break; 3032 default: 3033 BUG(); 3034 return; 3035 } 3036 3037 mgr_fld_write(channel, DISPC_MGR_FLD_TFTDATALINES, code); 3038 } 3039 3040 static void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode) 3041 { 3042 u32 l; 3043 int gpout0, gpout1; 3044 3045 switch (mode) { 3046 case DSS_IO_PAD_MODE_RESET: 3047 gpout0 = 0; 3048 gpout1 = 0; 3049 break; 3050 case DSS_IO_PAD_MODE_RFBI: 3051 gpout0 = 1; 3052 gpout1 = 0; 3053 break; 3054 case DSS_IO_PAD_MODE_BYPASS: 3055 gpout0 = 1; 3056 gpout1 = 1; 3057 break; 3058 default: 3059 BUG(); 3060 return; 3061 } 3062 3063 l = dispc_read_reg(DISPC_CONTROL); 3064 l = FLD_MOD(l, gpout0, 15, 15); 3065 l = FLD_MOD(l, gpout1, 16, 16); 3066 dispc_write_reg(DISPC_CONTROL, l); 3067 } 3068 3069 static void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable) 3070 { 3071 mgr_fld_write(channel, DISPC_MGR_FLD_STALLMODE, enable); 3072 } 3073 3074 void dispc_mgr_set_lcd_config(enum omap_channel channel, 3075 const struct dss_lcd_mgr_config *config) 3076 { 3077 dispc_mgr_set_io_pad_mode(config->io_pad_mode); 3078 3079 dispc_mgr_enable_stallmode(channel, config->stallmode); 3080 dispc_mgr_enable_fifohandcheck(channel, config->fifohandcheck); 3081 3082 dispc_mgr_set_clock_div(channel, &config->clock_info); 3083 3084 dispc_mgr_set_tft_data_lines(channel, config->video_port_width); 3085 3086 dispc_lcd_enable_signal_polarity(config->lcden_sig_polarity); 3087 3088 dispc_mgr_set_lcd_type_tft(channel); 3089 } 3090 EXPORT_SYMBOL(dispc_mgr_set_lcd_config); 3091 3092 static bool _dispc_mgr_size_ok(u16 width, u16 height) 3093 { 3094 return width <= dispc.feat->mgr_width_max && 3095 height <= dispc.feat->mgr_height_max; 3096 } 3097 3098 static bool _dispc_lcd_timings_ok(int hsync_len, int hfp, int hbp, 3099 int vsw, int vfp, int vbp) 3100 { 3101 if (hsync_len < 1 || hsync_len > dispc.feat->sw_max || 3102 hfp < 1 || hfp > dispc.feat->hp_max || 3103 hbp < 1 || hbp > dispc.feat->hp_max || 3104 vsw < 1 || vsw > dispc.feat->sw_max || 3105 vfp < 0 || vfp > dispc.feat->vp_max || 3106 vbp < 0 || vbp > dispc.feat->vp_max) 3107 return false; 3108 return true; 3109 } 3110 3111 static bool _dispc_mgr_pclk_ok(enum omap_channel channel, 3112 unsigned long pclk) 3113 { 3114 if (dss_mgr_is_lcd(channel)) 3115 return pclk <= dispc.feat->max_lcd_pclk; 3116 else 3117 return pclk <= dispc.feat->max_tv_pclk; 3118 } 3119 3120 bool dispc_mgr_timings_ok(enum omap_channel channel, const struct videomode *vm) 3121 { 3122 if (!_dispc_mgr_size_ok(vm->hactive, vm->vactive)) 3123 return false; 3124 3125 if (!_dispc_mgr_pclk_ok(channel, vm->pixelclock)) 3126 return false; 3127 3128 if (dss_mgr_is_lcd(channel)) { 3129 /* TODO: OMAP4+ supports interlace for LCD outputs */ 3130 if (vm->flags & DISPLAY_FLAGS_INTERLACED) 3131 return false; 3132 3133 if (!_dispc_lcd_timings_ok(vm->hsync_len, 3134 vm->hfront_porch, vm->hback_porch, 3135 vm->vsync_len, vm->vfront_porch, 3136 vm->vback_porch)) 3137 return false; 3138 } 3139 3140 return true; 3141 } 3142 3143 static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, 3144 const struct videomode *vm) 3145 { 3146 u32 timing_h, timing_v, l; 3147 bool onoff, rf, ipc, vs, hs, de; 3148 3149 timing_h = FLD_VAL(vm->hsync_len - 1, dispc.feat->sw_start, 0) | 3150 FLD_VAL(vm->hfront_porch - 1, dispc.feat->fp_start, 8) | 3151 FLD_VAL(vm->hback_porch - 1, dispc.feat->bp_start, 20); 3152 timing_v = FLD_VAL(vm->vsync_len - 1, dispc.feat->sw_start, 0) | 3153 FLD_VAL(vm->vfront_porch, dispc.feat->fp_start, 8) | 3154 FLD_VAL(vm->vback_porch, dispc.feat->bp_start, 20); 3155 3156 dispc_write_reg(DISPC_TIMING_H(channel), timing_h); 3157 dispc_write_reg(DISPC_TIMING_V(channel), timing_v); 3158 3159 if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH) 3160 vs = false; 3161 else 3162 vs = true; 3163 3164 if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH) 3165 hs = false; 3166 else 3167 hs = true; 3168 3169 if (vm->flags & DISPLAY_FLAGS_DE_HIGH) 3170 de = false; 3171 else 3172 de = true; 3173 3174 if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) 3175 ipc = false; 3176 else 3177 ipc = true; 3178 3179 /* always use the 'rf' setting */ 3180 onoff = true; 3181 3182 if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE) 3183 rf = true; 3184 else 3185 rf = false; 3186 3187 l = FLD_VAL(onoff, 17, 17) | 3188 FLD_VAL(rf, 16, 16) | 3189 FLD_VAL(de, 15, 15) | 3190 FLD_VAL(ipc, 14, 14) | 3191 FLD_VAL(hs, 13, 13) | 3192 FLD_VAL(vs, 12, 12); 3193 3194 /* always set ALIGN bit when available */ 3195 if (dispc.feat->supports_sync_align) 3196 l |= (1 << 18); 3197 3198 dispc_write_reg(DISPC_POL_FREQ(channel), l); 3199 3200 if (dispc.syscon_pol) { 3201 const int shifts[] = { 3202 [OMAP_DSS_CHANNEL_LCD] = 0, 3203 [OMAP_DSS_CHANNEL_LCD2] = 1, 3204 [OMAP_DSS_CHANNEL_LCD3] = 2, 3205 }; 3206 3207 u32 mask, val; 3208 3209 mask = (1 << 0) | (1 << 3) | (1 << 6); 3210 val = (rf << 0) | (ipc << 3) | (onoff << 6); 3211 3212 mask <<= 16 + shifts[channel]; 3213 val <<= 16 + shifts[channel]; 3214 3215 regmap_update_bits(dispc.syscon_pol, dispc.syscon_pol_offset, 3216 mask, val); 3217 } 3218 } 3219 3220 /* change name to mode? */ 3221 void dispc_mgr_set_timings(enum omap_channel channel, 3222 const struct videomode *vm) 3223 { 3224 unsigned xtot, ytot; 3225 unsigned long ht, vt; 3226 struct videomode t = *vm; 3227 3228 DSSDBG("channel %d xres %u yres %u\n", channel, t.hactive, t.vactive); 3229 3230 if (!dispc_mgr_timings_ok(channel, &t)) { 3231 BUG(); 3232 return; 3233 } 3234 3235 if (dss_mgr_is_lcd(channel)) { 3236 _dispc_mgr_set_lcd_timings(channel, &t); 3237 3238 xtot = t.hactive + t.hfront_porch + t.hsync_len + t.hback_porch; 3239 ytot = t.vactive + t.vfront_porch + t.vsync_len + t.vback_porch; 3240 3241 ht = vm->pixelclock / xtot; 3242 vt = vm->pixelclock / xtot / ytot; 3243 3244 DSSDBG("pck %lu\n", vm->pixelclock); 3245 DSSDBG("hsync_len %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", 3246 t.hsync_len, t.hfront_porch, t.hback_porch, 3247 t.vsync_len, t.vfront_porch, t.vback_porch); 3248 DSSDBG("vsync_level %d hsync_level %d data_pclk_edge %d de_level %d sync_pclk_edge %d\n", 3249 !!(t.flags & DISPLAY_FLAGS_VSYNC_HIGH), 3250 !!(t.flags & DISPLAY_FLAGS_HSYNC_HIGH), 3251 !!(t.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE), 3252 !!(t.flags & DISPLAY_FLAGS_DE_HIGH), 3253 !!(t.flags & DISPLAY_FLAGS_SYNC_POSEDGE)); 3254 3255 DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); 3256 } else { 3257 if (t.flags & DISPLAY_FLAGS_INTERLACED) 3258 t.vactive /= 2; 3259 3260 if (dispc.feat->supports_double_pixel) 3261 REG_FLD_MOD(DISPC_CONTROL, 3262 !!(t.flags & DISPLAY_FLAGS_DOUBLECLK), 3263 19, 17); 3264 } 3265 3266 dispc_mgr_set_size(channel, t.hactive, t.vactive); 3267 } 3268 EXPORT_SYMBOL(dispc_mgr_set_timings); 3269 3270 static void dispc_mgr_set_lcd_divisor(enum omap_channel channel, u16 lck_div, 3271 u16 pck_div) 3272 { 3273 BUG_ON(lck_div < 1); 3274 BUG_ON(pck_div < 1); 3275 3276 dispc_write_reg(DISPC_DIVISORo(channel), 3277 FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); 3278 3279 if (!dss_has_feature(FEAT_CORE_CLK_DIV) && 3280 channel == OMAP_DSS_CHANNEL_LCD) 3281 dispc.core_clk_rate = dispc_fclk_rate() / lck_div; 3282 } 3283 3284 static void dispc_mgr_get_lcd_divisor(enum omap_channel channel, int *lck_div, 3285 int *pck_div) 3286 { 3287 u32 l; 3288 l = dispc_read_reg(DISPC_DIVISORo(channel)); 3289 *lck_div = FLD_GET(l, 23, 16); 3290 *pck_div = FLD_GET(l, 7, 0); 3291 } 3292 3293 static unsigned long dispc_fclk_rate(void) 3294 { 3295 unsigned long r; 3296 enum dss_clk_source src; 3297 3298 src = dss_get_dispc_clk_source(); 3299 3300 if (src == DSS_CLK_SRC_FCK) { 3301 r = dss_get_dispc_clk_rate(); 3302 } else { 3303 struct dss_pll *pll; 3304 unsigned clkout_idx; 3305 3306 pll = dss_pll_find_by_src(src); 3307 clkout_idx = dss_pll_get_clkout_idx_for_src(src); 3308 3309 r = pll->cinfo.clkout[clkout_idx]; 3310 } 3311 3312 return r; 3313 } 3314 3315 static unsigned long dispc_mgr_lclk_rate(enum omap_channel channel) 3316 { 3317 int lcd; 3318 unsigned long r; 3319 enum dss_clk_source src; 3320 3321 /* for TV, LCLK rate is the FCLK rate */ 3322 if (!dss_mgr_is_lcd(channel)) 3323 return dispc_fclk_rate(); 3324 3325 src = dss_get_lcd_clk_source(channel); 3326 3327 if (src == DSS_CLK_SRC_FCK) { 3328 r = dss_get_dispc_clk_rate(); 3329 } else { 3330 struct dss_pll *pll; 3331 unsigned clkout_idx; 3332 3333 pll = dss_pll_find_by_src(src); 3334 clkout_idx = dss_pll_get_clkout_idx_for_src(src); 3335 3336 r = pll->cinfo.clkout[clkout_idx]; 3337 } 3338 3339 lcd = REG_GET(DISPC_DIVISORo(channel), 23, 16); 3340 3341 return r / lcd; 3342 } 3343 3344 static unsigned long dispc_mgr_pclk_rate(enum omap_channel channel) 3345 { 3346 unsigned long r; 3347 3348 if (dss_mgr_is_lcd(channel)) { 3349 int pcd; 3350 u32 l; 3351 3352 l = dispc_read_reg(DISPC_DIVISORo(channel)); 3353 3354 pcd = FLD_GET(l, 7, 0); 3355 3356 r = dispc_mgr_lclk_rate(channel); 3357 3358 return r / pcd; 3359 } else { 3360 return dispc.tv_pclk_rate; 3361 } 3362 } 3363 3364 void dispc_set_tv_pclk(unsigned long pclk) 3365 { 3366 dispc.tv_pclk_rate = pclk; 3367 } 3368 3369 static unsigned long dispc_core_clk_rate(void) 3370 { 3371 return dispc.core_clk_rate; 3372 } 3373 3374 static unsigned long dispc_plane_pclk_rate(enum omap_plane plane) 3375 { 3376 enum omap_channel channel; 3377 3378 if (plane == OMAP_DSS_WB) 3379 return 0; 3380 3381 channel = dispc_ovl_get_channel_out(plane); 3382 3383 return dispc_mgr_pclk_rate(channel); 3384 } 3385 3386 static unsigned long dispc_plane_lclk_rate(enum omap_plane plane) 3387 { 3388 enum omap_channel channel; 3389 3390 if (plane == OMAP_DSS_WB) 3391 return 0; 3392 3393 channel = dispc_ovl_get_channel_out(plane); 3394 3395 return dispc_mgr_lclk_rate(channel); 3396 } 3397 3398 static void dispc_dump_clocks_channel(struct seq_file *s, enum omap_channel channel) 3399 { 3400 int lcd, pcd; 3401 enum dss_clk_source lcd_clk_src; 3402 3403 seq_printf(s, "- %s -\n", mgr_desc[channel].name); 3404 3405 lcd_clk_src = dss_get_lcd_clk_source(channel); 3406 3407 seq_printf(s, "%s clk source = %s\n", mgr_desc[channel].name, 3408 dss_get_clk_source_name(lcd_clk_src)); 3409 3410 dispc_mgr_get_lcd_divisor(channel, &lcd, &pcd); 3411 3412 seq_printf(s, "lck\t\t%-16lulck div\t%u\n", 3413 dispc_mgr_lclk_rate(channel), lcd); 3414 seq_printf(s, "pck\t\t%-16lupck div\t%u\n", 3415 dispc_mgr_pclk_rate(channel), pcd); 3416 } 3417 3418 void dispc_dump_clocks(struct seq_file *s) 3419 { 3420 int lcd; 3421 u32 l; 3422 enum dss_clk_source dispc_clk_src = dss_get_dispc_clk_source(); 3423 3424 if (dispc_runtime_get()) 3425 return; 3426 3427 seq_printf(s, "- DISPC -\n"); 3428 3429 seq_printf(s, "dispc fclk source = %s\n", 3430 dss_get_clk_source_name(dispc_clk_src)); 3431 3432 seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate()); 3433 3434 if (dss_has_feature(FEAT_CORE_CLK_DIV)) { 3435 seq_printf(s, "- DISPC-CORE-CLK -\n"); 3436 l = dispc_read_reg(DISPC_DIVISOR); 3437 lcd = FLD_GET(l, 23, 16); 3438 3439 seq_printf(s, "lck\t\t%-16lulck div\t%u\n", 3440 (dispc_fclk_rate()/lcd), lcd); 3441 } 3442 3443 dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD); 3444 3445 if (dss_has_feature(FEAT_MGR_LCD2)) 3446 dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD2); 3447 if (dss_has_feature(FEAT_MGR_LCD3)) 3448 dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD3); 3449 3450 dispc_runtime_put(); 3451 } 3452 3453 static void dispc_dump_regs(struct seq_file *s) 3454 { 3455 int i, j; 3456 const char *mgr_names[] = { 3457 [OMAP_DSS_CHANNEL_LCD] = "LCD", 3458 [OMAP_DSS_CHANNEL_DIGIT] = "TV", 3459 [OMAP_DSS_CHANNEL_LCD2] = "LCD2", 3460 [OMAP_DSS_CHANNEL_LCD3] = "LCD3", 3461 }; 3462 const char *ovl_names[] = { 3463 [OMAP_DSS_GFX] = "GFX", 3464 [OMAP_DSS_VIDEO1] = "VID1", 3465 [OMAP_DSS_VIDEO2] = "VID2", 3466 [OMAP_DSS_VIDEO3] = "VID3", 3467 [OMAP_DSS_WB] = "WB", 3468 }; 3469 const char **p_names; 3470 3471 #define DUMPREG(r) seq_printf(s, "%-50s %08x\n", #r, dispc_read_reg(r)) 3472 3473 if (dispc_runtime_get()) 3474 return; 3475 3476 /* DISPC common registers */ 3477 DUMPREG(DISPC_REVISION); 3478 DUMPREG(DISPC_SYSCONFIG); 3479 DUMPREG(DISPC_SYSSTATUS); 3480 DUMPREG(DISPC_IRQSTATUS); 3481 DUMPREG(DISPC_IRQENABLE); 3482 DUMPREG(DISPC_CONTROL); 3483 DUMPREG(DISPC_CONFIG); 3484 DUMPREG(DISPC_CAPABLE); 3485 DUMPREG(DISPC_LINE_STATUS); 3486 DUMPREG(DISPC_LINE_NUMBER); 3487 if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) || 3488 dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) 3489 DUMPREG(DISPC_GLOBAL_ALPHA); 3490 if (dss_has_feature(FEAT_MGR_LCD2)) { 3491 DUMPREG(DISPC_CONTROL2); 3492 DUMPREG(DISPC_CONFIG2); 3493 } 3494 if (dss_has_feature(FEAT_MGR_LCD3)) { 3495 DUMPREG(DISPC_CONTROL3); 3496 DUMPREG(DISPC_CONFIG3); 3497 } 3498 if (dss_has_feature(FEAT_MFLAG)) 3499 DUMPREG(DISPC_GLOBAL_MFLAG_ATTRIBUTE); 3500 3501 #undef DUMPREG 3502 3503 #define DISPC_REG(i, name) name(i) 3504 #define DUMPREG(i, r) seq_printf(s, "%s(%s)%*s %08x\n", #r, p_names[i], \ 3505 (int)(48 - strlen(#r) - strlen(p_names[i])), " ", \ 3506 dispc_read_reg(DISPC_REG(i, r))) 3507 3508 p_names = mgr_names; 3509 3510 /* DISPC channel specific registers */ 3511 for (i = 0; i < dss_feat_get_num_mgrs(); i++) { 3512 DUMPREG(i, DISPC_DEFAULT_COLOR); 3513 DUMPREG(i, DISPC_TRANS_COLOR); 3514 DUMPREG(i, DISPC_SIZE_MGR); 3515 3516 if (i == OMAP_DSS_CHANNEL_DIGIT) 3517 continue; 3518 3519 DUMPREG(i, DISPC_TIMING_H); 3520 DUMPREG(i, DISPC_TIMING_V); 3521 DUMPREG(i, DISPC_POL_FREQ); 3522 DUMPREG(i, DISPC_DIVISORo); 3523 3524 DUMPREG(i, DISPC_DATA_CYCLE1); 3525 DUMPREG(i, DISPC_DATA_CYCLE2); 3526 DUMPREG(i, DISPC_DATA_CYCLE3); 3527 3528 if (dss_has_feature(FEAT_CPR)) { 3529 DUMPREG(i, DISPC_CPR_COEF_R); 3530 DUMPREG(i, DISPC_CPR_COEF_G); 3531 DUMPREG(i, DISPC_CPR_COEF_B); 3532 } 3533 } 3534 3535 p_names = ovl_names; 3536 3537 for (i = 0; i < dss_feat_get_num_ovls(); i++) { 3538 DUMPREG(i, DISPC_OVL_BA0); 3539 DUMPREG(i, DISPC_OVL_BA1); 3540 DUMPREG(i, DISPC_OVL_POSITION); 3541 DUMPREG(i, DISPC_OVL_SIZE); 3542 DUMPREG(i, DISPC_OVL_ATTRIBUTES); 3543 DUMPREG(i, DISPC_OVL_FIFO_THRESHOLD); 3544 DUMPREG(i, DISPC_OVL_FIFO_SIZE_STATUS); 3545 DUMPREG(i, DISPC_OVL_ROW_INC); 3546 DUMPREG(i, DISPC_OVL_PIXEL_INC); 3547 3548 if (dss_has_feature(FEAT_PRELOAD)) 3549 DUMPREG(i, DISPC_OVL_PRELOAD); 3550 if (dss_has_feature(FEAT_MFLAG)) 3551 DUMPREG(i, DISPC_OVL_MFLAG_THRESHOLD); 3552 3553 if (i == OMAP_DSS_GFX) { 3554 DUMPREG(i, DISPC_OVL_WINDOW_SKIP); 3555 DUMPREG(i, DISPC_OVL_TABLE_BA); 3556 continue; 3557 } 3558 3559 DUMPREG(i, DISPC_OVL_FIR); 3560 DUMPREG(i, DISPC_OVL_PICTURE_SIZE); 3561 DUMPREG(i, DISPC_OVL_ACCU0); 3562 DUMPREG(i, DISPC_OVL_ACCU1); 3563 if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { 3564 DUMPREG(i, DISPC_OVL_BA0_UV); 3565 DUMPREG(i, DISPC_OVL_BA1_UV); 3566 DUMPREG(i, DISPC_OVL_FIR2); 3567 DUMPREG(i, DISPC_OVL_ACCU2_0); 3568 DUMPREG(i, DISPC_OVL_ACCU2_1); 3569 } 3570 if (dss_has_feature(FEAT_ATTR2)) 3571 DUMPREG(i, DISPC_OVL_ATTRIBUTES2); 3572 } 3573 3574 if (dispc.feat->has_writeback) { 3575 i = OMAP_DSS_WB; 3576 DUMPREG(i, DISPC_OVL_BA0); 3577 DUMPREG(i, DISPC_OVL_BA1); 3578 DUMPREG(i, DISPC_OVL_SIZE); 3579 DUMPREG(i, DISPC_OVL_ATTRIBUTES); 3580 DUMPREG(i, DISPC_OVL_FIFO_THRESHOLD); 3581 DUMPREG(i, DISPC_OVL_FIFO_SIZE_STATUS); 3582 DUMPREG(i, DISPC_OVL_ROW_INC); 3583 DUMPREG(i, DISPC_OVL_PIXEL_INC); 3584 3585 if (dss_has_feature(FEAT_MFLAG)) 3586 DUMPREG(i, DISPC_OVL_MFLAG_THRESHOLD); 3587 3588 DUMPREG(i, DISPC_OVL_FIR); 3589 DUMPREG(i, DISPC_OVL_PICTURE_SIZE); 3590 DUMPREG(i, DISPC_OVL_ACCU0); 3591 DUMPREG(i, DISPC_OVL_ACCU1); 3592 if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { 3593 DUMPREG(i, DISPC_OVL_BA0_UV); 3594 DUMPREG(i, DISPC_OVL_BA1_UV); 3595 DUMPREG(i, DISPC_OVL_FIR2); 3596 DUMPREG(i, DISPC_OVL_ACCU2_0); 3597 DUMPREG(i, DISPC_OVL_ACCU2_1); 3598 } 3599 if (dss_has_feature(FEAT_ATTR2)) 3600 DUMPREG(i, DISPC_OVL_ATTRIBUTES2); 3601 } 3602 3603 #undef DISPC_REG 3604 #undef DUMPREG 3605 3606 #define DISPC_REG(plane, name, i) name(plane, i) 3607 #define DUMPREG(plane, name, i) \ 3608 seq_printf(s, "%s_%d(%s)%*s %08x\n", #name, i, p_names[plane], \ 3609 (int)(46 - strlen(#name) - strlen(p_names[plane])), " ", \ 3610 dispc_read_reg(DISPC_REG(plane, name, i))) 3611 3612 /* Video pipeline coefficient registers */ 3613 3614 /* start from OMAP_DSS_VIDEO1 */ 3615 for (i = 1; i < dss_feat_get_num_ovls(); i++) { 3616 for (j = 0; j < 8; j++) 3617 DUMPREG(i, DISPC_OVL_FIR_COEF_H, j); 3618 3619 for (j = 0; j < 8; j++) 3620 DUMPREG(i, DISPC_OVL_FIR_COEF_HV, j); 3621 3622 for (j = 0; j < 5; j++) 3623 DUMPREG(i, DISPC_OVL_CONV_COEF, j); 3624 3625 if (dss_has_feature(FEAT_FIR_COEF_V)) { 3626 for (j = 0; j < 8; j++) 3627 DUMPREG(i, DISPC_OVL_FIR_COEF_V, j); 3628 } 3629 3630 if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { 3631 for (j = 0; j < 8; j++) 3632 DUMPREG(i, DISPC_OVL_FIR_COEF_H2, j); 3633 3634 for (j = 0; j < 8; j++) 3635 DUMPREG(i, DISPC_OVL_FIR_COEF_HV2, j); 3636 3637 for (j = 0; j < 8; j++) 3638 DUMPREG(i, DISPC_OVL_FIR_COEF_V2, j); 3639 } 3640 } 3641 3642 dispc_runtime_put(); 3643 3644 #undef DISPC_REG 3645 #undef DUMPREG 3646 } 3647 3648 /* calculate clock rates using dividers in cinfo */ 3649 int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, 3650 struct dispc_clock_info *cinfo) 3651 { 3652 if (cinfo->lck_div > 255 || cinfo->lck_div == 0) 3653 return -EINVAL; 3654 if (cinfo->pck_div < 1 || cinfo->pck_div > 255) 3655 return -EINVAL; 3656 3657 cinfo->lck = dispc_fclk_rate / cinfo->lck_div; 3658 cinfo->pck = cinfo->lck / cinfo->pck_div; 3659 3660 return 0; 3661 } 3662 3663 bool dispc_div_calc(unsigned long dispc, 3664 unsigned long pck_min, unsigned long pck_max, 3665 dispc_div_calc_func func, void *data) 3666 { 3667 int lckd, lckd_start, lckd_stop; 3668 int pckd, pckd_start, pckd_stop; 3669 unsigned long pck, lck; 3670 unsigned long lck_max; 3671 unsigned long pckd_hw_min, pckd_hw_max; 3672 unsigned min_fck_per_pck; 3673 unsigned long fck; 3674 3675 #ifdef CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK 3676 min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK; 3677 #else 3678 min_fck_per_pck = 0; 3679 #endif 3680 3681 pckd_hw_min = dss_feat_get_param_min(FEAT_PARAM_DSS_PCD); 3682 pckd_hw_max = dss_feat_get_param_max(FEAT_PARAM_DSS_PCD); 3683 3684 lck_max = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); 3685 3686 pck_min = pck_min ? pck_min : 1; 3687 pck_max = pck_max ? pck_max : ULONG_MAX; 3688 3689 lckd_start = max(DIV_ROUND_UP(dispc, lck_max), 1ul); 3690 lckd_stop = min(dispc / pck_min, 255ul); 3691 3692 for (lckd = lckd_start; lckd <= lckd_stop; ++lckd) { 3693 lck = dispc / lckd; 3694 3695 pckd_start = max(DIV_ROUND_UP(lck, pck_max), pckd_hw_min); 3696 pckd_stop = min(lck / pck_min, pckd_hw_max); 3697 3698 for (pckd = pckd_start; pckd <= pckd_stop; ++pckd) { 3699 pck = lck / pckd; 3700 3701 /* 3702 * For OMAP2/3 the DISPC fclk is the same as LCD's logic 3703 * clock, which means we're configuring DISPC fclk here 3704 * also. Thus we need to use the calculated lck. For 3705 * OMAP4+ the DISPC fclk is a separate clock. 3706 */ 3707 if (dss_has_feature(FEAT_CORE_CLK_DIV)) 3708 fck = dispc_core_clk_rate(); 3709 else 3710 fck = lck; 3711 3712 if (fck < pck * min_fck_per_pck) 3713 continue; 3714 3715 if (func(lckd, pckd, lck, pck, data)) 3716 return true; 3717 } 3718 } 3719 3720 return false; 3721 } 3722 3723 void dispc_mgr_set_clock_div(enum omap_channel channel, 3724 const struct dispc_clock_info *cinfo) 3725 { 3726 DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div); 3727 DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div); 3728 3729 dispc_mgr_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div); 3730 } 3731 3732 int dispc_mgr_get_clock_div(enum omap_channel channel, 3733 struct dispc_clock_info *cinfo) 3734 { 3735 unsigned long fck; 3736 3737 fck = dispc_fclk_rate(); 3738 3739 cinfo->lck_div = REG_GET(DISPC_DIVISORo(channel), 23, 16); 3740 cinfo->pck_div = REG_GET(DISPC_DIVISORo(channel), 7, 0); 3741 3742 cinfo->lck = fck / cinfo->lck_div; 3743 cinfo->pck = cinfo->lck / cinfo->pck_div; 3744 3745 return 0; 3746 } 3747 3748 u32 dispc_read_irqstatus(void) 3749 { 3750 return dispc_read_reg(DISPC_IRQSTATUS); 3751 } 3752 EXPORT_SYMBOL(dispc_read_irqstatus); 3753 3754 void dispc_clear_irqstatus(u32 mask) 3755 { 3756 dispc_write_reg(DISPC_IRQSTATUS, mask); 3757 } 3758 EXPORT_SYMBOL(dispc_clear_irqstatus); 3759 3760 u32 dispc_read_irqenable(void) 3761 { 3762 return dispc_read_reg(DISPC_IRQENABLE); 3763 } 3764 EXPORT_SYMBOL(dispc_read_irqenable); 3765 3766 void dispc_write_irqenable(u32 mask) 3767 { 3768 u32 old_mask = dispc_read_reg(DISPC_IRQENABLE); 3769 3770 /* clear the irqstatus for newly enabled irqs */ 3771 dispc_clear_irqstatus((mask ^ old_mask) & mask); 3772 3773 dispc_write_reg(DISPC_IRQENABLE, mask); 3774 } 3775 EXPORT_SYMBOL(dispc_write_irqenable); 3776 3777 void dispc_enable_sidle(void) 3778 { 3779 REG_FLD_MOD(DISPC_SYSCONFIG, 2, 4, 3); /* SIDLEMODE: smart idle */ 3780 } 3781 3782 void dispc_disable_sidle(void) 3783 { 3784 REG_FLD_MOD(DISPC_SYSCONFIG, 1, 4, 3); /* SIDLEMODE: no idle */ 3785 } 3786 3787 u32 dispc_mgr_gamma_size(enum omap_channel channel) 3788 { 3789 const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; 3790 3791 if (!dispc.feat->has_gamma_table) 3792 return 0; 3793 3794 return gdesc->len; 3795 } 3796 EXPORT_SYMBOL(dispc_mgr_gamma_size); 3797 3798 static void dispc_mgr_write_gamma_table(enum omap_channel channel) 3799 { 3800 const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; 3801 u32 *table = dispc.gamma_table[channel]; 3802 unsigned int i; 3803 3804 DSSDBG("%s: channel %d\n", __func__, channel); 3805 3806 for (i = 0; i < gdesc->len; ++i) { 3807 u32 v = table[i]; 3808 3809 if (gdesc->has_index) 3810 v |= i << 24; 3811 else if (i == 0) 3812 v |= 1 << 31; 3813 3814 dispc_write_reg(gdesc->reg, v); 3815 } 3816 } 3817 3818 static void dispc_restore_gamma_tables(void) 3819 { 3820 DSSDBG("%s()\n", __func__); 3821 3822 if (!dispc.feat->has_gamma_table) 3823 return; 3824 3825 dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD); 3826 3827 dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_DIGIT); 3828 3829 if (dss_has_feature(FEAT_MGR_LCD2)) 3830 dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD2); 3831 3832 if (dss_has_feature(FEAT_MGR_LCD3)) 3833 dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD3); 3834 } 3835 3836 static const struct drm_color_lut dispc_mgr_gamma_default_lut[] = { 3837 { .red = 0, .green = 0, .blue = 0, }, 3838 { .red = U16_MAX, .green = U16_MAX, .blue = U16_MAX, }, 3839 }; 3840 3841 void dispc_mgr_set_gamma(enum omap_channel channel, 3842 const struct drm_color_lut *lut, 3843 unsigned int length) 3844 { 3845 const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; 3846 u32 *table = dispc.gamma_table[channel]; 3847 uint i; 3848 3849 DSSDBG("%s: channel %d, lut len %u, hw len %u\n", __func__, 3850 channel, length, gdesc->len); 3851 3852 if (!dispc.feat->has_gamma_table) 3853 return; 3854 3855 if (lut == NULL || length < 2) { 3856 lut = dispc_mgr_gamma_default_lut; 3857 length = ARRAY_SIZE(dispc_mgr_gamma_default_lut); 3858 } 3859 3860 for (i = 0; i < length - 1; ++i) { 3861 uint first = i * (gdesc->len - 1) / (length - 1); 3862 uint last = (i + 1) * (gdesc->len - 1) / (length - 1); 3863 uint w = last - first; 3864 u16 r, g, b; 3865 uint j; 3866 3867 if (w == 0) 3868 continue; 3869 3870 for (j = 0; j <= w; j++) { 3871 r = (lut[i].red * (w - j) + lut[i+1].red * j) / w; 3872 g = (lut[i].green * (w - j) + lut[i+1].green * j) / w; 3873 b = (lut[i].blue * (w - j) + lut[i+1].blue * j) / w; 3874 3875 r >>= 16 - gdesc->bits; 3876 g >>= 16 - gdesc->bits; 3877 b >>= 16 - gdesc->bits; 3878 3879 table[first + j] = (r << (gdesc->bits * 2)) | 3880 (g << gdesc->bits) | b; 3881 } 3882 } 3883 3884 if (dispc.is_enabled) 3885 dispc_mgr_write_gamma_table(channel); 3886 } 3887 EXPORT_SYMBOL(dispc_mgr_set_gamma); 3888 3889 static int dispc_init_gamma_tables(void) 3890 { 3891 int channel; 3892 3893 if (!dispc.feat->has_gamma_table) 3894 return 0; 3895 3896 for (channel = 0; channel < ARRAY_SIZE(dispc.gamma_table); channel++) { 3897 const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; 3898 u32 *gt; 3899 3900 if (channel == OMAP_DSS_CHANNEL_LCD2 && 3901 !dss_has_feature(FEAT_MGR_LCD2)) 3902 continue; 3903 3904 if (channel == OMAP_DSS_CHANNEL_LCD3 && 3905 !dss_has_feature(FEAT_MGR_LCD3)) 3906 continue; 3907 3908 gt = devm_kmalloc_array(&dispc.pdev->dev, gdesc->len, 3909 sizeof(u32), GFP_KERNEL); 3910 if (!gt) 3911 return -ENOMEM; 3912 3913 dispc.gamma_table[channel] = gt; 3914 3915 dispc_mgr_set_gamma(channel, NULL, 0); 3916 } 3917 return 0; 3918 } 3919 3920 static void _omap_dispc_initial_config(void) 3921 { 3922 u32 l; 3923 3924 /* Exclusively enable DISPC_CORE_CLK and set divider to 1 */ 3925 if (dss_has_feature(FEAT_CORE_CLK_DIV)) { 3926 l = dispc_read_reg(DISPC_DIVISOR); 3927 /* Use DISPC_DIVISOR.LCD, instead of DISPC_DIVISOR1.LCD */ 3928 l = FLD_MOD(l, 1, 0, 0); 3929 l = FLD_MOD(l, 1, 23, 16); 3930 dispc_write_reg(DISPC_DIVISOR, l); 3931 3932 dispc.core_clk_rate = dispc_fclk_rate(); 3933 } 3934 3935 /* Use gamma table mode, instead of palette mode */ 3936 if (dispc.feat->has_gamma_table) 3937 REG_FLD_MOD(DISPC_CONFIG, 1, 3, 3); 3938 3939 /* For older DSS versions (FEAT_FUNCGATED) this enables 3940 * func-clock auto-gating. For newer versions 3941 * (dispc.feat->has_gamma_table) this enables tv-out gamma tables. 3942 */ 3943 if (dss_has_feature(FEAT_FUNCGATED) || dispc.feat->has_gamma_table) 3944 REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); 3945 3946 dispc_setup_color_conv_coef(); 3947 3948 dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY); 3949 3950 dispc_init_fifos(); 3951 3952 dispc_configure_burst_sizes(); 3953 3954 dispc_ovl_enable_zorder_planes(); 3955 3956 if (dispc.feat->mstandby_workaround) 3957 REG_FLD_MOD(DISPC_MSTANDBY_CTRL, 1, 0, 0); 3958 3959 if (dss_has_feature(FEAT_MFLAG)) 3960 dispc_init_mflag(); 3961 } 3962 3963 static const struct dispc_features omap24xx_dispc_feats = { 3964 .sw_start = 5, 3965 .fp_start = 15, 3966 .bp_start = 27, 3967 .sw_max = 64, 3968 .vp_max = 255, 3969 .hp_max = 256, 3970 .mgr_width_start = 10, 3971 .mgr_height_start = 26, 3972 .mgr_width_max = 2048, 3973 .mgr_height_max = 2048, 3974 .max_lcd_pclk = 66500000, 3975 .calc_scaling = dispc_ovl_calc_scaling_24xx, 3976 .calc_core_clk = calc_core_clk_24xx, 3977 .num_fifos = 3, 3978 .no_framedone_tv = true, 3979 .set_max_preload = false, 3980 .last_pixel_inc_missing = true, 3981 }; 3982 3983 static const struct dispc_features omap34xx_rev1_0_dispc_feats = { 3984 .sw_start = 5, 3985 .fp_start = 15, 3986 .bp_start = 27, 3987 .sw_max = 64, 3988 .vp_max = 255, 3989 .hp_max = 256, 3990 .mgr_width_start = 10, 3991 .mgr_height_start = 26, 3992 .mgr_width_max = 2048, 3993 .mgr_height_max = 2048, 3994 .max_lcd_pclk = 173000000, 3995 .max_tv_pclk = 59000000, 3996 .calc_scaling = dispc_ovl_calc_scaling_34xx, 3997 .calc_core_clk = calc_core_clk_34xx, 3998 .num_fifos = 3, 3999 .no_framedone_tv = true, 4000 .set_max_preload = false, 4001 .last_pixel_inc_missing = true, 4002 }; 4003 4004 static const struct dispc_features omap34xx_rev3_0_dispc_feats = { 4005 .sw_start = 7, 4006 .fp_start = 19, 4007 .bp_start = 31, 4008 .sw_max = 256, 4009 .vp_max = 4095, 4010 .hp_max = 4096, 4011 .mgr_width_start = 10, 4012 .mgr_height_start = 26, 4013 .mgr_width_max = 2048, 4014 .mgr_height_max = 2048, 4015 .max_lcd_pclk = 173000000, 4016 .max_tv_pclk = 59000000, 4017 .calc_scaling = dispc_ovl_calc_scaling_34xx, 4018 .calc_core_clk = calc_core_clk_34xx, 4019 .num_fifos = 3, 4020 .no_framedone_tv = true, 4021 .set_max_preload = false, 4022 .last_pixel_inc_missing = true, 4023 }; 4024 4025 static const struct dispc_features omap44xx_dispc_feats = { 4026 .sw_start = 7, 4027 .fp_start = 19, 4028 .bp_start = 31, 4029 .sw_max = 256, 4030 .vp_max = 4095, 4031 .hp_max = 4096, 4032 .mgr_width_start = 10, 4033 .mgr_height_start = 26, 4034 .mgr_width_max = 2048, 4035 .mgr_height_max = 2048, 4036 .max_lcd_pclk = 170000000, 4037 .max_tv_pclk = 185625000, 4038 .calc_scaling = dispc_ovl_calc_scaling_44xx, 4039 .calc_core_clk = calc_core_clk_44xx, 4040 .num_fifos = 5, 4041 .gfx_fifo_workaround = true, 4042 .set_max_preload = true, 4043 .supports_sync_align = true, 4044 .has_writeback = true, 4045 .supports_double_pixel = true, 4046 .reverse_ilace_field_order = true, 4047 .has_gamma_table = true, 4048 .has_gamma_i734_bug = true, 4049 }; 4050 4051 static const struct dispc_features omap54xx_dispc_feats = { 4052 .sw_start = 7, 4053 .fp_start = 19, 4054 .bp_start = 31, 4055 .sw_max = 256, 4056 .vp_max = 4095, 4057 .hp_max = 4096, 4058 .mgr_width_start = 11, 4059 .mgr_height_start = 27, 4060 .mgr_width_max = 4096, 4061 .mgr_height_max = 4096, 4062 .max_lcd_pclk = 170000000, 4063 .max_tv_pclk = 186000000, 4064 .calc_scaling = dispc_ovl_calc_scaling_44xx, 4065 .calc_core_clk = calc_core_clk_44xx, 4066 .num_fifos = 5, 4067 .gfx_fifo_workaround = true, 4068 .mstandby_workaround = true, 4069 .set_max_preload = true, 4070 .supports_sync_align = true, 4071 .has_writeback = true, 4072 .supports_double_pixel = true, 4073 .reverse_ilace_field_order = true, 4074 .has_gamma_table = true, 4075 .has_gamma_i734_bug = true, 4076 }; 4077 4078 static int dispc_init_features(struct platform_device *pdev) 4079 { 4080 const struct dispc_features *src; 4081 struct dispc_features *dst; 4082 4083 dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL); 4084 if (!dst) { 4085 dev_err(&pdev->dev, "Failed to allocate DISPC Features\n"); 4086 return -ENOMEM; 4087 } 4088 4089 switch (omapdss_get_version()) { 4090 case OMAPDSS_VER_OMAP24xx: 4091 src = &omap24xx_dispc_feats; 4092 break; 4093 4094 case OMAPDSS_VER_OMAP34xx_ES1: 4095 src = &omap34xx_rev1_0_dispc_feats; 4096 break; 4097 4098 case OMAPDSS_VER_OMAP34xx_ES3: 4099 case OMAPDSS_VER_OMAP3630: 4100 case OMAPDSS_VER_AM35xx: 4101 case OMAPDSS_VER_AM43xx: 4102 src = &omap34xx_rev3_0_dispc_feats; 4103 break; 4104 4105 case OMAPDSS_VER_OMAP4430_ES1: 4106 case OMAPDSS_VER_OMAP4430_ES2: 4107 case OMAPDSS_VER_OMAP4: 4108 src = &omap44xx_dispc_feats; 4109 break; 4110 4111 case OMAPDSS_VER_OMAP5: 4112 case OMAPDSS_VER_DRA7xx: 4113 src = &omap54xx_dispc_feats; 4114 break; 4115 4116 default: 4117 return -ENODEV; 4118 } 4119 4120 memcpy(dst, src, sizeof(*dst)); 4121 dispc.feat = dst; 4122 4123 return 0; 4124 } 4125 4126 static irqreturn_t dispc_irq_handler(int irq, void *arg) 4127 { 4128 if (!dispc.is_enabled) 4129 return IRQ_NONE; 4130 4131 return dispc.user_handler(irq, dispc.user_data); 4132 } 4133 4134 int dispc_request_irq(irq_handler_t handler, void *dev_id) 4135 { 4136 int r; 4137 4138 if (dispc.user_handler != NULL) 4139 return -EBUSY; 4140 4141 dispc.user_handler = handler; 4142 dispc.user_data = dev_id; 4143 4144 /* ensure the dispc_irq_handler sees the values above */ 4145 smp_wmb(); 4146 4147 r = devm_request_irq(&dispc.pdev->dev, dispc.irq, dispc_irq_handler, 4148 IRQF_SHARED, "OMAP DISPC", &dispc); 4149 if (r) { 4150 dispc.user_handler = NULL; 4151 dispc.user_data = NULL; 4152 } 4153 4154 return r; 4155 } 4156 EXPORT_SYMBOL(dispc_request_irq); 4157 4158 void dispc_free_irq(void *dev_id) 4159 { 4160 devm_free_irq(&dispc.pdev->dev, dispc.irq, &dispc); 4161 4162 dispc.user_handler = NULL; 4163 dispc.user_data = NULL; 4164 } 4165 EXPORT_SYMBOL(dispc_free_irq); 4166 4167 /* 4168 * Workaround for errata i734 in DSS dispc 4169 * - LCD1 Gamma Correction Is Not Working When GFX Pipe Is Disabled 4170 * 4171 * For gamma tables to work on LCD1 the GFX plane has to be used at 4172 * least once after DSS HW has come out of reset. The workaround 4173 * sets up a minimal LCD setup with GFX plane and waits for one 4174 * vertical sync irq before disabling the setup and continuing with 4175 * the context restore. The physical outputs are gated during the 4176 * operation. This workaround requires that gamma table's LOADMODE 4177 * is set to 0x2 in DISPC_CONTROL1 register. 4178 * 4179 * For details see: 4180 * OMAP543x Multimedia Device Silicon Revision 2.0 Silicon Errata 4181 * Literature Number: SWPZ037E 4182 * Or some other relevant errata document for the DSS IP version. 4183 */ 4184 4185 static const struct dispc_errata_i734_data { 4186 struct videomode vm; 4187 struct omap_overlay_info ovli; 4188 struct omap_overlay_manager_info mgri; 4189 struct dss_lcd_mgr_config lcd_conf; 4190 } i734 = { 4191 .vm = { 4192 .hactive = 8, .vactive = 1, 4193 .pixelclock = 16000000, 4194 .hsync_len = 8, .hfront_porch = 4, .hback_porch = 4, 4195 .vsync_len = 1, .vfront_porch = 1, .vback_porch = 1, 4196 4197 .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | 4198 DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_SYNC_POSEDGE | 4199 DISPLAY_FLAGS_PIXDATA_POSEDGE, 4200 }, 4201 .ovli = { 4202 .screen_width = 1, 4203 .width = 1, .height = 1, 4204 .color_mode = OMAP_DSS_COLOR_RGB24U, 4205 .rotation = OMAP_DSS_ROT_0, 4206 .rotation_type = OMAP_DSS_ROT_DMA, 4207 .mirror = 0, 4208 .pos_x = 0, .pos_y = 0, 4209 .out_width = 0, .out_height = 0, 4210 .global_alpha = 0xff, 4211 .pre_mult_alpha = 0, 4212 .zorder = 0, 4213 }, 4214 .mgri = { 4215 .default_color = 0, 4216 .trans_enabled = false, 4217 .partial_alpha_enabled = false, 4218 .cpr_enable = false, 4219 }, 4220 .lcd_conf = { 4221 .io_pad_mode = DSS_IO_PAD_MODE_BYPASS, 4222 .stallmode = false, 4223 .fifohandcheck = false, 4224 .clock_info = { 4225 .lck_div = 1, 4226 .pck_div = 2, 4227 }, 4228 .video_port_width = 24, 4229 .lcden_sig_polarity = 0, 4230 }, 4231 }; 4232 4233 static struct i734_buf { 4234 size_t size; 4235 dma_addr_t paddr; 4236 void *vaddr; 4237 } i734_buf; 4238 4239 static int dispc_errata_i734_wa_init(void) 4240 { 4241 if (!dispc.feat->has_gamma_i734_bug) 4242 return 0; 4243 4244 i734_buf.size = i734.ovli.width * i734.ovli.height * 4245 color_mode_to_bpp(i734.ovli.color_mode) / 8; 4246 4247 i734_buf.vaddr = dma_alloc_writecombine(&dispc.pdev->dev, i734_buf.size, 4248 &i734_buf.paddr, GFP_KERNEL); 4249 if (!i734_buf.vaddr) { 4250 dev_err(&dispc.pdev->dev, "%s: dma_alloc_writecombine failed", 4251 __func__); 4252 return -ENOMEM; 4253 } 4254 4255 return 0; 4256 } 4257 4258 static void dispc_errata_i734_wa_fini(void) 4259 { 4260 if (!dispc.feat->has_gamma_i734_bug) 4261 return; 4262 4263 dma_free_writecombine(&dispc.pdev->dev, i734_buf.size, i734_buf.vaddr, 4264 i734_buf.paddr); 4265 } 4266 4267 static void dispc_errata_i734_wa(void) 4268 { 4269 u32 framedone_irq = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_LCD); 4270 struct omap_overlay_info ovli; 4271 struct dss_lcd_mgr_config lcd_conf; 4272 u32 gatestate; 4273 unsigned int count; 4274 4275 if (!dispc.feat->has_gamma_i734_bug) 4276 return; 4277 4278 gatestate = REG_GET(DISPC_CONFIG, 8, 4); 4279 4280 ovli = i734.ovli; 4281 ovli.paddr = i734_buf.paddr; 4282 lcd_conf = i734.lcd_conf; 4283 4284 /* Gate all LCD1 outputs */ 4285 REG_FLD_MOD(DISPC_CONFIG, 0x1f, 8, 4); 4286 4287 /* Setup and enable GFX plane */ 4288 dispc_ovl_set_channel_out(OMAP_DSS_GFX, OMAP_DSS_CHANNEL_LCD); 4289 dispc_ovl_setup(OMAP_DSS_GFX, &ovli, false, &i734.vm, false); 4290 dispc_ovl_enable(OMAP_DSS_GFX, true); 4291 4292 /* Set up and enable display manager for LCD1 */ 4293 dispc_mgr_setup(OMAP_DSS_CHANNEL_LCD, &i734.mgri); 4294 dispc_calc_clock_rates(dss_get_dispc_clk_rate(), 4295 &lcd_conf.clock_info); 4296 dispc_mgr_set_lcd_config(OMAP_DSS_CHANNEL_LCD, &lcd_conf); 4297 dispc_mgr_set_timings(OMAP_DSS_CHANNEL_LCD, &i734.vm); 4298 4299 dispc_clear_irqstatus(framedone_irq); 4300 4301 /* Enable and shut the channel to produce just one frame */ 4302 dispc_mgr_enable(OMAP_DSS_CHANNEL_LCD, true); 4303 dispc_mgr_enable(OMAP_DSS_CHANNEL_LCD, false); 4304 4305 /* Busy wait for framedone. We can't fiddle with irq handlers 4306 * in PM resume. Typically the loop runs less than 5 times and 4307 * waits less than a micro second. 4308 */ 4309 count = 0; 4310 while (!(dispc_read_irqstatus() & framedone_irq)) { 4311 if (count++ > 10000) { 4312 dev_err(&dispc.pdev->dev, "%s: framedone timeout\n", 4313 __func__); 4314 break; 4315 } 4316 } 4317 dispc_ovl_enable(OMAP_DSS_GFX, false); 4318 4319 /* Clear all irq bits before continuing */ 4320 dispc_clear_irqstatus(0xffffffff); 4321 4322 /* Restore the original state to LCD1 output gates */ 4323 REG_FLD_MOD(DISPC_CONFIG, gatestate, 8, 4); 4324 } 4325 4326 /* DISPC HW IP initialisation */ 4327 static int dispc_bind(struct device *dev, struct device *master, void *data) 4328 { 4329 struct platform_device *pdev = to_platform_device(dev); 4330 u32 rev; 4331 int r = 0; 4332 struct resource *dispc_mem; 4333 struct device_node *np = pdev->dev.of_node; 4334 4335 dispc.pdev = pdev; 4336 4337 spin_lock_init(&dispc.control_lock); 4338 4339 r = dispc_init_features(dispc.pdev); 4340 if (r) 4341 return r; 4342 4343 r = dispc_errata_i734_wa_init(); 4344 if (r) 4345 return r; 4346 4347 dispc_mem = platform_get_resource(dispc.pdev, IORESOURCE_MEM, 0); 4348 if (!dispc_mem) { 4349 DSSERR("can't get IORESOURCE_MEM DISPC\n"); 4350 return -EINVAL; 4351 } 4352 4353 dispc.base = devm_ioremap(&pdev->dev, dispc_mem->start, 4354 resource_size(dispc_mem)); 4355 if (!dispc.base) { 4356 DSSERR("can't ioremap DISPC\n"); 4357 return -ENOMEM; 4358 } 4359 4360 dispc.irq = platform_get_irq(dispc.pdev, 0); 4361 if (dispc.irq < 0) { 4362 DSSERR("platform_get_irq failed\n"); 4363 return -ENODEV; 4364 } 4365 4366 if (np && of_property_read_bool(np, "syscon-pol")) { 4367 dispc.syscon_pol = syscon_regmap_lookup_by_phandle(np, "syscon-pol"); 4368 if (IS_ERR(dispc.syscon_pol)) { 4369 dev_err(&pdev->dev, "failed to get syscon-pol regmap\n"); 4370 return PTR_ERR(dispc.syscon_pol); 4371 } 4372 4373 if (of_property_read_u32_index(np, "syscon-pol", 1, 4374 &dispc.syscon_pol_offset)) { 4375 dev_err(&pdev->dev, "failed to get syscon-pol offset\n"); 4376 return -EINVAL; 4377 } 4378 } 4379 4380 r = dispc_init_gamma_tables(); 4381 if (r) 4382 return r; 4383 4384 pm_runtime_enable(&pdev->dev); 4385 4386 r = dispc_runtime_get(); 4387 if (r) 4388 goto err_runtime_get; 4389 4390 _omap_dispc_initial_config(); 4391 4392 rev = dispc_read_reg(DISPC_REVISION); 4393 dev_dbg(&pdev->dev, "OMAP DISPC rev %d.%d\n", 4394 FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); 4395 4396 dispc_runtime_put(); 4397 4398 dss_debugfs_create_file("dispc", dispc_dump_regs); 4399 4400 return 0; 4401 4402 err_runtime_get: 4403 pm_runtime_disable(&pdev->dev); 4404 return r; 4405 } 4406 4407 static void dispc_unbind(struct device *dev, struct device *master, 4408 void *data) 4409 { 4410 pm_runtime_disable(dev); 4411 4412 dispc_errata_i734_wa_fini(); 4413 } 4414 4415 static const struct component_ops dispc_component_ops = { 4416 .bind = dispc_bind, 4417 .unbind = dispc_unbind, 4418 }; 4419 4420 static int dispc_probe(struct platform_device *pdev) 4421 { 4422 return component_add(&pdev->dev, &dispc_component_ops); 4423 } 4424 4425 static int dispc_remove(struct platform_device *pdev) 4426 { 4427 component_del(&pdev->dev, &dispc_component_ops); 4428 return 0; 4429 } 4430 4431 static int dispc_runtime_suspend(struct device *dev) 4432 { 4433 dispc.is_enabled = false; 4434 /* ensure the dispc_irq_handler sees the is_enabled value */ 4435 smp_wmb(); 4436 /* wait for current handler to finish before turning the DISPC off */ 4437 synchronize_irq(dispc.irq); 4438 4439 dispc_save_context(); 4440 4441 return 0; 4442 } 4443 4444 static int dispc_runtime_resume(struct device *dev) 4445 { 4446 /* 4447 * The reset value for load mode is 0 (OMAP_DSS_LOAD_CLUT_AND_FRAME) 4448 * but we always initialize it to 2 (OMAP_DSS_LOAD_FRAME_ONLY) in 4449 * _omap_dispc_initial_config(). We can thus use it to detect if 4450 * we have lost register context. 4451 */ 4452 if (REG_GET(DISPC_CONFIG, 2, 1) != OMAP_DSS_LOAD_FRAME_ONLY) { 4453 _omap_dispc_initial_config(); 4454 4455 dispc_errata_i734_wa(); 4456 4457 dispc_restore_context(); 4458 4459 dispc_restore_gamma_tables(); 4460 } 4461 4462 dispc.is_enabled = true; 4463 /* ensure the dispc_irq_handler sees the is_enabled value */ 4464 smp_wmb(); 4465 4466 return 0; 4467 } 4468 4469 static const struct dev_pm_ops dispc_pm_ops = { 4470 .runtime_suspend = dispc_runtime_suspend, 4471 .runtime_resume = dispc_runtime_resume, 4472 }; 4473 4474 static const struct of_device_id dispc_of_match[] = { 4475 { .compatible = "ti,omap2-dispc", }, 4476 { .compatible = "ti,omap3-dispc", }, 4477 { .compatible = "ti,omap4-dispc", }, 4478 { .compatible = "ti,omap5-dispc", }, 4479 { .compatible = "ti,dra7-dispc", }, 4480 {}, 4481 }; 4482 4483 static struct platform_driver omap_dispchw_driver = { 4484 .probe = dispc_probe, 4485 .remove = dispc_remove, 4486 .driver = { 4487 .name = "omapdss_dispc", 4488 .pm = &dispc_pm_ops, 4489 .of_match_table = dispc_of_match, 4490 .suppress_bind_attrs = true, 4491 }, 4492 }; 4493 4494 int __init dispc_init_platform_driver(void) 4495 { 4496 return platform_driver_register(&omap_dispchw_driver); 4497 } 4498 4499 void dispc_uninit_platform_driver(void) 4500 { 4501 platform_driver_unregister(&omap_dispchw_driver); 4502 } 4503