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