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; 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->hactive + t->hfp + t->hsync_len + 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->hsync_len + 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->hactive; 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->hactive; 2823 int in_height = mgr_timings->vactive; 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 hsync_len, int hfp, int hbp, 3097 int vsw, int vfp, int vbp) 3098 { 3099 if (hsync_len < 1 || hsync_len > 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; 3114 else 3115 return pclk <= dispc.feat->max_tv_pclk; 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->hactive, timings->vactive)) 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->hsync_len, 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 hsync_len, 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(hsync_len-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.hactive, t.vactive); 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.hsync_len, t.hfp, t.hbp, 3271 t.vsw, t.vfp, t.vbp, t.vsync_level, 3272 t.hsync_level, t.data_pclk_edge, t.de_level, 3273 t.sync_pclk_edge); 3274 3275 xtot = t.hactive + t.hfp + t.hsync_len + t.hbp; 3276 ytot = t.vactive + t.vfp + t.vsw + t.vbp; 3277 3278 ht = timings->pixelclock / xtot; 3279 vt = timings->pixelclock / xtot / ytot; 3280 3281 DSSDBG("pck %u\n", timings->pixelclock); 3282 DSSDBG("hsync_len %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", 3283 t.hsync_len, t.hfp, t.hbp, t.vsw, t.vfp, t.vbp); 3284 DSSDBG("vsync_level %d hsync_level %d data_pclk_edge %d de_level %d sync_pclk_edge %d\n", 3285 t.vsync_level, t.hsync_level, t.data_pclk_edge, 3286 t.de_level, t.sync_pclk_edge); 3287 3288 DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); 3289 } else { 3290 if (t.interlace) 3291 t.vactive /= 2; 3292 3293 if (dispc.feat->supports_double_pixel) 3294 REG_FLD_MOD(DISPC_CONTROL, t.double_pixel ? 1 : 0, 3295 19, 17); 3296 } 3297 3298 dispc_mgr_set_size(channel, t.hactive, t.vactive); 3299 } 3300 EXPORT_SYMBOL(dispc_mgr_set_timings); 3301 3302 static void dispc_mgr_set_lcd_divisor(enum omap_channel channel, u16 lck_div, 3303 u16 pck_div) 3304 { 3305 BUG_ON(lck_div < 1); 3306 BUG_ON(pck_div < 1); 3307 3308 dispc_write_reg(DISPC_DIVISORo(channel), 3309 FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); 3310 3311 if (!dss_has_feature(FEAT_CORE_CLK_DIV) && 3312 channel == OMAP_DSS_CHANNEL_LCD) 3313 dispc.core_clk_rate = dispc_fclk_rate() / lck_div; 3314 } 3315 3316 static void dispc_mgr_get_lcd_divisor(enum omap_channel channel, int *lck_div, 3317 int *pck_div) 3318 { 3319 u32 l; 3320 l = dispc_read_reg(DISPC_DIVISORo(channel)); 3321 *lck_div = FLD_GET(l, 23, 16); 3322 *pck_div = FLD_GET(l, 7, 0); 3323 } 3324 3325 static unsigned long dispc_fclk_rate(void) 3326 { 3327 unsigned long r; 3328 enum dss_clk_source src; 3329 3330 src = dss_get_dispc_clk_source(); 3331 3332 if (src == DSS_CLK_SRC_FCK) { 3333 r = dss_get_dispc_clk_rate(); 3334 } else { 3335 struct dss_pll *pll; 3336 unsigned clkout_idx; 3337 3338 pll = dss_pll_find_by_src(src); 3339 clkout_idx = dss_pll_get_clkout_idx_for_src(src); 3340 3341 r = pll->cinfo.clkout[clkout_idx]; 3342 } 3343 3344 return r; 3345 } 3346 3347 static unsigned long dispc_mgr_lclk_rate(enum omap_channel channel) 3348 { 3349 int lcd; 3350 unsigned long r; 3351 enum dss_clk_source src; 3352 3353 /* for TV, LCLK rate is the FCLK rate */ 3354 if (!dss_mgr_is_lcd(channel)) 3355 return dispc_fclk_rate(); 3356 3357 src = dss_get_lcd_clk_source(channel); 3358 3359 if (src == DSS_CLK_SRC_FCK) { 3360 r = dss_get_dispc_clk_rate(); 3361 } else { 3362 struct dss_pll *pll; 3363 unsigned clkout_idx; 3364 3365 pll = dss_pll_find_by_src(src); 3366 clkout_idx = dss_pll_get_clkout_idx_for_src(src); 3367 3368 r = pll->cinfo.clkout[clkout_idx]; 3369 } 3370 3371 lcd = REG_GET(DISPC_DIVISORo(channel), 23, 16); 3372 3373 return r / lcd; 3374 } 3375 3376 static unsigned long dispc_mgr_pclk_rate(enum omap_channel channel) 3377 { 3378 unsigned long r; 3379 3380 if (dss_mgr_is_lcd(channel)) { 3381 int pcd; 3382 u32 l; 3383 3384 l = dispc_read_reg(DISPC_DIVISORo(channel)); 3385 3386 pcd = FLD_GET(l, 7, 0); 3387 3388 r = dispc_mgr_lclk_rate(channel); 3389 3390 return r / pcd; 3391 } else { 3392 return dispc.tv_pclk_rate; 3393 } 3394 } 3395 3396 void dispc_set_tv_pclk(unsigned long pclk) 3397 { 3398 dispc.tv_pclk_rate = pclk; 3399 } 3400 3401 static unsigned long dispc_core_clk_rate(void) 3402 { 3403 return dispc.core_clk_rate; 3404 } 3405 3406 static unsigned long dispc_plane_pclk_rate(enum omap_plane plane) 3407 { 3408 enum omap_channel channel; 3409 3410 if (plane == OMAP_DSS_WB) 3411 return 0; 3412 3413 channel = dispc_ovl_get_channel_out(plane); 3414 3415 return dispc_mgr_pclk_rate(channel); 3416 } 3417 3418 static unsigned long dispc_plane_lclk_rate(enum omap_plane plane) 3419 { 3420 enum omap_channel channel; 3421 3422 if (plane == OMAP_DSS_WB) 3423 return 0; 3424 3425 channel = dispc_ovl_get_channel_out(plane); 3426 3427 return dispc_mgr_lclk_rate(channel); 3428 } 3429 3430 static void dispc_dump_clocks_channel(struct seq_file *s, enum omap_channel channel) 3431 { 3432 int lcd, pcd; 3433 enum dss_clk_source lcd_clk_src; 3434 3435 seq_printf(s, "- %s -\n", mgr_desc[channel].name); 3436 3437 lcd_clk_src = dss_get_lcd_clk_source(channel); 3438 3439 seq_printf(s, "%s clk source = %s\n", mgr_desc[channel].name, 3440 dss_get_clk_source_name(lcd_clk_src)); 3441 3442 dispc_mgr_get_lcd_divisor(channel, &lcd, &pcd); 3443 3444 seq_printf(s, "lck\t\t%-16lulck div\t%u\n", 3445 dispc_mgr_lclk_rate(channel), lcd); 3446 seq_printf(s, "pck\t\t%-16lupck div\t%u\n", 3447 dispc_mgr_pclk_rate(channel), pcd); 3448 } 3449 3450 void dispc_dump_clocks(struct seq_file *s) 3451 { 3452 int lcd; 3453 u32 l; 3454 enum dss_clk_source dispc_clk_src = dss_get_dispc_clk_source(); 3455 3456 if (dispc_runtime_get()) 3457 return; 3458 3459 seq_printf(s, "- DISPC -\n"); 3460 3461 seq_printf(s, "dispc fclk source = %s\n", 3462 dss_get_clk_source_name(dispc_clk_src)); 3463 3464 seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate()); 3465 3466 if (dss_has_feature(FEAT_CORE_CLK_DIV)) { 3467 seq_printf(s, "- DISPC-CORE-CLK -\n"); 3468 l = dispc_read_reg(DISPC_DIVISOR); 3469 lcd = FLD_GET(l, 23, 16); 3470 3471 seq_printf(s, "lck\t\t%-16lulck div\t%u\n", 3472 (dispc_fclk_rate()/lcd), lcd); 3473 } 3474 3475 dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD); 3476 3477 if (dss_has_feature(FEAT_MGR_LCD2)) 3478 dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD2); 3479 if (dss_has_feature(FEAT_MGR_LCD3)) 3480 dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD3); 3481 3482 dispc_runtime_put(); 3483 } 3484 3485 static void dispc_dump_regs(struct seq_file *s) 3486 { 3487 int i, j; 3488 const char *mgr_names[] = { 3489 [OMAP_DSS_CHANNEL_LCD] = "LCD", 3490 [OMAP_DSS_CHANNEL_DIGIT] = "TV", 3491 [OMAP_DSS_CHANNEL_LCD2] = "LCD2", 3492 [OMAP_DSS_CHANNEL_LCD3] = "LCD3", 3493 }; 3494 const char *ovl_names[] = { 3495 [OMAP_DSS_GFX] = "GFX", 3496 [OMAP_DSS_VIDEO1] = "VID1", 3497 [OMAP_DSS_VIDEO2] = "VID2", 3498 [OMAP_DSS_VIDEO3] = "VID3", 3499 [OMAP_DSS_WB] = "WB", 3500 }; 3501 const char **p_names; 3502 3503 #define DUMPREG(r) seq_printf(s, "%-50s %08x\n", #r, dispc_read_reg(r)) 3504 3505 if (dispc_runtime_get()) 3506 return; 3507 3508 /* DISPC common registers */ 3509 DUMPREG(DISPC_REVISION); 3510 DUMPREG(DISPC_SYSCONFIG); 3511 DUMPREG(DISPC_SYSSTATUS); 3512 DUMPREG(DISPC_IRQSTATUS); 3513 DUMPREG(DISPC_IRQENABLE); 3514 DUMPREG(DISPC_CONTROL); 3515 DUMPREG(DISPC_CONFIG); 3516 DUMPREG(DISPC_CAPABLE); 3517 DUMPREG(DISPC_LINE_STATUS); 3518 DUMPREG(DISPC_LINE_NUMBER); 3519 if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) || 3520 dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) 3521 DUMPREG(DISPC_GLOBAL_ALPHA); 3522 if (dss_has_feature(FEAT_MGR_LCD2)) { 3523 DUMPREG(DISPC_CONTROL2); 3524 DUMPREG(DISPC_CONFIG2); 3525 } 3526 if (dss_has_feature(FEAT_MGR_LCD3)) { 3527 DUMPREG(DISPC_CONTROL3); 3528 DUMPREG(DISPC_CONFIG3); 3529 } 3530 if (dss_has_feature(FEAT_MFLAG)) 3531 DUMPREG(DISPC_GLOBAL_MFLAG_ATTRIBUTE); 3532 3533 #undef DUMPREG 3534 3535 #define DISPC_REG(i, name) name(i) 3536 #define DUMPREG(i, r) seq_printf(s, "%s(%s)%*s %08x\n", #r, p_names[i], \ 3537 (int)(48 - strlen(#r) - strlen(p_names[i])), " ", \ 3538 dispc_read_reg(DISPC_REG(i, r))) 3539 3540 p_names = mgr_names; 3541 3542 /* DISPC channel specific registers */ 3543 for (i = 0; i < dss_feat_get_num_mgrs(); i++) { 3544 DUMPREG(i, DISPC_DEFAULT_COLOR); 3545 DUMPREG(i, DISPC_TRANS_COLOR); 3546 DUMPREG(i, DISPC_SIZE_MGR); 3547 3548 if (i == OMAP_DSS_CHANNEL_DIGIT) 3549 continue; 3550 3551 DUMPREG(i, DISPC_TIMING_H); 3552 DUMPREG(i, DISPC_TIMING_V); 3553 DUMPREG(i, DISPC_POL_FREQ); 3554 DUMPREG(i, DISPC_DIVISORo); 3555 3556 DUMPREG(i, DISPC_DATA_CYCLE1); 3557 DUMPREG(i, DISPC_DATA_CYCLE2); 3558 DUMPREG(i, DISPC_DATA_CYCLE3); 3559 3560 if (dss_has_feature(FEAT_CPR)) { 3561 DUMPREG(i, DISPC_CPR_COEF_R); 3562 DUMPREG(i, DISPC_CPR_COEF_G); 3563 DUMPREG(i, DISPC_CPR_COEF_B); 3564 } 3565 } 3566 3567 p_names = ovl_names; 3568 3569 for (i = 0; i < dss_feat_get_num_ovls(); i++) { 3570 DUMPREG(i, DISPC_OVL_BA0); 3571 DUMPREG(i, DISPC_OVL_BA1); 3572 DUMPREG(i, DISPC_OVL_POSITION); 3573 DUMPREG(i, DISPC_OVL_SIZE); 3574 DUMPREG(i, DISPC_OVL_ATTRIBUTES); 3575 DUMPREG(i, DISPC_OVL_FIFO_THRESHOLD); 3576 DUMPREG(i, DISPC_OVL_FIFO_SIZE_STATUS); 3577 DUMPREG(i, DISPC_OVL_ROW_INC); 3578 DUMPREG(i, DISPC_OVL_PIXEL_INC); 3579 3580 if (dss_has_feature(FEAT_PRELOAD)) 3581 DUMPREG(i, DISPC_OVL_PRELOAD); 3582 if (dss_has_feature(FEAT_MFLAG)) 3583 DUMPREG(i, DISPC_OVL_MFLAG_THRESHOLD); 3584 3585 if (i == OMAP_DSS_GFX) { 3586 DUMPREG(i, DISPC_OVL_WINDOW_SKIP); 3587 DUMPREG(i, DISPC_OVL_TABLE_BA); 3588 continue; 3589 } 3590 3591 DUMPREG(i, DISPC_OVL_FIR); 3592 DUMPREG(i, DISPC_OVL_PICTURE_SIZE); 3593 DUMPREG(i, DISPC_OVL_ACCU0); 3594 DUMPREG(i, DISPC_OVL_ACCU1); 3595 if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { 3596 DUMPREG(i, DISPC_OVL_BA0_UV); 3597 DUMPREG(i, DISPC_OVL_BA1_UV); 3598 DUMPREG(i, DISPC_OVL_FIR2); 3599 DUMPREG(i, DISPC_OVL_ACCU2_0); 3600 DUMPREG(i, DISPC_OVL_ACCU2_1); 3601 } 3602 if (dss_has_feature(FEAT_ATTR2)) 3603 DUMPREG(i, DISPC_OVL_ATTRIBUTES2); 3604 } 3605 3606 if (dispc.feat->has_writeback) { 3607 i = OMAP_DSS_WB; 3608 DUMPREG(i, DISPC_OVL_BA0); 3609 DUMPREG(i, DISPC_OVL_BA1); 3610 DUMPREG(i, DISPC_OVL_SIZE); 3611 DUMPREG(i, DISPC_OVL_ATTRIBUTES); 3612 DUMPREG(i, DISPC_OVL_FIFO_THRESHOLD); 3613 DUMPREG(i, DISPC_OVL_FIFO_SIZE_STATUS); 3614 DUMPREG(i, DISPC_OVL_ROW_INC); 3615 DUMPREG(i, DISPC_OVL_PIXEL_INC); 3616 3617 if (dss_has_feature(FEAT_MFLAG)) 3618 DUMPREG(i, DISPC_OVL_MFLAG_THRESHOLD); 3619 3620 DUMPREG(i, DISPC_OVL_FIR); 3621 DUMPREG(i, DISPC_OVL_PICTURE_SIZE); 3622 DUMPREG(i, DISPC_OVL_ACCU0); 3623 DUMPREG(i, DISPC_OVL_ACCU1); 3624 if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { 3625 DUMPREG(i, DISPC_OVL_BA0_UV); 3626 DUMPREG(i, DISPC_OVL_BA1_UV); 3627 DUMPREG(i, DISPC_OVL_FIR2); 3628 DUMPREG(i, DISPC_OVL_ACCU2_0); 3629 DUMPREG(i, DISPC_OVL_ACCU2_1); 3630 } 3631 if (dss_has_feature(FEAT_ATTR2)) 3632 DUMPREG(i, DISPC_OVL_ATTRIBUTES2); 3633 } 3634 3635 #undef DISPC_REG 3636 #undef DUMPREG 3637 3638 #define DISPC_REG(plane, name, i) name(plane, i) 3639 #define DUMPREG(plane, name, i) \ 3640 seq_printf(s, "%s_%d(%s)%*s %08x\n", #name, i, p_names[plane], \ 3641 (int)(46 - strlen(#name) - strlen(p_names[plane])), " ", \ 3642 dispc_read_reg(DISPC_REG(plane, name, i))) 3643 3644 /* Video pipeline coefficient registers */ 3645 3646 /* start from OMAP_DSS_VIDEO1 */ 3647 for (i = 1; i < dss_feat_get_num_ovls(); i++) { 3648 for (j = 0; j < 8; j++) 3649 DUMPREG(i, DISPC_OVL_FIR_COEF_H, j); 3650 3651 for (j = 0; j < 8; j++) 3652 DUMPREG(i, DISPC_OVL_FIR_COEF_HV, j); 3653 3654 for (j = 0; j < 5; j++) 3655 DUMPREG(i, DISPC_OVL_CONV_COEF, j); 3656 3657 if (dss_has_feature(FEAT_FIR_COEF_V)) { 3658 for (j = 0; j < 8; j++) 3659 DUMPREG(i, DISPC_OVL_FIR_COEF_V, j); 3660 } 3661 3662 if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { 3663 for (j = 0; j < 8; j++) 3664 DUMPREG(i, DISPC_OVL_FIR_COEF_H2, j); 3665 3666 for (j = 0; j < 8; j++) 3667 DUMPREG(i, DISPC_OVL_FIR_COEF_HV2, j); 3668 3669 for (j = 0; j < 8; j++) 3670 DUMPREG(i, DISPC_OVL_FIR_COEF_V2, j); 3671 } 3672 } 3673 3674 dispc_runtime_put(); 3675 3676 #undef DISPC_REG 3677 #undef DUMPREG 3678 } 3679 3680 /* calculate clock rates using dividers in cinfo */ 3681 int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, 3682 struct dispc_clock_info *cinfo) 3683 { 3684 if (cinfo->lck_div > 255 || cinfo->lck_div == 0) 3685 return -EINVAL; 3686 if (cinfo->pck_div < 1 || cinfo->pck_div > 255) 3687 return -EINVAL; 3688 3689 cinfo->lck = dispc_fclk_rate / cinfo->lck_div; 3690 cinfo->pck = cinfo->lck / cinfo->pck_div; 3691 3692 return 0; 3693 } 3694 3695 bool dispc_div_calc(unsigned long dispc, 3696 unsigned long pck_min, unsigned long pck_max, 3697 dispc_div_calc_func func, void *data) 3698 { 3699 int lckd, lckd_start, lckd_stop; 3700 int pckd, pckd_start, pckd_stop; 3701 unsigned long pck, lck; 3702 unsigned long lck_max; 3703 unsigned long pckd_hw_min, pckd_hw_max; 3704 unsigned min_fck_per_pck; 3705 unsigned long fck; 3706 3707 #ifdef CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK 3708 min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK; 3709 #else 3710 min_fck_per_pck = 0; 3711 #endif 3712 3713 pckd_hw_min = dss_feat_get_param_min(FEAT_PARAM_DSS_PCD); 3714 pckd_hw_max = dss_feat_get_param_max(FEAT_PARAM_DSS_PCD); 3715 3716 lck_max = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); 3717 3718 pck_min = pck_min ? pck_min : 1; 3719 pck_max = pck_max ? pck_max : ULONG_MAX; 3720 3721 lckd_start = max(DIV_ROUND_UP(dispc, lck_max), 1ul); 3722 lckd_stop = min(dispc / pck_min, 255ul); 3723 3724 for (lckd = lckd_start; lckd <= lckd_stop; ++lckd) { 3725 lck = dispc / lckd; 3726 3727 pckd_start = max(DIV_ROUND_UP(lck, pck_max), pckd_hw_min); 3728 pckd_stop = min(lck / pck_min, pckd_hw_max); 3729 3730 for (pckd = pckd_start; pckd <= pckd_stop; ++pckd) { 3731 pck = lck / pckd; 3732 3733 /* 3734 * For OMAP2/3 the DISPC fclk is the same as LCD's logic 3735 * clock, which means we're configuring DISPC fclk here 3736 * also. Thus we need to use the calculated lck. For 3737 * OMAP4+ the DISPC fclk is a separate clock. 3738 */ 3739 if (dss_has_feature(FEAT_CORE_CLK_DIV)) 3740 fck = dispc_core_clk_rate(); 3741 else 3742 fck = lck; 3743 3744 if (fck < pck * min_fck_per_pck) 3745 continue; 3746 3747 if (func(lckd, pckd, lck, pck, data)) 3748 return true; 3749 } 3750 } 3751 3752 return false; 3753 } 3754 3755 void dispc_mgr_set_clock_div(enum omap_channel channel, 3756 const struct dispc_clock_info *cinfo) 3757 { 3758 DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div); 3759 DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div); 3760 3761 dispc_mgr_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div); 3762 } 3763 3764 int dispc_mgr_get_clock_div(enum omap_channel channel, 3765 struct dispc_clock_info *cinfo) 3766 { 3767 unsigned long fck; 3768 3769 fck = dispc_fclk_rate(); 3770 3771 cinfo->lck_div = REG_GET(DISPC_DIVISORo(channel), 23, 16); 3772 cinfo->pck_div = REG_GET(DISPC_DIVISORo(channel), 7, 0); 3773 3774 cinfo->lck = fck / cinfo->lck_div; 3775 cinfo->pck = cinfo->lck / cinfo->pck_div; 3776 3777 return 0; 3778 } 3779 3780 u32 dispc_read_irqstatus(void) 3781 { 3782 return dispc_read_reg(DISPC_IRQSTATUS); 3783 } 3784 EXPORT_SYMBOL(dispc_read_irqstatus); 3785 3786 void dispc_clear_irqstatus(u32 mask) 3787 { 3788 dispc_write_reg(DISPC_IRQSTATUS, mask); 3789 } 3790 EXPORT_SYMBOL(dispc_clear_irqstatus); 3791 3792 u32 dispc_read_irqenable(void) 3793 { 3794 return dispc_read_reg(DISPC_IRQENABLE); 3795 } 3796 EXPORT_SYMBOL(dispc_read_irqenable); 3797 3798 void dispc_write_irqenable(u32 mask) 3799 { 3800 u32 old_mask = dispc_read_reg(DISPC_IRQENABLE); 3801 3802 /* clear the irqstatus for newly enabled irqs */ 3803 dispc_clear_irqstatus((mask ^ old_mask) & mask); 3804 3805 dispc_write_reg(DISPC_IRQENABLE, mask); 3806 } 3807 EXPORT_SYMBOL(dispc_write_irqenable); 3808 3809 void dispc_enable_sidle(void) 3810 { 3811 REG_FLD_MOD(DISPC_SYSCONFIG, 2, 4, 3); /* SIDLEMODE: smart idle */ 3812 } 3813 3814 void dispc_disable_sidle(void) 3815 { 3816 REG_FLD_MOD(DISPC_SYSCONFIG, 1, 4, 3); /* SIDLEMODE: no idle */ 3817 } 3818 3819 u32 dispc_mgr_gamma_size(enum omap_channel channel) 3820 { 3821 const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; 3822 3823 if (!dispc.feat->has_gamma_table) 3824 return 0; 3825 3826 return gdesc->len; 3827 } 3828 EXPORT_SYMBOL(dispc_mgr_gamma_size); 3829 3830 static void dispc_mgr_write_gamma_table(enum omap_channel channel) 3831 { 3832 const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; 3833 u32 *table = dispc.gamma_table[channel]; 3834 unsigned int i; 3835 3836 DSSDBG("%s: channel %d\n", __func__, channel); 3837 3838 for (i = 0; i < gdesc->len; ++i) { 3839 u32 v = table[i]; 3840 3841 if (gdesc->has_index) 3842 v |= i << 24; 3843 else if (i == 0) 3844 v |= 1 << 31; 3845 3846 dispc_write_reg(gdesc->reg, v); 3847 } 3848 } 3849 3850 static void dispc_restore_gamma_tables(void) 3851 { 3852 DSSDBG("%s()\n", __func__); 3853 3854 if (!dispc.feat->has_gamma_table) 3855 return; 3856 3857 dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD); 3858 3859 dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_DIGIT); 3860 3861 if (dss_has_feature(FEAT_MGR_LCD2)) 3862 dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD2); 3863 3864 if (dss_has_feature(FEAT_MGR_LCD3)) 3865 dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD3); 3866 } 3867 3868 static const struct drm_color_lut dispc_mgr_gamma_default_lut[] = { 3869 { .red = 0, .green = 0, .blue = 0, }, 3870 { .red = U16_MAX, .green = U16_MAX, .blue = U16_MAX, }, 3871 }; 3872 3873 void dispc_mgr_set_gamma(enum omap_channel channel, 3874 const struct drm_color_lut *lut, 3875 unsigned int length) 3876 { 3877 const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; 3878 u32 *table = dispc.gamma_table[channel]; 3879 uint i; 3880 3881 DSSDBG("%s: channel %d, lut len %u, hw len %u\n", __func__, 3882 channel, length, gdesc->len); 3883 3884 if (!dispc.feat->has_gamma_table) 3885 return; 3886 3887 if (lut == NULL || length < 2) { 3888 lut = dispc_mgr_gamma_default_lut; 3889 length = ARRAY_SIZE(dispc_mgr_gamma_default_lut); 3890 } 3891 3892 for (i = 0; i < length - 1; ++i) { 3893 uint first = i * (gdesc->len - 1) / (length - 1); 3894 uint last = (i + 1) * (gdesc->len - 1) / (length - 1); 3895 uint w = last - first; 3896 u16 r, g, b; 3897 uint j; 3898 3899 if (w == 0) 3900 continue; 3901 3902 for (j = 0; j <= w; j++) { 3903 r = (lut[i].red * (w - j) + lut[i+1].red * j) / w; 3904 g = (lut[i].green * (w - j) + lut[i+1].green * j) / w; 3905 b = (lut[i].blue * (w - j) + lut[i+1].blue * j) / w; 3906 3907 r >>= 16 - gdesc->bits; 3908 g >>= 16 - gdesc->bits; 3909 b >>= 16 - gdesc->bits; 3910 3911 table[first + j] = (r << (gdesc->bits * 2)) | 3912 (g << gdesc->bits) | b; 3913 } 3914 } 3915 3916 if (dispc.is_enabled) 3917 dispc_mgr_write_gamma_table(channel); 3918 } 3919 EXPORT_SYMBOL(dispc_mgr_set_gamma); 3920 3921 static int dispc_init_gamma_tables(void) 3922 { 3923 int channel; 3924 3925 if (!dispc.feat->has_gamma_table) 3926 return 0; 3927 3928 for (channel = 0; channel < ARRAY_SIZE(dispc.gamma_table); channel++) { 3929 const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; 3930 u32 *gt; 3931 3932 if (channel == OMAP_DSS_CHANNEL_LCD2 && 3933 !dss_has_feature(FEAT_MGR_LCD2)) 3934 continue; 3935 3936 if (channel == OMAP_DSS_CHANNEL_LCD3 && 3937 !dss_has_feature(FEAT_MGR_LCD3)) 3938 continue; 3939 3940 gt = devm_kmalloc_array(&dispc.pdev->dev, gdesc->len, 3941 sizeof(u32), GFP_KERNEL); 3942 if (!gt) 3943 return -ENOMEM; 3944 3945 dispc.gamma_table[channel] = gt; 3946 3947 dispc_mgr_set_gamma(channel, NULL, 0); 3948 } 3949 return 0; 3950 } 3951 3952 static void _omap_dispc_initial_config(void) 3953 { 3954 u32 l; 3955 3956 /* Exclusively enable DISPC_CORE_CLK and set divider to 1 */ 3957 if (dss_has_feature(FEAT_CORE_CLK_DIV)) { 3958 l = dispc_read_reg(DISPC_DIVISOR); 3959 /* Use DISPC_DIVISOR.LCD, instead of DISPC_DIVISOR1.LCD */ 3960 l = FLD_MOD(l, 1, 0, 0); 3961 l = FLD_MOD(l, 1, 23, 16); 3962 dispc_write_reg(DISPC_DIVISOR, l); 3963 3964 dispc.core_clk_rate = dispc_fclk_rate(); 3965 } 3966 3967 /* Use gamma table mode, instead of palette mode */ 3968 if (dispc.feat->has_gamma_table) 3969 REG_FLD_MOD(DISPC_CONFIG, 1, 3, 3); 3970 3971 /* For older DSS versions (FEAT_FUNCGATED) this enables 3972 * func-clock auto-gating. For newer versions 3973 * (dispc.feat->has_gamma_table) this enables tv-out gamma tables. 3974 */ 3975 if (dss_has_feature(FEAT_FUNCGATED) || dispc.feat->has_gamma_table) 3976 REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); 3977 3978 dispc_setup_color_conv_coef(); 3979 3980 dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY); 3981 3982 dispc_init_fifos(); 3983 3984 dispc_configure_burst_sizes(); 3985 3986 dispc_ovl_enable_zorder_planes(); 3987 3988 if (dispc.feat->mstandby_workaround) 3989 REG_FLD_MOD(DISPC_MSTANDBY_CTRL, 1, 0, 0); 3990 3991 if (dss_has_feature(FEAT_MFLAG)) 3992 dispc_init_mflag(); 3993 } 3994 3995 static const struct dispc_features omap24xx_dispc_feats = { 3996 .sw_start = 5, 3997 .fp_start = 15, 3998 .bp_start = 27, 3999 .sw_max = 64, 4000 .vp_max = 255, 4001 .hp_max = 256, 4002 .mgr_width_start = 10, 4003 .mgr_height_start = 26, 4004 .mgr_width_max = 2048, 4005 .mgr_height_max = 2048, 4006 .max_lcd_pclk = 66500000, 4007 .calc_scaling = dispc_ovl_calc_scaling_24xx, 4008 .calc_core_clk = calc_core_clk_24xx, 4009 .num_fifos = 3, 4010 .no_framedone_tv = true, 4011 .set_max_preload = false, 4012 .last_pixel_inc_missing = true, 4013 }; 4014 4015 static const struct dispc_features omap34xx_rev1_0_dispc_feats = { 4016 .sw_start = 5, 4017 .fp_start = 15, 4018 .bp_start = 27, 4019 .sw_max = 64, 4020 .vp_max = 255, 4021 .hp_max = 256, 4022 .mgr_width_start = 10, 4023 .mgr_height_start = 26, 4024 .mgr_width_max = 2048, 4025 .mgr_height_max = 2048, 4026 .max_lcd_pclk = 173000000, 4027 .max_tv_pclk = 59000000, 4028 .calc_scaling = dispc_ovl_calc_scaling_34xx, 4029 .calc_core_clk = calc_core_clk_34xx, 4030 .num_fifos = 3, 4031 .no_framedone_tv = true, 4032 .set_max_preload = false, 4033 .last_pixel_inc_missing = true, 4034 }; 4035 4036 static const struct dispc_features omap34xx_rev3_0_dispc_feats = { 4037 .sw_start = 7, 4038 .fp_start = 19, 4039 .bp_start = 31, 4040 .sw_max = 256, 4041 .vp_max = 4095, 4042 .hp_max = 4096, 4043 .mgr_width_start = 10, 4044 .mgr_height_start = 26, 4045 .mgr_width_max = 2048, 4046 .mgr_height_max = 2048, 4047 .max_lcd_pclk = 173000000, 4048 .max_tv_pclk = 59000000, 4049 .calc_scaling = dispc_ovl_calc_scaling_34xx, 4050 .calc_core_clk = calc_core_clk_34xx, 4051 .num_fifos = 3, 4052 .no_framedone_tv = true, 4053 .set_max_preload = false, 4054 .last_pixel_inc_missing = true, 4055 }; 4056 4057 static const struct dispc_features omap44xx_dispc_feats = { 4058 .sw_start = 7, 4059 .fp_start = 19, 4060 .bp_start = 31, 4061 .sw_max = 256, 4062 .vp_max = 4095, 4063 .hp_max = 4096, 4064 .mgr_width_start = 10, 4065 .mgr_height_start = 26, 4066 .mgr_width_max = 2048, 4067 .mgr_height_max = 2048, 4068 .max_lcd_pclk = 170000000, 4069 .max_tv_pclk = 185625000, 4070 .calc_scaling = dispc_ovl_calc_scaling_44xx, 4071 .calc_core_clk = calc_core_clk_44xx, 4072 .num_fifos = 5, 4073 .gfx_fifo_workaround = true, 4074 .set_max_preload = true, 4075 .supports_sync_align = true, 4076 .has_writeback = true, 4077 .supports_double_pixel = true, 4078 .reverse_ilace_field_order = true, 4079 .has_gamma_table = true, 4080 .has_gamma_i734_bug = true, 4081 }; 4082 4083 static const struct dispc_features omap54xx_dispc_feats = { 4084 .sw_start = 7, 4085 .fp_start = 19, 4086 .bp_start = 31, 4087 .sw_max = 256, 4088 .vp_max = 4095, 4089 .hp_max = 4096, 4090 .mgr_width_start = 11, 4091 .mgr_height_start = 27, 4092 .mgr_width_max = 4096, 4093 .mgr_height_max = 4096, 4094 .max_lcd_pclk = 170000000, 4095 .max_tv_pclk = 186000000, 4096 .calc_scaling = dispc_ovl_calc_scaling_44xx, 4097 .calc_core_clk = calc_core_clk_44xx, 4098 .num_fifos = 5, 4099 .gfx_fifo_workaround = true, 4100 .mstandby_workaround = true, 4101 .set_max_preload = true, 4102 .supports_sync_align = true, 4103 .has_writeback = true, 4104 .supports_double_pixel = true, 4105 .reverse_ilace_field_order = true, 4106 .has_gamma_table = true, 4107 .has_gamma_i734_bug = true, 4108 }; 4109 4110 static int dispc_init_features(struct platform_device *pdev) 4111 { 4112 const struct dispc_features *src; 4113 struct dispc_features *dst; 4114 4115 dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL); 4116 if (!dst) { 4117 dev_err(&pdev->dev, "Failed to allocate DISPC Features\n"); 4118 return -ENOMEM; 4119 } 4120 4121 switch (omapdss_get_version()) { 4122 case OMAPDSS_VER_OMAP24xx: 4123 src = &omap24xx_dispc_feats; 4124 break; 4125 4126 case OMAPDSS_VER_OMAP34xx_ES1: 4127 src = &omap34xx_rev1_0_dispc_feats; 4128 break; 4129 4130 case OMAPDSS_VER_OMAP34xx_ES3: 4131 case OMAPDSS_VER_OMAP3630: 4132 case OMAPDSS_VER_AM35xx: 4133 case OMAPDSS_VER_AM43xx: 4134 src = &omap34xx_rev3_0_dispc_feats; 4135 break; 4136 4137 case OMAPDSS_VER_OMAP4430_ES1: 4138 case OMAPDSS_VER_OMAP4430_ES2: 4139 case OMAPDSS_VER_OMAP4: 4140 src = &omap44xx_dispc_feats; 4141 break; 4142 4143 case OMAPDSS_VER_OMAP5: 4144 case OMAPDSS_VER_DRA7xx: 4145 src = &omap54xx_dispc_feats; 4146 break; 4147 4148 default: 4149 return -ENODEV; 4150 } 4151 4152 memcpy(dst, src, sizeof(*dst)); 4153 dispc.feat = dst; 4154 4155 return 0; 4156 } 4157 4158 static irqreturn_t dispc_irq_handler(int irq, void *arg) 4159 { 4160 if (!dispc.is_enabled) 4161 return IRQ_NONE; 4162 4163 return dispc.user_handler(irq, dispc.user_data); 4164 } 4165 4166 int dispc_request_irq(irq_handler_t handler, void *dev_id) 4167 { 4168 int r; 4169 4170 if (dispc.user_handler != NULL) 4171 return -EBUSY; 4172 4173 dispc.user_handler = handler; 4174 dispc.user_data = dev_id; 4175 4176 /* ensure the dispc_irq_handler sees the values above */ 4177 smp_wmb(); 4178 4179 r = devm_request_irq(&dispc.pdev->dev, dispc.irq, dispc_irq_handler, 4180 IRQF_SHARED, "OMAP DISPC", &dispc); 4181 if (r) { 4182 dispc.user_handler = NULL; 4183 dispc.user_data = NULL; 4184 } 4185 4186 return r; 4187 } 4188 EXPORT_SYMBOL(dispc_request_irq); 4189 4190 void dispc_free_irq(void *dev_id) 4191 { 4192 devm_free_irq(&dispc.pdev->dev, dispc.irq, &dispc); 4193 4194 dispc.user_handler = NULL; 4195 dispc.user_data = NULL; 4196 } 4197 EXPORT_SYMBOL(dispc_free_irq); 4198 4199 /* 4200 * Workaround for errata i734 in DSS dispc 4201 * - LCD1 Gamma Correction Is Not Working When GFX Pipe Is Disabled 4202 * 4203 * For gamma tables to work on LCD1 the GFX plane has to be used at 4204 * least once after DSS HW has come out of reset. The workaround 4205 * sets up a minimal LCD setup with GFX plane and waits for one 4206 * vertical sync irq before disabling the setup and continuing with 4207 * the context restore. The physical outputs are gated during the 4208 * operation. This workaround requires that gamma table's LOADMODE 4209 * is set to 0x2 in DISPC_CONTROL1 register. 4210 * 4211 * For details see: 4212 * OMAP543x Multimedia Device Silicon Revision 2.0 Silicon Errata 4213 * Literature Number: SWPZ037E 4214 * Or some other relevant errata document for the DSS IP version. 4215 */ 4216 4217 static const struct dispc_errata_i734_data { 4218 struct omap_video_timings timings; 4219 struct omap_overlay_info ovli; 4220 struct omap_overlay_manager_info mgri; 4221 struct dss_lcd_mgr_config lcd_conf; 4222 } i734 = { 4223 .timings = { 4224 .hactive = 8, .vactive = 1, 4225 .pixelclock = 16000000, 4226 .hsync_len = 8, .hfp = 4, .hbp = 4, 4227 .vsw = 1, .vfp = 1, .vbp = 1, 4228 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, 4229 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, 4230 .interlace = false, 4231 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, 4232 .de_level = OMAPDSS_SIG_ACTIVE_HIGH, 4233 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, 4234 .double_pixel = false, 4235 }, 4236 .ovli = { 4237 .screen_width = 1, 4238 .width = 1, .height = 1, 4239 .color_mode = OMAP_DSS_COLOR_RGB24U, 4240 .rotation = OMAP_DSS_ROT_0, 4241 .rotation_type = OMAP_DSS_ROT_DMA, 4242 .mirror = 0, 4243 .pos_x = 0, .pos_y = 0, 4244 .out_width = 0, .out_height = 0, 4245 .global_alpha = 0xff, 4246 .pre_mult_alpha = 0, 4247 .zorder = 0, 4248 }, 4249 .mgri = { 4250 .default_color = 0, 4251 .trans_enabled = false, 4252 .partial_alpha_enabled = false, 4253 .cpr_enable = false, 4254 }, 4255 .lcd_conf = { 4256 .io_pad_mode = DSS_IO_PAD_MODE_BYPASS, 4257 .stallmode = false, 4258 .fifohandcheck = false, 4259 .clock_info = { 4260 .lck_div = 1, 4261 .pck_div = 2, 4262 }, 4263 .video_port_width = 24, 4264 .lcden_sig_polarity = 0, 4265 }, 4266 }; 4267 4268 static struct i734_buf { 4269 size_t size; 4270 dma_addr_t paddr; 4271 void *vaddr; 4272 } i734_buf; 4273 4274 static int dispc_errata_i734_wa_init(void) 4275 { 4276 if (!dispc.feat->has_gamma_i734_bug) 4277 return 0; 4278 4279 i734_buf.size = i734.ovli.width * i734.ovli.height * 4280 color_mode_to_bpp(i734.ovli.color_mode) / 8; 4281 4282 i734_buf.vaddr = dma_alloc_writecombine(&dispc.pdev->dev, i734_buf.size, 4283 &i734_buf.paddr, GFP_KERNEL); 4284 if (!i734_buf.vaddr) { 4285 dev_err(&dispc.pdev->dev, "%s: dma_alloc_writecombine failed", 4286 __func__); 4287 return -ENOMEM; 4288 } 4289 4290 return 0; 4291 } 4292 4293 static void dispc_errata_i734_wa_fini(void) 4294 { 4295 if (!dispc.feat->has_gamma_i734_bug) 4296 return; 4297 4298 dma_free_writecombine(&dispc.pdev->dev, i734_buf.size, i734_buf.vaddr, 4299 i734_buf.paddr); 4300 } 4301 4302 static void dispc_errata_i734_wa(void) 4303 { 4304 u32 framedone_irq = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_LCD); 4305 struct omap_overlay_info ovli; 4306 struct dss_lcd_mgr_config lcd_conf; 4307 u32 gatestate; 4308 unsigned int count; 4309 4310 if (!dispc.feat->has_gamma_i734_bug) 4311 return; 4312 4313 gatestate = REG_GET(DISPC_CONFIG, 8, 4); 4314 4315 ovli = i734.ovli; 4316 ovli.paddr = i734_buf.paddr; 4317 lcd_conf = i734.lcd_conf; 4318 4319 /* Gate all LCD1 outputs */ 4320 REG_FLD_MOD(DISPC_CONFIG, 0x1f, 8, 4); 4321 4322 /* Setup and enable GFX plane */ 4323 dispc_ovl_set_channel_out(OMAP_DSS_GFX, OMAP_DSS_CHANNEL_LCD); 4324 dispc_ovl_setup(OMAP_DSS_GFX, &ovli, false, &i734.timings, false); 4325 dispc_ovl_enable(OMAP_DSS_GFX, true); 4326 4327 /* Set up and enable display manager for LCD1 */ 4328 dispc_mgr_setup(OMAP_DSS_CHANNEL_LCD, &i734.mgri); 4329 dispc_calc_clock_rates(dss_get_dispc_clk_rate(), 4330 &lcd_conf.clock_info); 4331 dispc_mgr_set_lcd_config(OMAP_DSS_CHANNEL_LCD, &lcd_conf); 4332 dispc_mgr_set_timings(OMAP_DSS_CHANNEL_LCD, &i734.timings); 4333 4334 dispc_clear_irqstatus(framedone_irq); 4335 4336 /* Enable and shut the channel to produce just one frame */ 4337 dispc_mgr_enable(OMAP_DSS_CHANNEL_LCD, true); 4338 dispc_mgr_enable(OMAP_DSS_CHANNEL_LCD, false); 4339 4340 /* Busy wait for framedone. We can't fiddle with irq handlers 4341 * in PM resume. Typically the loop runs less than 5 times and 4342 * waits less than a micro second. 4343 */ 4344 count = 0; 4345 while (!(dispc_read_irqstatus() & framedone_irq)) { 4346 if (count++ > 10000) { 4347 dev_err(&dispc.pdev->dev, "%s: framedone timeout\n", 4348 __func__); 4349 break; 4350 } 4351 } 4352 dispc_ovl_enable(OMAP_DSS_GFX, false); 4353 4354 /* Clear all irq bits before continuing */ 4355 dispc_clear_irqstatus(0xffffffff); 4356 4357 /* Restore the original state to LCD1 output gates */ 4358 REG_FLD_MOD(DISPC_CONFIG, gatestate, 8, 4); 4359 } 4360 4361 /* DISPC HW IP initialisation */ 4362 static int dispc_bind(struct device *dev, struct device *master, void *data) 4363 { 4364 struct platform_device *pdev = to_platform_device(dev); 4365 u32 rev; 4366 int r = 0; 4367 struct resource *dispc_mem; 4368 struct device_node *np = pdev->dev.of_node; 4369 4370 dispc.pdev = pdev; 4371 4372 spin_lock_init(&dispc.control_lock); 4373 4374 r = dispc_init_features(dispc.pdev); 4375 if (r) 4376 return r; 4377 4378 r = dispc_errata_i734_wa_init(); 4379 if (r) 4380 return r; 4381 4382 dispc_mem = platform_get_resource(dispc.pdev, IORESOURCE_MEM, 0); 4383 if (!dispc_mem) { 4384 DSSERR("can't get IORESOURCE_MEM DISPC\n"); 4385 return -EINVAL; 4386 } 4387 4388 dispc.base = devm_ioremap(&pdev->dev, dispc_mem->start, 4389 resource_size(dispc_mem)); 4390 if (!dispc.base) { 4391 DSSERR("can't ioremap DISPC\n"); 4392 return -ENOMEM; 4393 } 4394 4395 dispc.irq = platform_get_irq(dispc.pdev, 0); 4396 if (dispc.irq < 0) { 4397 DSSERR("platform_get_irq failed\n"); 4398 return -ENODEV; 4399 } 4400 4401 if (np && of_property_read_bool(np, "syscon-pol")) { 4402 dispc.syscon_pol = syscon_regmap_lookup_by_phandle(np, "syscon-pol"); 4403 if (IS_ERR(dispc.syscon_pol)) { 4404 dev_err(&pdev->dev, "failed to get syscon-pol regmap\n"); 4405 return PTR_ERR(dispc.syscon_pol); 4406 } 4407 4408 if (of_property_read_u32_index(np, "syscon-pol", 1, 4409 &dispc.syscon_pol_offset)) { 4410 dev_err(&pdev->dev, "failed to get syscon-pol offset\n"); 4411 return -EINVAL; 4412 } 4413 } 4414 4415 r = dispc_init_gamma_tables(); 4416 if (r) 4417 return r; 4418 4419 pm_runtime_enable(&pdev->dev); 4420 4421 r = dispc_runtime_get(); 4422 if (r) 4423 goto err_runtime_get; 4424 4425 _omap_dispc_initial_config(); 4426 4427 rev = dispc_read_reg(DISPC_REVISION); 4428 dev_dbg(&pdev->dev, "OMAP DISPC rev %d.%d\n", 4429 FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); 4430 4431 dispc_runtime_put(); 4432 4433 dss_debugfs_create_file("dispc", dispc_dump_regs); 4434 4435 return 0; 4436 4437 err_runtime_get: 4438 pm_runtime_disable(&pdev->dev); 4439 return r; 4440 } 4441 4442 static void dispc_unbind(struct device *dev, struct device *master, 4443 void *data) 4444 { 4445 pm_runtime_disable(dev); 4446 4447 dispc_errata_i734_wa_fini(); 4448 } 4449 4450 static const struct component_ops dispc_component_ops = { 4451 .bind = dispc_bind, 4452 .unbind = dispc_unbind, 4453 }; 4454 4455 static int dispc_probe(struct platform_device *pdev) 4456 { 4457 return component_add(&pdev->dev, &dispc_component_ops); 4458 } 4459 4460 static int dispc_remove(struct platform_device *pdev) 4461 { 4462 component_del(&pdev->dev, &dispc_component_ops); 4463 return 0; 4464 } 4465 4466 static int dispc_runtime_suspend(struct device *dev) 4467 { 4468 dispc.is_enabled = false; 4469 /* ensure the dispc_irq_handler sees the is_enabled value */ 4470 smp_wmb(); 4471 /* wait for current handler to finish before turning the DISPC off */ 4472 synchronize_irq(dispc.irq); 4473 4474 dispc_save_context(); 4475 4476 return 0; 4477 } 4478 4479 static int dispc_runtime_resume(struct device *dev) 4480 { 4481 /* 4482 * The reset value for load mode is 0 (OMAP_DSS_LOAD_CLUT_AND_FRAME) 4483 * but we always initialize it to 2 (OMAP_DSS_LOAD_FRAME_ONLY) in 4484 * _omap_dispc_initial_config(). We can thus use it to detect if 4485 * we have lost register context. 4486 */ 4487 if (REG_GET(DISPC_CONFIG, 2, 1) != OMAP_DSS_LOAD_FRAME_ONLY) { 4488 _omap_dispc_initial_config(); 4489 4490 dispc_errata_i734_wa(); 4491 4492 dispc_restore_context(); 4493 4494 dispc_restore_gamma_tables(); 4495 } 4496 4497 dispc.is_enabled = true; 4498 /* ensure the dispc_irq_handler sees the is_enabled value */ 4499 smp_wmb(); 4500 4501 return 0; 4502 } 4503 4504 static const struct dev_pm_ops dispc_pm_ops = { 4505 .runtime_suspend = dispc_runtime_suspend, 4506 .runtime_resume = dispc_runtime_resume, 4507 }; 4508 4509 static const struct of_device_id dispc_of_match[] = { 4510 { .compatible = "ti,omap2-dispc", }, 4511 { .compatible = "ti,omap3-dispc", }, 4512 { .compatible = "ti,omap4-dispc", }, 4513 { .compatible = "ti,omap5-dispc", }, 4514 { .compatible = "ti,dra7-dispc", }, 4515 {}, 4516 }; 4517 4518 static struct platform_driver omap_dispchw_driver = { 4519 .probe = dispc_probe, 4520 .remove = dispc_remove, 4521 .driver = { 4522 .name = "omapdss_dispc", 4523 .pm = &dispc_pm_ops, 4524 .of_match_table = dispc_of_match, 4525 .suppress_bind_attrs = true, 4526 }, 4527 }; 4528 4529 int __init dispc_init_platform_driver(void) 4530 { 4531 return platform_driver_register(&omap_dispchw_driver); 4532 } 4533 4534 void dispc_uninit_platform_driver(void) 4535 { 4536 platform_driver_unregister(&omap_dispchw_driver); 4537 } 4538