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