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