1 /* 2 * OMAP1 Special OptimiSed Screen Interface support 3 * 4 * Copyright (C) 2004-2005 Nokia Corporation 5 * Author: Juha Yrjölä <juha.yrjola@nokia.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2 of the License, or (at your 10 * option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 */ 21 #include <linux/module.h> 22 #include <linux/mm.h> 23 #include <linux/clk.h> 24 #include <linux/irq.h> 25 #include <linux/io.h> 26 #include <linux/interrupt.h> 27 28 #include <linux/omap-dma.h> 29 30 #include "omapfb.h" 31 #include "lcdc.h" 32 33 #define MODULE_NAME "omapfb-sossi" 34 35 #define OMAP_SOSSI_BASE 0xfffbac00 36 #define SOSSI_ID_REG 0x00 37 #define SOSSI_INIT1_REG 0x04 38 #define SOSSI_INIT2_REG 0x08 39 #define SOSSI_INIT3_REG 0x0c 40 #define SOSSI_FIFO_REG 0x10 41 #define SOSSI_REOTABLE_REG 0x14 42 #define SOSSI_TEARING_REG 0x18 43 #define SOSSI_INIT1B_REG 0x1c 44 #define SOSSI_FIFOB_REG 0x20 45 46 #define DMA_GSCR 0xfffedc04 47 #define DMA_LCD_CCR 0xfffee3c2 48 #define DMA_LCD_CTRL 0xfffee3c4 49 #define DMA_LCD_LCH_CTRL 0xfffee3ea 50 51 #define CONF_SOSSI_RESET_R (1 << 23) 52 53 #define RD_ACCESS 0 54 #define WR_ACCESS 1 55 56 #define SOSSI_MAX_XMIT_BYTES (512 * 1024) 57 58 static struct { 59 void __iomem *base; 60 struct clk *fck; 61 unsigned long fck_hz; 62 spinlock_t lock; 63 int bus_pick_count; 64 int bus_pick_width; 65 int tearsync_mode; 66 int tearsync_line; 67 void (*lcdc_callback)(void *data); 68 void *lcdc_callback_data; 69 int vsync_dma_pending; 70 /* timing for read and write access */ 71 int clk_div; 72 u8 clk_tw0[2]; 73 u8 clk_tw1[2]; 74 /* 75 * if last_access is the same as current we don't have to change 76 * the timings 77 */ 78 int last_access; 79 80 struct omapfb_device *fbdev; 81 } sossi; 82 83 static inline u32 sossi_read_reg(int reg) 84 { 85 return readl(sossi.base + reg); 86 } 87 88 static inline u16 sossi_read_reg16(int reg) 89 { 90 return readw(sossi.base + reg); 91 } 92 93 static inline u8 sossi_read_reg8(int reg) 94 { 95 return readb(sossi.base + reg); 96 } 97 98 static inline void sossi_write_reg(int reg, u32 value) 99 { 100 writel(value, sossi.base + reg); 101 } 102 103 static inline void sossi_write_reg16(int reg, u16 value) 104 { 105 writew(value, sossi.base + reg); 106 } 107 108 static inline void sossi_write_reg8(int reg, u8 value) 109 { 110 writeb(value, sossi.base + reg); 111 } 112 113 static void sossi_set_bits(int reg, u32 bits) 114 { 115 sossi_write_reg(reg, sossi_read_reg(reg) | bits); 116 } 117 118 static void sossi_clear_bits(int reg, u32 bits) 119 { 120 sossi_write_reg(reg, sossi_read_reg(reg) & ~bits); 121 } 122 123 #define HZ_TO_PS(x) (1000000000 / (x / 1000)) 124 125 static u32 ps_to_sossi_ticks(u32 ps, int div) 126 { 127 u32 clk_period = HZ_TO_PS(sossi.fck_hz) * div; 128 return (clk_period + ps - 1) / clk_period; 129 } 130 131 static int calc_rd_timings(struct extif_timings *t) 132 { 133 u32 tw0, tw1; 134 int reon, reoff, recyc, actim; 135 int div = t->clk_div; 136 137 /* 138 * Make sure that after conversion it still holds that: 139 * reoff > reon, recyc >= reoff, actim > reon 140 */ 141 reon = ps_to_sossi_ticks(t->re_on_time, div); 142 /* reon will be exactly one sossi tick */ 143 if (reon > 1) 144 return -1; 145 146 reoff = ps_to_sossi_ticks(t->re_off_time, div); 147 148 if (reoff <= reon) 149 reoff = reon + 1; 150 151 tw0 = reoff - reon; 152 if (tw0 > 0x10) 153 return -1; 154 155 recyc = ps_to_sossi_ticks(t->re_cycle_time, div); 156 if (recyc <= reoff) 157 recyc = reoff + 1; 158 159 tw1 = recyc - tw0; 160 /* values less then 3 result in the SOSSI block resetting itself */ 161 if (tw1 < 3) 162 tw1 = 3; 163 if (tw1 > 0x40) 164 return -1; 165 166 actim = ps_to_sossi_ticks(t->access_time, div); 167 if (actim < reoff) 168 actim++; 169 /* 170 * access time (data hold time) will be exactly one sossi 171 * tick 172 */ 173 if (actim - reoff > 1) 174 return -1; 175 176 t->tim[0] = tw0 - 1; 177 t->tim[1] = tw1 - 1; 178 179 return 0; 180 } 181 182 static int calc_wr_timings(struct extif_timings *t) 183 { 184 u32 tw0, tw1; 185 int weon, weoff, wecyc; 186 int div = t->clk_div; 187 188 /* 189 * Make sure that after conversion it still holds that: 190 * weoff > weon, wecyc >= weoff 191 */ 192 weon = ps_to_sossi_ticks(t->we_on_time, div); 193 /* weon will be exactly one sossi tick */ 194 if (weon > 1) 195 return -1; 196 197 weoff = ps_to_sossi_ticks(t->we_off_time, div); 198 if (weoff <= weon) 199 weoff = weon + 1; 200 tw0 = weoff - weon; 201 if (tw0 > 0x10) 202 return -1; 203 204 wecyc = ps_to_sossi_ticks(t->we_cycle_time, div); 205 if (wecyc <= weoff) 206 wecyc = weoff + 1; 207 208 tw1 = wecyc - tw0; 209 /* values less then 3 result in the SOSSI block resetting itself */ 210 if (tw1 < 3) 211 tw1 = 3; 212 if (tw1 > 0x40) 213 return -1; 214 215 t->tim[2] = tw0 - 1; 216 t->tim[3] = tw1 - 1; 217 218 return 0; 219 } 220 221 static void _set_timing(int div, int tw0, int tw1) 222 { 223 u32 l; 224 225 #ifdef VERBOSE 226 dev_dbg(sossi.fbdev->dev, "Using TW0 = %d, TW1 = %d, div = %d\n", 227 tw0 + 1, tw1 + 1, div); 228 #endif 229 230 clk_set_rate(sossi.fck, sossi.fck_hz / div); 231 clk_enable(sossi.fck); 232 l = sossi_read_reg(SOSSI_INIT1_REG); 233 l &= ~((0x0f << 20) | (0x3f << 24)); 234 l |= (tw0 << 20) | (tw1 << 24); 235 sossi_write_reg(SOSSI_INIT1_REG, l); 236 clk_disable(sossi.fck); 237 } 238 239 static void _set_bits_per_cycle(int bus_pick_count, int bus_pick_width) 240 { 241 u32 l; 242 243 l = sossi_read_reg(SOSSI_INIT3_REG); 244 l &= ~0x3ff; 245 l |= ((bus_pick_count - 1) << 5) | ((bus_pick_width - 1) & 0x1f); 246 sossi_write_reg(SOSSI_INIT3_REG, l); 247 } 248 249 static void _set_tearsync_mode(int mode, unsigned line) 250 { 251 u32 l; 252 253 l = sossi_read_reg(SOSSI_TEARING_REG); 254 l &= ~(((1 << 11) - 1) << 15); 255 l |= line << 15; 256 l &= ~(0x3 << 26); 257 l |= mode << 26; 258 sossi_write_reg(SOSSI_TEARING_REG, l); 259 if (mode) 260 sossi_set_bits(SOSSI_INIT2_REG, 1 << 6); /* TE logic */ 261 else 262 sossi_clear_bits(SOSSI_INIT2_REG, 1 << 6); 263 } 264 265 static inline void set_timing(int access) 266 { 267 if (access != sossi.last_access) { 268 sossi.last_access = access; 269 _set_timing(sossi.clk_div, 270 sossi.clk_tw0[access], sossi.clk_tw1[access]); 271 } 272 } 273 274 static void sossi_start_transfer(void) 275 { 276 /* WE */ 277 sossi_clear_bits(SOSSI_INIT2_REG, 1 << 4); 278 /* CS active low */ 279 sossi_clear_bits(SOSSI_INIT1_REG, 1 << 30); 280 } 281 282 static void sossi_stop_transfer(void) 283 { 284 /* WE */ 285 sossi_set_bits(SOSSI_INIT2_REG, 1 << 4); 286 /* CS active low */ 287 sossi_set_bits(SOSSI_INIT1_REG, 1 << 30); 288 } 289 290 static void wait_end_of_write(void) 291 { 292 /* Before reading we must check if some writings are going on */ 293 while (!(sossi_read_reg(SOSSI_INIT2_REG) & (1 << 3))); 294 } 295 296 static void send_data(const void *data, unsigned int len) 297 { 298 while (len >= 4) { 299 sossi_write_reg(SOSSI_FIFO_REG, *(const u32 *) data); 300 len -= 4; 301 data += 4; 302 } 303 while (len >= 2) { 304 sossi_write_reg16(SOSSI_FIFO_REG, *(const u16 *) data); 305 len -= 2; 306 data += 2; 307 } 308 while (len) { 309 sossi_write_reg8(SOSSI_FIFO_REG, *(const u8 *) data); 310 len--; 311 data++; 312 } 313 } 314 315 static void set_cycles(unsigned int len) 316 { 317 unsigned long nr_cycles = len / (sossi.bus_pick_width / 8); 318 319 BUG_ON((nr_cycles - 1) & ~0x3ffff); 320 321 sossi_clear_bits(SOSSI_INIT1_REG, 0x3ffff); 322 sossi_set_bits(SOSSI_INIT1_REG, (nr_cycles - 1) & 0x3ffff); 323 } 324 325 static int sossi_convert_timings(struct extif_timings *t) 326 { 327 int r = 0; 328 int div = t->clk_div; 329 330 t->converted = 0; 331 332 if (div <= 0 || div > 8) 333 return -1; 334 335 /* no CS on SOSSI, so ignore cson, csoff, cs_pulsewidth */ 336 if ((r = calc_rd_timings(t)) < 0) 337 return r; 338 339 if ((r = calc_wr_timings(t)) < 0) 340 return r; 341 342 t->tim[4] = div; 343 344 t->converted = 1; 345 346 return 0; 347 } 348 349 static void sossi_set_timings(const struct extif_timings *t) 350 { 351 BUG_ON(!t->converted); 352 353 sossi.clk_tw0[RD_ACCESS] = t->tim[0]; 354 sossi.clk_tw1[RD_ACCESS] = t->tim[1]; 355 356 sossi.clk_tw0[WR_ACCESS] = t->tim[2]; 357 sossi.clk_tw1[WR_ACCESS] = t->tim[3]; 358 359 sossi.clk_div = t->tim[4]; 360 } 361 362 static void sossi_get_clk_info(u32 *clk_period, u32 *max_clk_div) 363 { 364 *clk_period = HZ_TO_PS(sossi.fck_hz); 365 *max_clk_div = 8; 366 } 367 368 static void sossi_set_bits_per_cycle(int bpc) 369 { 370 int bus_pick_count, bus_pick_width; 371 372 /* 373 * We set explicitly the the bus_pick_count as well, although 374 * with remapping/reordering disabled it will be calculated by HW 375 * as (32 / bus_pick_width). 376 */ 377 switch (bpc) { 378 case 8: 379 bus_pick_count = 4; 380 bus_pick_width = 8; 381 break; 382 case 16: 383 bus_pick_count = 2; 384 bus_pick_width = 16; 385 break; 386 default: 387 BUG(); 388 return; 389 } 390 sossi.bus_pick_width = bus_pick_width; 391 sossi.bus_pick_count = bus_pick_count; 392 } 393 394 static int sossi_setup_tearsync(unsigned pin_cnt, 395 unsigned hs_pulse_time, unsigned vs_pulse_time, 396 int hs_pol_inv, int vs_pol_inv, int div) 397 { 398 int hs, vs; 399 u32 l; 400 401 if (pin_cnt != 1 || div < 1 || div > 8) 402 return -EINVAL; 403 404 hs = ps_to_sossi_ticks(hs_pulse_time, div); 405 vs = ps_to_sossi_ticks(vs_pulse_time, div); 406 if (vs < 8 || vs <= hs || vs >= (1 << 12)) 407 return -EDOM; 408 vs /= 8; 409 vs--; 410 if (hs > 8) 411 hs = 8; 412 if (hs) 413 hs--; 414 415 dev_dbg(sossi.fbdev->dev, 416 "setup_tearsync: hs %d vs %d hs_inv %d vs_inv %d\n", 417 hs, vs, hs_pol_inv, vs_pol_inv); 418 419 clk_enable(sossi.fck); 420 l = sossi_read_reg(SOSSI_TEARING_REG); 421 l &= ~((1 << 15) - 1); 422 l |= vs << 3; 423 l |= hs; 424 if (hs_pol_inv) 425 l |= 1 << 29; 426 else 427 l &= ~(1 << 29); 428 if (vs_pol_inv) 429 l |= 1 << 28; 430 else 431 l &= ~(1 << 28); 432 sossi_write_reg(SOSSI_TEARING_REG, l); 433 clk_disable(sossi.fck); 434 435 return 0; 436 } 437 438 static int sossi_enable_tearsync(int enable, unsigned line) 439 { 440 int mode; 441 442 dev_dbg(sossi.fbdev->dev, "tearsync %d line %d\n", enable, line); 443 if (line >= 1 << 11) 444 return -EINVAL; 445 if (enable) { 446 if (line) 447 mode = 2; /* HS or VS */ 448 else 449 mode = 3; /* VS only */ 450 } else 451 mode = 0; 452 sossi.tearsync_line = line; 453 sossi.tearsync_mode = mode; 454 455 return 0; 456 } 457 458 static void sossi_write_command(const void *data, unsigned int len) 459 { 460 clk_enable(sossi.fck); 461 set_timing(WR_ACCESS); 462 _set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width); 463 /* CMD#/DATA */ 464 sossi_clear_bits(SOSSI_INIT1_REG, 1 << 18); 465 set_cycles(len); 466 sossi_start_transfer(); 467 send_data(data, len); 468 sossi_stop_transfer(); 469 wait_end_of_write(); 470 clk_disable(sossi.fck); 471 } 472 473 static void sossi_write_data(const void *data, unsigned int len) 474 { 475 clk_enable(sossi.fck); 476 set_timing(WR_ACCESS); 477 _set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width); 478 /* CMD#/DATA */ 479 sossi_set_bits(SOSSI_INIT1_REG, 1 << 18); 480 set_cycles(len); 481 sossi_start_transfer(); 482 send_data(data, len); 483 sossi_stop_transfer(); 484 wait_end_of_write(); 485 clk_disable(sossi.fck); 486 } 487 488 static void sossi_transfer_area(int width, int height, 489 void (callback)(void *data), void *data) 490 { 491 BUG_ON(callback == NULL); 492 493 sossi.lcdc_callback = callback; 494 sossi.lcdc_callback_data = data; 495 496 clk_enable(sossi.fck); 497 set_timing(WR_ACCESS); 498 _set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width); 499 _set_tearsync_mode(sossi.tearsync_mode, sossi.tearsync_line); 500 /* CMD#/DATA */ 501 sossi_set_bits(SOSSI_INIT1_REG, 1 << 18); 502 set_cycles(width * height * sossi.bus_pick_width / 8); 503 504 sossi_start_transfer(); 505 if (sossi.tearsync_mode) { 506 /* 507 * Wait for the sync signal and start the transfer only 508 * then. We can't seem to be able to use HW sync DMA for 509 * this since LCD DMA shows huge latencies, as if it 510 * would ignore some of the DMA requests from SoSSI. 511 */ 512 unsigned long flags; 513 514 spin_lock_irqsave(&sossi.lock, flags); 515 sossi.vsync_dma_pending++; 516 spin_unlock_irqrestore(&sossi.lock, flags); 517 } else 518 /* Just start the transfer right away. */ 519 omap_enable_lcd_dma(); 520 } 521 522 static void sossi_dma_callback(void *data) 523 { 524 omap_stop_lcd_dma(); 525 sossi_stop_transfer(); 526 clk_disable(sossi.fck); 527 sossi.lcdc_callback(sossi.lcdc_callback_data); 528 } 529 530 static void sossi_read_data(void *data, unsigned int len) 531 { 532 clk_enable(sossi.fck); 533 set_timing(RD_ACCESS); 534 _set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width); 535 /* CMD#/DATA */ 536 sossi_set_bits(SOSSI_INIT1_REG, 1 << 18); 537 set_cycles(len); 538 sossi_start_transfer(); 539 while (len >= 4) { 540 *(u32 *) data = sossi_read_reg(SOSSI_FIFO_REG); 541 len -= 4; 542 data += 4; 543 } 544 while (len >= 2) { 545 *(u16 *) data = sossi_read_reg16(SOSSI_FIFO_REG); 546 len -= 2; 547 data += 2; 548 } 549 while (len) { 550 *(u8 *) data = sossi_read_reg8(SOSSI_FIFO_REG); 551 len--; 552 data++; 553 } 554 sossi_stop_transfer(); 555 clk_disable(sossi.fck); 556 } 557 558 static irqreturn_t sossi_match_irq(int irq, void *data) 559 { 560 unsigned long flags; 561 562 spin_lock_irqsave(&sossi.lock, flags); 563 if (sossi.vsync_dma_pending) { 564 sossi.vsync_dma_pending--; 565 omap_enable_lcd_dma(); 566 } 567 spin_unlock_irqrestore(&sossi.lock, flags); 568 return IRQ_HANDLED; 569 } 570 571 static int sossi_init(struct omapfb_device *fbdev) 572 { 573 u32 l, k; 574 struct clk *fck; 575 struct clk *dpll1out_ck; 576 int r; 577 578 sossi.base = ioremap(OMAP_SOSSI_BASE, SZ_1K); 579 if (!sossi.base) { 580 dev_err(fbdev->dev, "can't ioremap SoSSI\n"); 581 return -ENOMEM; 582 } 583 584 sossi.fbdev = fbdev; 585 spin_lock_init(&sossi.lock); 586 587 dpll1out_ck = clk_get(fbdev->dev, "ck_dpll1out"); 588 if (IS_ERR(dpll1out_ck)) { 589 dev_err(fbdev->dev, "can't get DPLL1OUT clock\n"); 590 return PTR_ERR(dpll1out_ck); 591 } 592 /* 593 * We need the parent clock rate, which we might divide further 594 * depending on the timing requirements of the controller. See 595 * _set_timings. 596 */ 597 sossi.fck_hz = clk_get_rate(dpll1out_ck); 598 clk_put(dpll1out_ck); 599 600 fck = clk_get(fbdev->dev, "ck_sossi"); 601 if (IS_ERR(fck)) { 602 dev_err(fbdev->dev, "can't get SoSSI functional clock\n"); 603 return PTR_ERR(fck); 604 } 605 sossi.fck = fck; 606 607 /* Reset and enable the SoSSI module */ 608 l = omap_readl(MOD_CONF_CTRL_1); 609 l |= CONF_SOSSI_RESET_R; 610 omap_writel(l, MOD_CONF_CTRL_1); 611 l &= ~CONF_SOSSI_RESET_R; 612 omap_writel(l, MOD_CONF_CTRL_1); 613 614 clk_enable(sossi.fck); 615 l = omap_readl(ARM_IDLECT2); 616 l &= ~(1 << 8); /* DMACK_REQ */ 617 omap_writel(l, ARM_IDLECT2); 618 619 l = sossi_read_reg(SOSSI_INIT2_REG); 620 /* Enable and reset the SoSSI block */ 621 l |= (1 << 0) | (1 << 1); 622 sossi_write_reg(SOSSI_INIT2_REG, l); 623 /* Take SoSSI out of reset */ 624 l &= ~(1 << 1); 625 sossi_write_reg(SOSSI_INIT2_REG, l); 626 627 sossi_write_reg(SOSSI_ID_REG, 0); 628 l = sossi_read_reg(SOSSI_ID_REG); 629 k = sossi_read_reg(SOSSI_ID_REG); 630 631 if (l != 0x55555555 || k != 0xaaaaaaaa) { 632 dev_err(fbdev->dev, 633 "invalid SoSSI sync pattern: %08x, %08x\n", l, k); 634 r = -ENODEV; 635 goto err; 636 } 637 638 if ((r = omap_lcdc_set_dma_callback(sossi_dma_callback, NULL)) < 0) { 639 dev_err(fbdev->dev, "can't get LCDC IRQ\n"); 640 r = -ENODEV; 641 goto err; 642 } 643 644 l = sossi_read_reg(SOSSI_ID_REG); /* Component code */ 645 l = sossi_read_reg(SOSSI_ID_REG); 646 dev_info(fbdev->dev, "SoSSI version %d.%d initialized\n", 647 l >> 16, l & 0xffff); 648 649 l = sossi_read_reg(SOSSI_INIT1_REG); 650 l |= (1 << 19); /* DMA_MODE */ 651 l &= ~(1 << 31); /* REORDERING */ 652 sossi_write_reg(SOSSI_INIT1_REG, l); 653 654 if ((r = request_irq(INT_1610_SoSSI_MATCH, sossi_match_irq, 655 IRQ_TYPE_EDGE_FALLING, 656 "sossi_match", sossi.fbdev->dev)) < 0) { 657 dev_err(sossi.fbdev->dev, "can't get SoSSI match IRQ\n"); 658 goto err; 659 } 660 661 clk_disable(sossi.fck); 662 return 0; 663 664 err: 665 clk_disable(sossi.fck); 666 clk_put(sossi.fck); 667 return r; 668 } 669 670 static void sossi_cleanup(void) 671 { 672 omap_lcdc_free_dma_callback(); 673 clk_put(sossi.fck); 674 iounmap(sossi.base); 675 } 676 677 struct lcd_ctrl_extif omap1_ext_if = { 678 .init = sossi_init, 679 .cleanup = sossi_cleanup, 680 .get_clk_info = sossi_get_clk_info, 681 .convert_timings = sossi_convert_timings, 682 .set_timings = sossi_set_timings, 683 .set_bits_per_cycle = sossi_set_bits_per_cycle, 684 .setup_tearsync = sossi_setup_tearsync, 685 .enable_tearsync = sossi_enable_tearsync, 686 .write_command = sossi_write_command, 687 .read_data = sossi_read_data, 688 .write_data = sossi_write_data, 689 .transfer_area = sossi_transfer_area, 690 691 .max_transmit_size = SOSSI_MAX_XMIT_BYTES, 692 }; 693 694