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