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