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