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