Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.30-rc6 935 lines 23 kB view raw
1/* 2 * SuperH Mobile LCDC Framebuffer 3 * 4 * Copyright (c) 2008 Magnus Damm 5 * 6 * This file is subject to the terms and conditions of the GNU General Public 7 * License. See the file "COPYING" in the main directory of this archive 8 * for more details. 9 */ 10 11#include <linux/kernel.h> 12#include <linux/init.h> 13#include <linux/delay.h> 14#include <linux/mm.h> 15#include <linux/fb.h> 16#include <linux/clk.h> 17#include <linux/platform_device.h> 18#include <linux/dma-mapping.h> 19#include <linux/interrupt.h> 20#include <video/sh_mobile_lcdc.h> 21#include <asm/atomic.h> 22 23#define PALETTE_NR 16 24 25struct sh_mobile_lcdc_priv; 26struct sh_mobile_lcdc_chan { 27 struct sh_mobile_lcdc_priv *lcdc; 28 unsigned long *reg_offs; 29 unsigned long ldmt1r_value; 30 unsigned long enabled; /* ME and SE in LDCNT2R */ 31 struct sh_mobile_lcdc_chan_cfg cfg; 32 u32 pseudo_palette[PALETTE_NR]; 33 struct fb_info info; 34 dma_addr_t dma_handle; 35 struct fb_deferred_io defio; 36 unsigned long frame_end; 37 wait_queue_head_t frame_end_wait; 38}; 39 40struct sh_mobile_lcdc_priv { 41 void __iomem *base; 42 int irq; 43#ifdef CONFIG_HAVE_CLK 44 atomic_t clk_usecnt; 45 struct clk *dot_clk; 46 struct clk *clk; 47#endif 48 unsigned long lddckr; 49 struct sh_mobile_lcdc_chan ch[2]; 50}; 51 52/* shared registers */ 53#define _LDDCKR 0x410 54#define _LDDCKSTPR 0x414 55#define _LDINTR 0x468 56#define _LDSR 0x46c 57#define _LDCNT1R 0x470 58#define _LDCNT2R 0x474 59#define _LDDDSR 0x47c 60#define _LDDWD0R 0x800 61#define _LDDRDR 0x840 62#define _LDDWAR 0x900 63#define _LDDRAR 0x904 64 65/* per-channel registers */ 66enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, 67 LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR }; 68 69static unsigned long lcdc_offs_mainlcd[] = { 70 [LDDCKPAT1R] = 0x400, 71 [LDDCKPAT2R] = 0x404, 72 [LDMT1R] = 0x418, 73 [LDMT2R] = 0x41c, 74 [LDMT3R] = 0x420, 75 [LDDFR] = 0x424, 76 [LDSM1R] = 0x428, 77 [LDSM2R] = 0x42c, 78 [LDSA1R] = 0x430, 79 [LDMLSR] = 0x438, 80 [LDHCNR] = 0x448, 81 [LDHSYNR] = 0x44c, 82 [LDVLNR] = 0x450, 83 [LDVSYNR] = 0x454, 84 [LDPMR] = 0x460, 85}; 86 87static unsigned long lcdc_offs_sublcd[] = { 88 [LDDCKPAT1R] = 0x408, 89 [LDDCKPAT2R] = 0x40c, 90 [LDMT1R] = 0x600, 91 [LDMT2R] = 0x604, 92 [LDMT3R] = 0x608, 93 [LDDFR] = 0x60c, 94 [LDSM1R] = 0x610, 95 [LDSM2R] = 0x614, 96 [LDSA1R] = 0x618, 97 [LDMLSR] = 0x620, 98 [LDHCNR] = 0x624, 99 [LDHSYNR] = 0x628, 100 [LDVLNR] = 0x62c, 101 [LDVSYNR] = 0x630, 102 [LDPMR] = 0x63c, 103}; 104 105#define START_LCDC 0x00000001 106#define LCDC_RESET 0x00000100 107#define DISPLAY_BEU 0x00000008 108#define LCDC_ENABLE 0x00000001 109#define LDINTR_FE 0x00000400 110#define LDINTR_FS 0x00000004 111 112static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, 113 int reg_nr, unsigned long data) 114{ 115 iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr]); 116} 117 118static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan, 119 int reg_nr) 120{ 121 return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]); 122} 123 124static void lcdc_write(struct sh_mobile_lcdc_priv *priv, 125 unsigned long reg_offs, unsigned long data) 126{ 127 iowrite32(data, priv->base + reg_offs); 128} 129 130static unsigned long lcdc_read(struct sh_mobile_lcdc_priv *priv, 131 unsigned long reg_offs) 132{ 133 return ioread32(priv->base + reg_offs); 134} 135 136static void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv, 137 unsigned long reg_offs, 138 unsigned long mask, unsigned long until) 139{ 140 while ((lcdc_read(priv, reg_offs) & mask) != until) 141 cpu_relax(); 142} 143 144static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan) 145{ 146 return chan->cfg.chan == LCDC_CHAN_SUBLCD; 147} 148 149static void lcdc_sys_write_index(void *handle, unsigned long data) 150{ 151 struct sh_mobile_lcdc_chan *ch = handle; 152 153 lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000); 154 lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); 155 lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); 156} 157 158static void lcdc_sys_write_data(void *handle, unsigned long data) 159{ 160 struct sh_mobile_lcdc_chan *ch = handle; 161 162 lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000); 163 lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); 164 lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); 165} 166 167static unsigned long lcdc_sys_read_data(void *handle) 168{ 169 struct sh_mobile_lcdc_chan *ch = handle; 170 171 lcdc_write(ch->lcdc, _LDDRDR, 0x01000000); 172 lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); 173 lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); 174 udelay(1); 175 176 return lcdc_read(ch->lcdc, _LDDRDR) & 0xffff; 177} 178 179struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { 180 lcdc_sys_write_index, 181 lcdc_sys_write_data, 182 lcdc_sys_read_data, 183}; 184 185#ifdef CONFIG_HAVE_CLK 186static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) 187{ 188 if (atomic_inc_and_test(&priv->clk_usecnt)) { 189 clk_enable(priv->clk); 190 if (priv->dot_clk) 191 clk_enable(priv->dot_clk); 192 } 193} 194 195static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) 196{ 197 if (atomic_sub_return(1, &priv->clk_usecnt) == -1) { 198 if (priv->dot_clk) 199 clk_disable(priv->dot_clk); 200 clk_disable(priv->clk); 201 } 202} 203#else 204static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) {} 205static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) {} 206#endif 207 208static void sh_mobile_lcdc_deferred_io(struct fb_info *info, 209 struct list_head *pagelist) 210{ 211 struct sh_mobile_lcdc_chan *ch = info->par; 212 213 /* enable clocks before accessing hardware */ 214 sh_mobile_lcdc_clk_on(ch->lcdc); 215 216 /* trigger panel update */ 217 lcdc_write_chan(ch, LDSM2R, 1); 218} 219 220static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info) 221{ 222 struct fb_deferred_io *fbdefio = info->fbdefio; 223 224 if (fbdefio) 225 schedule_delayed_work(&info->deferred_work, fbdefio->delay); 226} 227 228static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) 229{ 230 struct sh_mobile_lcdc_priv *priv = data; 231 struct sh_mobile_lcdc_chan *ch; 232 unsigned long tmp; 233 int is_sub; 234 int k; 235 236 /* acknowledge interrupt */ 237 tmp = lcdc_read(priv, _LDINTR); 238 tmp &= 0xffffff00; /* mask in high 24 bits */ 239 tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */ 240 lcdc_write(priv, _LDINTR, tmp); 241 242 /* figure out if this interrupt is for main or sub lcd */ 243 is_sub = (lcdc_read(priv, _LDSR) & (1 << 10)) ? 1 : 0; 244 245 /* wake up channel and disable clocks*/ 246 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 247 ch = &priv->ch[k]; 248 249 if (!ch->enabled) 250 continue; 251 252 if (is_sub == lcdc_chan_is_sublcd(ch)) { 253 ch->frame_end = 1; 254 wake_up(&ch->frame_end_wait); 255 256 sh_mobile_lcdc_clk_off(priv); 257 } 258 } 259 260 return IRQ_HANDLED; 261} 262 263static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, 264 int start) 265{ 266 unsigned long tmp = lcdc_read(priv, _LDCNT2R); 267 int k; 268 269 /* start or stop the lcdc */ 270 if (start) 271 lcdc_write(priv, _LDCNT2R, tmp | START_LCDC); 272 else 273 lcdc_write(priv, _LDCNT2R, tmp & ~START_LCDC); 274 275 /* wait until power is applied/stopped on all channels */ 276 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) 277 if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled) 278 while (1) { 279 tmp = lcdc_read_chan(&priv->ch[k], LDPMR) & 3; 280 if (start && tmp == 3) 281 break; 282 if (!start && tmp == 0) 283 break; 284 cpu_relax(); 285 } 286 287 if (!start) 288 lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */ 289} 290 291static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) 292{ 293 struct sh_mobile_lcdc_chan *ch; 294 struct fb_videomode *lcd_cfg; 295 struct sh_mobile_lcdc_board_cfg *board_cfg; 296 unsigned long tmp; 297 int k, m; 298 int ret = 0; 299 300 /* enable clocks before accessing the hardware */ 301 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) 302 if (priv->ch[k].enabled) 303 sh_mobile_lcdc_clk_on(priv); 304 305 /* reset */ 306 lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET); 307 lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0); 308 309 /* enable LCDC channels */ 310 tmp = lcdc_read(priv, _LDCNT2R); 311 tmp |= priv->ch[0].enabled; 312 tmp |= priv->ch[1].enabled; 313 lcdc_write(priv, _LDCNT2R, tmp); 314 315 /* read data from external memory, avoid using the BEU for now */ 316 lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU); 317 318 /* stop the lcdc first */ 319 sh_mobile_lcdc_start_stop(priv, 0); 320 321 /* configure clocks */ 322 tmp = priv->lddckr; 323 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 324 ch = &priv->ch[k]; 325 326 if (!priv->ch[k].enabled) 327 continue; 328 329 m = ch->cfg.clock_divider; 330 if (!m) 331 continue; 332 333 if (m == 1) 334 m = 1 << 6; 335 tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); 336 337 lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000); 338 lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); 339 } 340 341 lcdc_write(priv, _LDDCKR, tmp); 342 343 /* start dotclock again */ 344 lcdc_write(priv, _LDDCKSTPR, 0); 345 lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); 346 347 /* interrupts are disabled to begin with */ 348 lcdc_write(priv, _LDINTR, 0); 349 350 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 351 ch = &priv->ch[k]; 352 lcd_cfg = &ch->cfg.lcd_cfg; 353 354 if (!ch->enabled) 355 continue; 356 357 tmp = ch->ldmt1r_value; 358 tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28; 359 tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27; 360 tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0; 361 tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0; 362 tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0; 363 tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0; 364 tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0; 365 lcdc_write_chan(ch, LDMT1R, tmp); 366 367 /* setup SYS bus */ 368 lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); 369 lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); 370 371 /* horizontal configuration */ 372 tmp = lcd_cfg->xres + lcd_cfg->hsync_len; 373 tmp += lcd_cfg->left_margin; 374 tmp += lcd_cfg->right_margin; 375 tmp /= 8; /* HTCN */ 376 tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */ 377 lcdc_write_chan(ch, LDHCNR, tmp); 378 379 tmp = lcd_cfg->xres; 380 tmp += lcd_cfg->right_margin; 381 tmp /= 8; /* HSYNP */ 382 tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */ 383 lcdc_write_chan(ch, LDHSYNR, tmp); 384 385 /* power supply */ 386 lcdc_write_chan(ch, LDPMR, 0); 387 388 /* vertical configuration */ 389 tmp = lcd_cfg->yres + lcd_cfg->vsync_len; 390 tmp += lcd_cfg->upper_margin; 391 tmp += lcd_cfg->lower_margin; /* VTLN */ 392 tmp |= lcd_cfg->yres << 16; /* VDLN */ 393 lcdc_write_chan(ch, LDVLNR, tmp); 394 395 tmp = lcd_cfg->yres; 396 tmp += lcd_cfg->lower_margin; /* VSYNP */ 397 tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */ 398 lcdc_write_chan(ch, LDVSYNR, tmp); 399 400 board_cfg = &ch->cfg.board_cfg; 401 if (board_cfg->setup_sys) 402 ret = board_cfg->setup_sys(board_cfg->board_data, ch, 403 &sh_mobile_lcdc_sys_bus_ops); 404 if (ret) 405 return ret; 406 } 407 408 /* word and long word swap */ 409 lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); 410 411 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 412 ch = &priv->ch[k]; 413 414 if (!priv->ch[k].enabled) 415 continue; 416 417 /* set bpp format in PKF[4:0] */ 418 tmp = lcdc_read_chan(ch, LDDFR); 419 tmp &= ~(0x0001001f); 420 tmp |= (priv->ch[k].info.var.bits_per_pixel == 16) ? 3 : 0; 421 lcdc_write_chan(ch, LDDFR, tmp); 422 423 /* point out our frame buffer */ 424 lcdc_write_chan(ch, LDSA1R, ch->info.fix.smem_start); 425 426 /* set line size */ 427 lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length); 428 429 /* setup deferred io if SYS bus */ 430 tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; 431 if (ch->ldmt1r_value & (1 << 12) && tmp) { 432 ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; 433 ch->defio.delay = msecs_to_jiffies(tmp); 434 ch->info.fbdefio = &ch->defio; 435 fb_deferred_io_init(&ch->info); 436 437 /* one-shot mode */ 438 lcdc_write_chan(ch, LDSM1R, 1); 439 440 /* enable "Frame End Interrupt Enable" bit */ 441 lcdc_write(priv, _LDINTR, LDINTR_FE); 442 443 } else { 444 /* continuous read mode */ 445 lcdc_write_chan(ch, LDSM1R, 0); 446 } 447 } 448 449 /* display output */ 450 lcdc_write(priv, _LDCNT1R, LCDC_ENABLE); 451 452 /* start the lcdc */ 453 sh_mobile_lcdc_start_stop(priv, 1); 454 455 /* tell the board code to enable the panel */ 456 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 457 ch = &priv->ch[k]; 458 board_cfg = &ch->cfg.board_cfg; 459 if (board_cfg->display_on) 460 board_cfg->display_on(board_cfg->board_data); 461 } 462 463 return 0; 464} 465 466static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) 467{ 468 struct sh_mobile_lcdc_chan *ch; 469 struct sh_mobile_lcdc_board_cfg *board_cfg; 470 int k; 471 472 /* clean up deferred io and ask board code to disable panel */ 473 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 474 ch = &priv->ch[k]; 475 476 /* deferred io mode: 477 * flush frame, and wait for frame end interrupt 478 * clean up deferred io and enable clock 479 */ 480 if (ch->info.fbdefio) { 481 ch->frame_end = 0; 482 schedule_delayed_work(&ch->info.deferred_work, 0); 483 wait_event(ch->frame_end_wait, ch->frame_end); 484 fb_deferred_io_cleanup(&ch->info); 485 ch->info.fbdefio = NULL; 486 sh_mobile_lcdc_clk_on(priv); 487 } 488 489 board_cfg = &ch->cfg.board_cfg; 490 if (board_cfg->display_off) 491 board_cfg->display_off(board_cfg->board_data); 492 493 } 494 495 /* stop the lcdc */ 496 sh_mobile_lcdc_start_stop(priv, 0); 497 498 /* stop clocks */ 499 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) 500 if (priv->ch[k].enabled) 501 sh_mobile_lcdc_clk_off(priv); 502} 503 504static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) 505{ 506 int ifm, miftyp; 507 508 switch (ch->cfg.interface_type) { 509 case RGB8: ifm = 0; miftyp = 0; break; 510 case RGB9: ifm = 0; miftyp = 4; break; 511 case RGB12A: ifm = 0; miftyp = 5; break; 512 case RGB12B: ifm = 0; miftyp = 6; break; 513 case RGB16: ifm = 0; miftyp = 7; break; 514 case RGB18: ifm = 0; miftyp = 10; break; 515 case RGB24: ifm = 0; miftyp = 11; break; 516 case SYS8A: ifm = 1; miftyp = 0; break; 517 case SYS8B: ifm = 1; miftyp = 1; break; 518 case SYS8C: ifm = 1; miftyp = 2; break; 519 case SYS8D: ifm = 1; miftyp = 3; break; 520 case SYS9: ifm = 1; miftyp = 4; break; 521 case SYS12: ifm = 1; miftyp = 5; break; 522 case SYS16A: ifm = 1; miftyp = 7; break; 523 case SYS16B: ifm = 1; miftyp = 8; break; 524 case SYS16C: ifm = 1; miftyp = 9; break; 525 case SYS18: ifm = 1; miftyp = 10; break; 526 case SYS24: ifm = 1; miftyp = 11; break; 527 default: goto bad; 528 } 529 530 /* SUBLCD only supports SYS interface */ 531 if (lcdc_chan_is_sublcd(ch)) { 532 if (ifm == 0) 533 goto bad; 534 else 535 ifm = 0; 536 } 537 538 ch->ldmt1r_value = (ifm << 12) | miftyp; 539 return 0; 540 bad: 541 return -EINVAL; 542} 543 544static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, 545 int clock_source, 546 struct sh_mobile_lcdc_priv *priv) 547{ 548#ifdef CONFIG_HAVE_CLK 549 char clk_name[8]; 550#endif 551 char *str; 552 int icksel; 553 554 switch (clock_source) { 555 case LCDC_CLK_BUS: str = "bus_clk"; icksel = 0; break; 556 case LCDC_CLK_PERIPHERAL: str = "peripheral_clk"; icksel = 1; break; 557 case LCDC_CLK_EXTERNAL: str = NULL; icksel = 2; break; 558 default: 559 return -EINVAL; 560 } 561 562 priv->lddckr = icksel << 16; 563 564#ifdef CONFIG_HAVE_CLK 565 atomic_set(&priv->clk_usecnt, -1); 566 snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id); 567 priv->clk = clk_get(&pdev->dev, clk_name); 568 if (IS_ERR(priv->clk)) { 569 dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); 570 return PTR_ERR(priv->clk); 571 } 572 573 if (str) { 574 priv->dot_clk = clk_get(&pdev->dev, str); 575 if (IS_ERR(priv->dot_clk)) { 576 dev_err(&pdev->dev, "cannot get dot clock %s\n", str); 577 clk_put(priv->clk); 578 return PTR_ERR(priv->dot_clk); 579 } 580 } 581#endif 582 583 return 0; 584} 585 586static int sh_mobile_lcdc_setcolreg(u_int regno, 587 u_int red, u_int green, u_int blue, 588 u_int transp, struct fb_info *info) 589{ 590 u32 *palette = info->pseudo_palette; 591 592 if (regno >= PALETTE_NR) 593 return -EINVAL; 594 595 /* only FB_VISUAL_TRUECOLOR supported */ 596 597 red >>= 16 - info->var.red.length; 598 green >>= 16 - info->var.green.length; 599 blue >>= 16 - info->var.blue.length; 600 transp >>= 16 - info->var.transp.length; 601 602 palette[regno] = (red << info->var.red.offset) | 603 (green << info->var.green.offset) | 604 (blue << info->var.blue.offset) | 605 (transp << info->var.transp.offset); 606 607 return 0; 608} 609 610static struct fb_fix_screeninfo sh_mobile_lcdc_fix = { 611 .id = "SH Mobile LCDC", 612 .type = FB_TYPE_PACKED_PIXELS, 613 .visual = FB_VISUAL_TRUECOLOR, 614 .accel = FB_ACCEL_NONE, 615}; 616 617static void sh_mobile_lcdc_fillrect(struct fb_info *info, 618 const struct fb_fillrect *rect) 619{ 620 sys_fillrect(info, rect); 621 sh_mobile_lcdc_deferred_io_touch(info); 622} 623 624static void sh_mobile_lcdc_copyarea(struct fb_info *info, 625 const struct fb_copyarea *area) 626{ 627 sys_copyarea(info, area); 628 sh_mobile_lcdc_deferred_io_touch(info); 629} 630 631static void sh_mobile_lcdc_imageblit(struct fb_info *info, 632 const struct fb_image *image) 633{ 634 sys_imageblit(info, image); 635 sh_mobile_lcdc_deferred_io_touch(info); 636} 637 638static struct fb_ops sh_mobile_lcdc_ops = { 639 .fb_setcolreg = sh_mobile_lcdc_setcolreg, 640 .fb_read = fb_sys_read, 641 .fb_write = fb_sys_write, 642 .fb_fillrect = sh_mobile_lcdc_fillrect, 643 .fb_copyarea = sh_mobile_lcdc_copyarea, 644 .fb_imageblit = sh_mobile_lcdc_imageblit, 645}; 646 647static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) 648{ 649 switch (bpp) { 650 case 16: /* PKF[4:0] = 00011 - RGB 565 */ 651 var->red.offset = 11; 652 var->red.length = 5; 653 var->green.offset = 5; 654 var->green.length = 6; 655 var->blue.offset = 0; 656 var->blue.length = 5; 657 var->transp.offset = 0; 658 var->transp.length = 0; 659 break; 660 661 case 32: /* PKF[4:0] = 00000 - RGB 888 662 * sh7722 pdf says 00RRGGBB but reality is GGBB00RR 663 * this may be because LDDDSR has word swap enabled.. 664 */ 665 var->red.offset = 0; 666 var->red.length = 8; 667 var->green.offset = 24; 668 var->green.length = 8; 669 var->blue.offset = 16; 670 var->blue.length = 8; 671 var->transp.offset = 0; 672 var->transp.length = 0; 673 break; 674 default: 675 return -EINVAL; 676 } 677 var->bits_per_pixel = bpp; 678 var->red.msb_right = 0; 679 var->green.msb_right = 0; 680 var->blue.msb_right = 0; 681 var->transp.msb_right = 0; 682 return 0; 683} 684 685static int sh_mobile_lcdc_suspend(struct device *dev) 686{ 687 struct platform_device *pdev = to_platform_device(dev); 688 689 sh_mobile_lcdc_stop(platform_get_drvdata(pdev)); 690 return 0; 691} 692 693static int sh_mobile_lcdc_resume(struct device *dev) 694{ 695 struct platform_device *pdev = to_platform_device(dev); 696 697 return sh_mobile_lcdc_start(platform_get_drvdata(pdev)); 698} 699 700static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { 701 .suspend = sh_mobile_lcdc_suspend, 702 .resume = sh_mobile_lcdc_resume, 703}; 704 705static int sh_mobile_lcdc_remove(struct platform_device *pdev); 706 707static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) 708{ 709 struct fb_info *info; 710 struct sh_mobile_lcdc_priv *priv; 711 struct sh_mobile_lcdc_info *pdata; 712 struct sh_mobile_lcdc_chan_cfg *cfg; 713 struct resource *res; 714 int error; 715 void *buf; 716 int i, j; 717 718 if (!pdev->dev.platform_data) { 719 dev_err(&pdev->dev, "no platform data defined\n"); 720 error = -EINVAL; 721 goto err0; 722 } 723 724 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 725 i = platform_get_irq(pdev, 0); 726 if (!res || i < 0) { 727 dev_err(&pdev->dev, "cannot get platform resources\n"); 728 error = -ENOENT; 729 goto err0; 730 } 731 732 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 733 if (!priv) { 734 dev_err(&pdev->dev, "cannot allocate device data\n"); 735 error = -ENOMEM; 736 goto err0; 737 } 738 739 error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED, 740 dev_name(&pdev->dev), priv); 741 if (error) { 742 dev_err(&pdev->dev, "unable to request irq\n"); 743 goto err1; 744 } 745 746 priv->irq = i; 747 platform_set_drvdata(pdev, priv); 748 pdata = pdev->dev.platform_data; 749 750 j = 0; 751 for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) { 752 priv->ch[j].lcdc = priv; 753 memcpy(&priv->ch[j].cfg, &pdata->ch[i], sizeof(pdata->ch[i])); 754 755 error = sh_mobile_lcdc_check_interface(&priv->ch[i]); 756 if (error) { 757 dev_err(&pdev->dev, "unsupported interface type\n"); 758 goto err1; 759 } 760 init_waitqueue_head(&priv->ch[i].frame_end_wait); 761 762 switch (pdata->ch[i].chan) { 763 case LCDC_CHAN_MAINLCD: 764 priv->ch[j].enabled = 1 << 1; 765 priv->ch[j].reg_offs = lcdc_offs_mainlcd; 766 j++; 767 break; 768 case LCDC_CHAN_SUBLCD: 769 priv->ch[j].enabled = 1 << 2; 770 priv->ch[j].reg_offs = lcdc_offs_sublcd; 771 j++; 772 break; 773 } 774 } 775 776 if (!j) { 777 dev_err(&pdev->dev, "no channels defined\n"); 778 error = -EINVAL; 779 goto err1; 780 } 781 782 error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv); 783 if (error) { 784 dev_err(&pdev->dev, "unable to setup clocks\n"); 785 goto err1; 786 } 787 788 priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1); 789 790 for (i = 0; i < j; i++) { 791 info = &priv->ch[i].info; 792 cfg = &priv->ch[i].cfg; 793 794 info->fbops = &sh_mobile_lcdc_ops; 795 info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres; 796 info->var.yres = info->var.yres_virtual = cfg->lcd_cfg.yres; 797 info->var.width = cfg->lcd_size_cfg.width; 798 info->var.height = cfg->lcd_size_cfg.height; 799 info->var.activate = FB_ACTIVATE_NOW; 800 error = sh_mobile_lcdc_set_bpp(&info->var, cfg->bpp); 801 if (error) 802 break; 803 804 info->fix = sh_mobile_lcdc_fix; 805 info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8); 806 info->fix.smem_len = info->fix.line_length * cfg->lcd_cfg.yres; 807 808 buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len, 809 &priv->ch[i].dma_handle, GFP_KERNEL); 810 if (!buf) { 811 dev_err(&pdev->dev, "unable to allocate buffer\n"); 812 error = -ENOMEM; 813 break; 814 } 815 816 info->pseudo_palette = &priv->ch[i].pseudo_palette; 817 info->flags = FBINFO_FLAG_DEFAULT; 818 819 error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); 820 if (error < 0) { 821 dev_err(&pdev->dev, "unable to allocate cmap\n"); 822 dma_free_coherent(&pdev->dev, info->fix.smem_len, 823 buf, priv->ch[i].dma_handle); 824 break; 825 } 826 827 memset(buf, 0, info->fix.smem_len); 828 info->fix.smem_start = priv->ch[i].dma_handle; 829 info->screen_base = buf; 830 info->device = &pdev->dev; 831 info->par = &priv->ch[i]; 832 } 833 834 if (error) 835 goto err1; 836 837 error = sh_mobile_lcdc_start(priv); 838 if (error) { 839 dev_err(&pdev->dev, "unable to start hardware\n"); 840 goto err1; 841 } 842 843 for (i = 0; i < j; i++) { 844 error = register_framebuffer(&priv->ch[i].info); 845 if (error < 0) 846 goto err1; 847 } 848 849 for (i = 0; i < j; i++) { 850 info = &priv->ch[i].info; 851 dev_info(info->dev, 852 "registered %s/%s as %dx%d %dbpp.\n", 853 pdev->name, 854 (priv->ch[i].cfg.chan == LCDC_CHAN_MAINLCD) ? 855 "mainlcd" : "sublcd", 856 (int) priv->ch[i].cfg.lcd_cfg.xres, 857 (int) priv->ch[i].cfg.lcd_cfg.yres, 858 priv->ch[i].cfg.bpp); 859 860 /* deferred io mode: disable clock to save power */ 861 if (info->fbdefio) 862 sh_mobile_lcdc_clk_off(priv); 863 } 864 865 return 0; 866 err1: 867 sh_mobile_lcdc_remove(pdev); 868 err0: 869 return error; 870} 871 872static int sh_mobile_lcdc_remove(struct platform_device *pdev) 873{ 874 struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); 875 struct fb_info *info; 876 int i; 877 878 for (i = 0; i < ARRAY_SIZE(priv->ch); i++) 879 if (priv->ch[i].info.dev) 880 unregister_framebuffer(&priv->ch[i].info); 881 882 sh_mobile_lcdc_stop(priv); 883 884 for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { 885 info = &priv->ch[i].info; 886 887 if (!info->device) 888 continue; 889 890 dma_free_coherent(&pdev->dev, info->fix.smem_len, 891 info->screen_base, priv->ch[i].dma_handle); 892 fb_dealloc_cmap(&info->cmap); 893 } 894 895#ifdef CONFIG_HAVE_CLK 896 if (priv->dot_clk) 897 clk_put(priv->dot_clk); 898 clk_put(priv->clk); 899#endif 900 901 if (priv->base) 902 iounmap(priv->base); 903 904 if (priv->irq) 905 free_irq(priv->irq, priv); 906 kfree(priv); 907 return 0; 908} 909 910static struct platform_driver sh_mobile_lcdc_driver = { 911 .driver = { 912 .name = "sh_mobile_lcdc_fb", 913 .owner = THIS_MODULE, 914 .pm = &sh_mobile_lcdc_dev_pm_ops, 915 }, 916 .probe = sh_mobile_lcdc_probe, 917 .remove = sh_mobile_lcdc_remove, 918}; 919 920static int __init sh_mobile_lcdc_init(void) 921{ 922 return platform_driver_register(&sh_mobile_lcdc_driver); 923} 924 925static void __exit sh_mobile_lcdc_exit(void) 926{ 927 platform_driver_unregister(&sh_mobile_lcdc_driver); 928} 929 930module_init(sh_mobile_lcdc_init); 931module_exit(sh_mobile_lcdc_exit); 932 933MODULE_DESCRIPTION("SuperH Mobile LCDC Framebuffer driver"); 934MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); 935MODULE_LICENSE("GPL v2");