aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/sh_mobile_lcdcfb.c
diff options
context:
space:
mode:
authorGlenn Elliott <gelliott@cs.unc.edu>2012-03-04 19:47:13 -0500
committerGlenn Elliott <gelliott@cs.unc.edu>2012-03-04 19:47:13 -0500
commitc71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch)
treeecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /drivers/video/sh_mobile_lcdcfb.c
parentea53c912f8a86a8567697115b6a0d8152beee5c8 (diff)
parent6a00f206debf8a5c8899055726ad127dbeeed098 (diff)
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts: litmus/sched_cedf.c
Diffstat (limited to 'drivers/video/sh_mobile_lcdcfb.c')
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c754
1 files changed, 603 insertions, 151 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index d72075a9f01c..019dbd3f12b2 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -12,7 +12,6 @@
12#include <linux/init.h> 12#include <linux/init.h>
13#include <linux/delay.h> 13#include <linux/delay.h>
14#include <linux/mm.h> 14#include <linux/mm.h>
15#include <linux/fb.h>
16#include <linux/clk.h> 15#include <linux/clk.h>
17#include <linux/pm_runtime.h> 16#include <linux/pm_runtime.h>
18#include <linux/platform_device.h> 17#include <linux/platform_device.h>
@@ -21,10 +20,15 @@
21#include <linux/vmalloc.h> 20#include <linux/vmalloc.h>
22#include <linux/ioctl.h> 21#include <linux/ioctl.h>
23#include <linux/slab.h> 22#include <linux/slab.h>
23#include <linux/console.h>
24#include <linux/backlight.h>
25#include <linux/gpio.h>
24#include <video/sh_mobile_lcdc.h> 26#include <video/sh_mobile_lcdc.h>
25#include <asm/atomic.h> 27#include <asm/atomic.h>
26 28
27#define PALETTE_NR 16 29#include "sh_mobile_lcdcfb.h"
30#include "sh_mobile_meram.h"
31
28#define SIDE_B_OFFSET 0x1000 32#define SIDE_B_OFFSET 0x1000
29#define MIRROR_OFFSET 0x2000 33#define MIRROR_OFFSET 0x2000
30 34
@@ -53,11 +57,8 @@ static int lcdc_shared_regs[] = {
53}; 57};
54#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) 58#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs)
55 59
56/* per-channel registers */ 60#define MAX_XRES 1920
57enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, 61#define MAX_YRES 1080
58 LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR,
59 LDHAJR,
60 NR_CH_REGS };
61 62
62static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { 63static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
63 [LDDCKPAT1R] = 0x400, 64 [LDDCKPAT1R] = 0x400,
@@ -69,6 +70,7 @@ static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
69 [LDSM1R] = 0x428, 70 [LDSM1R] = 0x428,
70 [LDSM2R] = 0x42c, 71 [LDSM2R] = 0x42c,
71 [LDSA1R] = 0x430, 72 [LDSA1R] = 0x430,
73 [LDSA2R] = 0x434,
72 [LDMLSR] = 0x438, 74 [LDMLSR] = 0x438,
73 [LDHCNR] = 0x448, 75 [LDHCNR] = 0x448,
74 [LDHSYNR] = 0x44c, 76 [LDHSYNR] = 0x44c,
@@ -112,23 +114,22 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
112#define LDRCNTR_MRC 0x00000001 114#define LDRCNTR_MRC 0x00000001
113#define LDSR_MRS 0x00000100 115#define LDSR_MRS 0x00000100
114 116
115struct sh_mobile_lcdc_priv; 117static const struct fb_videomode default_720p = {
116struct sh_mobile_lcdc_chan { 118 .name = "HDMI 720p",
117 struct sh_mobile_lcdc_priv *lcdc; 119 .xres = 1280,
118 unsigned long *reg_offs; 120 .yres = 720,
119 unsigned long ldmt1r_value; 121
120 unsigned long enabled; /* ME and SE in LDCNT2R */ 122 .left_margin = 220,
121 struct sh_mobile_lcdc_chan_cfg cfg; 123 .right_margin = 110,
122 u32 pseudo_palette[PALETTE_NR]; 124 .hsync_len = 40,
123 unsigned long saved_ch_regs[NR_CH_REGS]; 125
124 struct fb_info *info; 126 .upper_margin = 20,
125 dma_addr_t dma_handle; 127 .lower_margin = 5,
126 struct fb_deferred_io defio; 128 .vsync_len = 5,
127 struct scatterlist *sglist; 129
128 unsigned long frame_end; 130 .pixclock = 13468,
129 unsigned long pan_offset; 131 .refresh = 60,
130 wait_queue_head_t frame_end_wait; 132 .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT,
131 struct completion vsync_completion;
132}; 133};
133 134
134struct sh_mobile_lcdc_priv { 135struct sh_mobile_lcdc_priv {
@@ -142,6 +143,8 @@ struct sh_mobile_lcdc_priv {
142 struct notifier_block notifier; 143 struct notifier_block notifier;
143 unsigned long saved_shared_regs[NR_SHARED_REGS]; 144 unsigned long saved_shared_regs[NR_SHARED_REGS];
144 int started; 145 int started;
146 int forced_bpp; /* 2 channel LCDC must share bpp setting */
147 struct sh_mobile_meram_info *meram_dev;
145}; 148};
146 149
147static bool banked(int reg_nr) 150static bool banked(int reg_nr)
@@ -153,6 +156,7 @@ static bool banked(int reg_nr)
153 case LDDFR: 156 case LDDFR:
154 case LDSM1R: 157 case LDSM1R:
155 case LDSA1R: 158 case LDSA1R:
159 case LDSA2R:
156 case LDMLSR: 160 case LDMLSR:
157 case LDHCNR: 161 case LDHCNR:
158 case LDHSYNR: 162 case LDHSYNR:
@@ -409,8 +413,8 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
409 413
410static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) 414static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
411{ 415{
412 struct fb_var_screeninfo *var = &ch->info->var; 416 struct fb_var_screeninfo *var = &ch->info->var, *display_var = &ch->display_var;
413 unsigned long h_total, hsync_pos; 417 unsigned long h_total, hsync_pos, display_h_total;
414 u32 tmp; 418 u32 tmp;
415 419
416 tmp = ch->ldmt1r_value; 420 tmp = ch->ldmt1r_value;
@@ -428,31 +432,33 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
428 lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); 432 lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
429 433
430 /* horizontal configuration */ 434 /* horizontal configuration */
431 h_total = var->xres + var->hsync_len + 435 h_total = display_var->xres + display_var->hsync_len +
432 var->left_margin + var->right_margin; 436 display_var->left_margin + display_var->right_margin;
433 tmp = h_total / 8; /* HTCN */ 437 tmp = h_total / 8; /* HTCN */
434 tmp |= (var->xres / 8) << 16; /* HDCN */ 438 tmp |= (min(display_var->xres, var->xres) / 8) << 16; /* HDCN */
435 lcdc_write_chan(ch, LDHCNR, tmp); 439 lcdc_write_chan(ch, LDHCNR, tmp);
436 440
437 hsync_pos = var->xres + var->right_margin; 441 hsync_pos = display_var->xres + display_var->right_margin;
438 tmp = hsync_pos / 8; /* HSYNP */ 442 tmp = hsync_pos / 8; /* HSYNP */
439 tmp |= (var->hsync_len / 8) << 16; /* HSYNW */ 443 tmp |= (display_var->hsync_len / 8) << 16; /* HSYNW */
440 lcdc_write_chan(ch, LDHSYNR, tmp); 444 lcdc_write_chan(ch, LDHSYNR, tmp);
441 445
442 /* vertical configuration */ 446 /* vertical configuration */
443 tmp = var->yres + var->vsync_len + 447 tmp = display_var->yres + display_var->vsync_len +
444 var->upper_margin + var->lower_margin; /* VTLN */ 448 display_var->upper_margin + display_var->lower_margin; /* VTLN */
445 tmp |= var->yres << 16; /* VDLN */ 449 tmp |= min(display_var->yres, var->yres) << 16; /* VDLN */
446 lcdc_write_chan(ch, LDVLNR, tmp); 450 lcdc_write_chan(ch, LDVLNR, tmp);
447 451
448 tmp = var->yres + var->lower_margin; /* VSYNP */ 452 tmp = display_var->yres + display_var->lower_margin; /* VSYNP */
449 tmp |= var->vsync_len << 16; /* VSYNW */ 453 tmp |= display_var->vsync_len << 16; /* VSYNW */
450 lcdc_write_chan(ch, LDVSYNR, tmp); 454 lcdc_write_chan(ch, LDVSYNR, tmp);
451 455
452 /* Adjust horizontal synchronisation for HDMI */ 456 /* Adjust horizontal synchronisation for HDMI */
453 tmp = ((var->xres & 7) << 24) | 457 display_h_total = display_var->xres + display_var->hsync_len +
454 ((h_total & 7) << 16) | 458 display_var->left_margin + display_var->right_margin;
455 ((var->hsync_len & 7) << 8) | 459 tmp = ((display_var->xres & 7) << 24) |
460 ((display_h_total & 7) << 16) |
461 ((display_var->hsync_len & 7) << 8) |
456 hsync_pos; 462 hsync_pos;
457 lcdc_write_chan(ch, LDHAJR, tmp); 463 lcdc_write_chan(ch, LDHAJR, tmp);
458} 464}
@@ -460,16 +466,20 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
460static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) 466static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
461{ 467{
462 struct sh_mobile_lcdc_chan *ch; 468 struct sh_mobile_lcdc_chan *ch;
463 struct fb_videomode *lcd_cfg;
464 struct sh_mobile_lcdc_board_cfg *board_cfg; 469 struct sh_mobile_lcdc_board_cfg *board_cfg;
465 unsigned long tmp; 470 unsigned long tmp;
466 int k, m; 471 int bpp = 0;
467 int ret = 0; 472 unsigned long ldddsr;
473 int k, m, ret;
468 474
469 /* enable clocks before accessing the hardware */ 475 /* enable clocks before accessing the hardware */
470 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) 476 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
471 if (priv->ch[k].enabled) 477 if (priv->ch[k].enabled) {
472 sh_mobile_lcdc_clk_on(priv); 478 sh_mobile_lcdc_clk_on(priv);
479 if (!bpp)
480 bpp = priv->ch[k].info->var.bits_per_pixel;
481 }
482 }
473 483
474 /* reset */ 484 /* reset */
475 lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET); 485 lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
@@ -503,7 +513,8 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
503 m = 1 << 6; 513 m = 1 << 6;
504 tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); 514 tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);
505 515
506 lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000); 516 /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider denominator */
517 lcdc_write_chan(ch, LDDCKPAT1R, 0);
507 lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); 518 lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
508 } 519 }
509 520
@@ -518,7 +529,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
518 529
519 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 530 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
520 ch = &priv->ch[k]; 531 ch = &priv->ch[k];
521 lcd_cfg = &ch->cfg.lcd_cfg;
522 532
523 if (!ch->enabled) 533 if (!ch->enabled)
524 continue; 534 continue;
@@ -529,17 +539,36 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
529 lcdc_write_chan(ch, LDPMR, 0); 539 lcdc_write_chan(ch, LDPMR, 0);
530 540
531 board_cfg = &ch->cfg.board_cfg; 541 board_cfg = &ch->cfg.board_cfg;
532 if (board_cfg->setup_sys) 542 if (board_cfg->setup_sys) {
533 ret = board_cfg->setup_sys(board_cfg->board_data, ch, 543 ret = board_cfg->setup_sys(board_cfg->board_data,
534 &sh_mobile_lcdc_sys_bus_ops); 544 ch, &sh_mobile_lcdc_sys_bus_ops);
535 if (ret) 545 if (ret)
536 return ret; 546 return ret;
547 }
537 } 548 }
538 549
539 /* word and long word swap */ 550 /* word and long word swap */
540 lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); 551 ldddsr = lcdc_read(priv, _LDDDSR);
552 if (priv->ch[0].info->var.nonstd)
553 lcdc_write(priv, _LDDDSR, ldddsr | 7);
554 else {
555 switch (bpp) {
556 case 16:
557 lcdc_write(priv, _LDDDSR, ldddsr | 6);
558 break;
559 case 24:
560 lcdc_write(priv, _LDDDSR, ldddsr | 7);
561 break;
562 case 32:
563 lcdc_write(priv, _LDDDSR, ldddsr | 4);
564 break;
565 }
566 }
541 567
542 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 568 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
569 unsigned long base_addr_y;
570 unsigned long base_addr_c = 0;
571 int pitch;
543 ch = &priv->ch[k]; 572 ch = &priv->ch[k];
544 573
545 if (!priv->ch[k].enabled) 574 if (!priv->ch[k].enabled)
@@ -547,15 +576,95 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
547 576
548 /* set bpp format in PKF[4:0] */ 577 /* set bpp format in PKF[4:0] */
549 tmp = lcdc_read_chan(ch, LDDFR); 578 tmp = lcdc_read_chan(ch, LDDFR);
550 tmp &= ~(0x0001001f); 579 tmp &= ~0x0003031f;
551 tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0; 580 if (ch->info->var.nonstd) {
581 tmp |= (ch->info->var.nonstd << 16);
582 switch (ch->info->var.bits_per_pixel) {
583 case 12:
584 break;
585 case 16:
586 tmp |= (0x1 << 8);
587 break;
588 case 24:
589 tmp |= (0x2 << 8);
590 break;
591 }
592 } else {
593 switch (ch->info->var.bits_per_pixel) {
594 case 16:
595 tmp |= 0x03;
596 break;
597 case 24:
598 tmp |= 0x0b;
599 break;
600 case 32:
601 break;
602 }
603 }
552 lcdc_write_chan(ch, LDDFR, tmp); 604 lcdc_write_chan(ch, LDDFR, tmp);
553 605
606 base_addr_y = ch->info->fix.smem_start;
607 base_addr_c = base_addr_y +
608 ch->info->var.xres *
609 ch->info->var.yres_virtual;
610 pitch = ch->info->fix.line_length;
611
612 /* test if we can enable meram */
613 if (ch->cfg.meram_cfg && priv->meram_dev &&
614 priv->meram_dev->ops) {
615 struct sh_mobile_meram_cfg *cfg;
616 struct sh_mobile_meram_info *mdev;
617 unsigned long icb_addr_y, icb_addr_c;
618 int icb_pitch;
619 int pf;
620
621 cfg = ch->cfg.meram_cfg;
622 mdev = priv->meram_dev;
623 /* we need to de-init configured ICBs before we
624 * we can re-initialize them.
625 */
626 if (ch->meram_enabled)
627 mdev->ops->meram_unregister(mdev, cfg);
628
629 ch->meram_enabled = 0;
630
631 if (ch->info->var.nonstd) {
632 if (ch->info->var.bits_per_pixel == 24)
633 pf = SH_MOBILE_MERAM_PF_NV24;
634 else
635 pf = SH_MOBILE_MERAM_PF_NV;
636 } else {
637 pf = SH_MOBILE_MERAM_PF_RGB;
638 }
639
640 ret = mdev->ops->meram_register(mdev, cfg, pitch,
641 ch->info->var.yres,
642 pf,
643 base_addr_y,
644 base_addr_c,
645 &icb_addr_y,
646 &icb_addr_c,
647 &icb_pitch);
648 if (!ret) {
649 /* set LDSA1R value */
650 base_addr_y = icb_addr_y;
651 pitch = icb_pitch;
652
653 /* set LDSA2R value if required */
654 if (base_addr_c)
655 base_addr_c = icb_addr_c;
656
657 ch->meram_enabled = 1;
658 }
659 }
660
554 /* point out our frame buffer */ 661 /* point out our frame buffer */
555 lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start); 662 lcdc_write_chan(ch, LDSA1R, base_addr_y);
663 if (ch->info->var.nonstd)
664 lcdc_write_chan(ch, LDSA2R, base_addr_c);
556 665
557 /* set line size */ 666 /* set line size */
558 lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length); 667 lcdc_write_chan(ch, LDMLSR, pitch);
559 668
560 /* setup deferred io if SYS bus */ 669 /* setup deferred io if SYS bus */
561 tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; 670 tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
@@ -591,8 +700,15 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
591 continue; 700 continue;
592 701
593 board_cfg = &ch->cfg.board_cfg; 702 board_cfg = &ch->cfg.board_cfg;
594 if (board_cfg->display_on) 703 if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
595 board_cfg->display_on(board_cfg->board_data, ch->info); 704 board_cfg->display_on(board_cfg->board_data, ch->info);
705 module_put(board_cfg->owner);
706 }
707
708 if (ch->bl) {
709 ch->bl->props.power = FB_BLANK_UNBLANK;
710 backlight_update_status(ch->bl);
711 }
596 } 712 }
597 713
598 return 0; 714 return 0;
@@ -614,7 +730,7 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
614 * flush frame, and wait for frame end interrupt 730 * flush frame, and wait for frame end interrupt
615 * clean up deferred io and enable clock 731 * clean up deferred io and enable clock
616 */ 732 */
617 if (ch->info->fbdefio) { 733 if (ch->info && ch->info->fbdefio) {
618 ch->frame_end = 0; 734 ch->frame_end = 0;
619 schedule_delayed_work(&ch->info->deferred_work, 0); 735 schedule_delayed_work(&ch->info->deferred_work, 0);
620 wait_event(ch->frame_end_wait, ch->frame_end); 736 wait_event(ch->frame_end_wait, ch->frame_end);
@@ -623,9 +739,27 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
623 sh_mobile_lcdc_clk_on(priv); 739 sh_mobile_lcdc_clk_on(priv);
624 } 740 }
625 741
742 if (ch->bl) {
743 ch->bl->props.power = FB_BLANK_POWERDOWN;
744 backlight_update_status(ch->bl);
745 }
746
626 board_cfg = &ch->cfg.board_cfg; 747 board_cfg = &ch->cfg.board_cfg;
627 if (board_cfg->display_off) 748 if (board_cfg->display_off && try_module_get(board_cfg->owner)) {
628 board_cfg->display_off(board_cfg->board_data); 749 board_cfg->display_off(board_cfg->board_data);
750 module_put(board_cfg->owner);
751 }
752
753 /* disable the meram */
754 if (ch->meram_enabled) {
755 struct sh_mobile_meram_cfg *cfg;
756 struct sh_mobile_meram_info *mdev;
757 cfg = ch->cfg.meram_cfg;
758 mdev = priv->meram_dev;
759 mdev->ops->meram_unregister(mdev, cfg);
760 ch->meram_enabled = 0;
761 }
762
629 } 763 }
630 764
631 /* stop the lcdc */ 765 /* stop the lcdc */
@@ -704,7 +838,6 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
704 return PTR_ERR(priv->dot_clk); 838 return PTR_ERR(priv->dot_clk);
705 } 839 }
706 } 840 }
707 atomic_set(&priv->hw_usecnt, -1);
708 841
709 /* Runtime PM support involves two step for this driver: 842 /* Runtime PM support involves two step for this driver:
710 * 1) Enable Runtime PM 843 * 1) Enable Runtime PM
@@ -778,9 +911,15 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
778 struct sh_mobile_lcdc_priv *priv = ch->lcdc; 911 struct sh_mobile_lcdc_priv *priv = ch->lcdc;
779 unsigned long ldrcntr; 912 unsigned long ldrcntr;
780 unsigned long new_pan_offset; 913 unsigned long new_pan_offset;
914 unsigned long base_addr_y, base_addr_c;
915 unsigned long c_offset;
781 916
782 new_pan_offset = (var->yoffset * info->fix.line_length) + 917 if (!var->nonstd)
783 (var->xoffset * (info->var.bits_per_pixel / 8)); 918 new_pan_offset = (var->yoffset * info->fix.line_length) +
919 (var->xoffset * (info->var.bits_per_pixel / 8));
920 else
921 new_pan_offset = (var->yoffset * info->fix.line_length) +
922 (var->xoffset);
784 923
785 if (new_pan_offset == ch->pan_offset) 924 if (new_pan_offset == ch->pan_offset)
786 return 0; /* No change, do nothing */ 925 return 0; /* No change, do nothing */
@@ -788,7 +927,46 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
788 ldrcntr = lcdc_read(priv, _LDRCNTR); 927 ldrcntr = lcdc_read(priv, _LDRCNTR);
789 928
790 /* Set the source address for the next refresh */ 929 /* Set the source address for the next refresh */
791 lcdc_write_chan_mirror(ch, LDSA1R, ch->dma_handle + new_pan_offset); 930 base_addr_y = ch->dma_handle + new_pan_offset;
931 if (var->nonstd) {
932 /* Set y offset */
933 c_offset = (var->yoffset *
934 info->fix.line_length *
935 (info->var.bits_per_pixel - 8)) / 8;
936 base_addr_c = ch->dma_handle + var->xres * var->yres_virtual +
937 c_offset;
938 /* Set x offset */
939 if (info->var.bits_per_pixel == 24)
940 base_addr_c += 2 * var->xoffset;
941 else
942 base_addr_c += var->xoffset;
943 } else
944 base_addr_c = 0;
945
946 if (!ch->meram_enabled) {
947 lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
948 if (base_addr_c)
949 lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
950 } else {
951 struct sh_mobile_meram_cfg *cfg;
952 struct sh_mobile_meram_info *mdev;
953 unsigned long icb_addr_y, icb_addr_c;
954 int ret;
955
956 cfg = ch->cfg.meram_cfg;
957 mdev = priv->meram_dev;
958 ret = mdev->ops->meram_update(mdev, cfg,
959 base_addr_y, base_addr_c,
960 &icb_addr_y, &icb_addr_c);
961 if (ret)
962 return ret;
963
964 lcdc_write_chan_mirror(ch, LDSA1R, icb_addr_y);
965 if (icb_addr_c)
966 lcdc_write_chan_mirror(ch, LDSA2R, icb_addr_c);
967
968 }
969
792 if (lcdc_chan_is_sublcd(ch)) 970 if (lcdc_chan_is_sublcd(ch))
793 lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS); 971 lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS);
794 else 972 else
@@ -837,6 +1015,153 @@ static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
837 return retval; 1015 return retval;
838} 1016}
839 1017
1018static void sh_mobile_fb_reconfig(struct fb_info *info)
1019{
1020 struct sh_mobile_lcdc_chan *ch = info->par;
1021 struct fb_videomode mode1, mode2;
1022 struct fb_event event;
1023 int evnt = FB_EVENT_MODE_CHANGE_ALL;
1024
1025 if (ch->use_count > 1 || (ch->use_count == 1 && !info->fbcon_par))
1026 /* More framebuffer users are active */
1027 return;
1028
1029 fb_var_to_videomode(&mode1, &ch->display_var);
1030 fb_var_to_videomode(&mode2, &info->var);
1031
1032 if (fb_mode_is_equal(&mode1, &mode2))
1033 return;
1034
1035 /* Display has been re-plugged, framebuffer is free now, reconfigure */
1036 if (fb_set_var(info, &ch->display_var) < 0)
1037 /* Couldn't reconfigure, hopefully, can continue as before */
1038 return;
1039
1040 if (info->var.nonstd)
1041 info->fix.line_length = mode1.xres;
1042 else
1043 info->fix.line_length = mode1.xres * (ch->cfg.bpp / 8);
1044
1045 /*
1046 * fb_set_var() calls the notifier change internally, only if
1047 * FBINFO_MISC_USEREVENT flag is set. Since we do not want to fake a
1048 * user event, we have to call the chain ourselves.
1049 */
1050 event.info = info;
1051 event.data = &mode1;
1052 fb_notifier_call_chain(evnt, &event);
1053}
1054
1055/*
1056 * Locking: both .fb_release() and .fb_open() are called with info->lock held if
1057 * user == 1, or with console sem held, if user == 0.
1058 */
1059static int sh_mobile_release(struct fb_info *info, int user)
1060{
1061 struct sh_mobile_lcdc_chan *ch = info->par;
1062
1063 mutex_lock(&ch->open_lock);
1064 dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count);
1065
1066 ch->use_count--;
1067
1068 /* Nothing to reconfigure, when called from fbcon */
1069 if (user) {
1070 console_lock();
1071 sh_mobile_fb_reconfig(info);
1072 console_unlock();
1073 }
1074
1075 mutex_unlock(&ch->open_lock);
1076
1077 return 0;
1078}
1079
1080static int sh_mobile_open(struct fb_info *info, int user)
1081{
1082 struct sh_mobile_lcdc_chan *ch = info->par;
1083
1084 mutex_lock(&ch->open_lock);
1085 ch->use_count++;
1086
1087 dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count);
1088 mutex_unlock(&ch->open_lock);
1089
1090 return 0;
1091}
1092
1093static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
1094{
1095 struct sh_mobile_lcdc_chan *ch = info->par;
1096 struct sh_mobile_lcdc_priv *p = ch->lcdc;
1097
1098 if (var->xres > MAX_XRES || var->yres > MAX_YRES ||
1099 var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) {
1100 dev_warn(info->dev, "Invalid info: %u-%u-%u-%u x %u-%u-%u-%u @ %lukHz!\n",
1101 var->left_margin, var->xres, var->right_margin, var->hsync_len,
1102 var->upper_margin, var->yres, var->lower_margin, var->vsync_len,
1103 PICOS2KHZ(var->pixclock));
1104 return -EINVAL;
1105 }
1106
1107 /* only accept the forced_bpp for dual channel configurations */
1108 if (p->forced_bpp && p->forced_bpp != var->bits_per_pixel)
1109 return -EINVAL;
1110
1111 switch (var->bits_per_pixel) {
1112 case 16: /* PKF[4:0] = 00011 - RGB 565 */
1113 case 24: /* PKF[4:0] = 01011 - RGB 888 */
1114 case 32: /* PKF[4:0] = 00000 - RGBA 888 */
1115 break;
1116 default:
1117 return -EINVAL;
1118 }
1119
1120 return 0;
1121}
1122
1123/*
1124 * Screen blanking. Behavior is as follows:
1125 * FB_BLANK_UNBLANK: screen unblanked, clocks enabled
1126 * FB_BLANK_NORMAL: screen blanked, clocks enabled
1127 * FB_BLANK_VSYNC,
1128 * FB_BLANK_HSYNC,
1129 * FB_BLANK_POWEROFF: screen blanked, clocks disabled
1130 */
1131static int sh_mobile_lcdc_blank(int blank, struct fb_info *info)
1132{
1133 struct sh_mobile_lcdc_chan *ch = info->par;
1134 struct sh_mobile_lcdc_priv *p = ch->lcdc;
1135
1136 /* blank the screen? */
1137 if (blank > FB_BLANK_UNBLANK && ch->blank_status == FB_BLANK_UNBLANK) {
1138 struct fb_fillrect rect = {
1139 .width = info->var.xres,
1140 .height = info->var.yres,
1141 };
1142 sh_mobile_lcdc_fillrect(info, &rect);
1143 }
1144 /* turn clocks on? */
1145 if (blank <= FB_BLANK_NORMAL && ch->blank_status > FB_BLANK_NORMAL) {
1146 sh_mobile_lcdc_clk_on(p);
1147 }
1148 /* turn clocks off? */
1149 if (blank > FB_BLANK_NORMAL && ch->blank_status <= FB_BLANK_NORMAL) {
1150 /* make sure the screen is updated with the black fill before
1151 * switching the clocks off. one vsync is not enough since
1152 * blanking may occur in the middle of a refresh. deferred io
1153 * mode will reenable the clocks and update the screen in time,
1154 * so it does not need this. */
1155 if (!info->fbdefio) {
1156 sh_mobile_wait_for_vsync(info);
1157 sh_mobile_wait_for_vsync(info);
1158 }
1159 sh_mobile_lcdc_clk_off(p);
1160 }
1161
1162 ch->blank_status = blank;
1163 return 0;
1164}
840 1165
841static struct fb_ops sh_mobile_lcdc_ops = { 1166static struct fb_ops sh_mobile_lcdc_ops = {
842 .owner = THIS_MODULE, 1167 .owner = THIS_MODULE,
@@ -846,12 +1171,89 @@ static struct fb_ops sh_mobile_lcdc_ops = {
846 .fb_fillrect = sh_mobile_lcdc_fillrect, 1171 .fb_fillrect = sh_mobile_lcdc_fillrect,
847 .fb_copyarea = sh_mobile_lcdc_copyarea, 1172 .fb_copyarea = sh_mobile_lcdc_copyarea,
848 .fb_imageblit = sh_mobile_lcdc_imageblit, 1173 .fb_imageblit = sh_mobile_lcdc_imageblit,
1174 .fb_blank = sh_mobile_lcdc_blank,
849 .fb_pan_display = sh_mobile_fb_pan_display, 1175 .fb_pan_display = sh_mobile_fb_pan_display,
850 .fb_ioctl = sh_mobile_ioctl, 1176 .fb_ioctl = sh_mobile_ioctl,
1177 .fb_open = sh_mobile_open,
1178 .fb_release = sh_mobile_release,
1179 .fb_check_var = sh_mobile_check_var,
851}; 1180};
852 1181
853static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) 1182static int sh_mobile_lcdc_update_bl(struct backlight_device *bdev)
1183{
1184 struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev);
1185 struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg;
1186 int brightness = bdev->props.brightness;
1187
1188 if (bdev->props.power != FB_BLANK_UNBLANK ||
1189 bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
1190 brightness = 0;
1191
1192 return cfg->set_brightness(cfg->board_data, brightness);
1193}
1194
1195static int sh_mobile_lcdc_get_brightness(struct backlight_device *bdev)
1196{
1197 struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev);
1198 struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg;
1199
1200 return cfg->get_brightness(cfg->board_data);
1201}
1202
1203static int sh_mobile_lcdc_check_fb(struct backlight_device *bdev,
1204 struct fb_info *info)
854{ 1205{
1206 return (info->bl_dev == bdev);
1207}
1208
1209static struct backlight_ops sh_mobile_lcdc_bl_ops = {
1210 .options = BL_CORE_SUSPENDRESUME,
1211 .update_status = sh_mobile_lcdc_update_bl,
1212 .get_brightness = sh_mobile_lcdc_get_brightness,
1213 .check_fb = sh_mobile_lcdc_check_fb,
1214};
1215
1216static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent,
1217 struct sh_mobile_lcdc_chan *ch)
1218{
1219 struct backlight_device *bl;
1220
1221 bl = backlight_device_register(ch->cfg.bl_info.name, parent, ch,
1222 &sh_mobile_lcdc_bl_ops, NULL);
1223 if (IS_ERR(bl)) {
1224 dev_err(parent, "unable to register backlight device: %ld\n",
1225 PTR_ERR(bl));
1226 return NULL;
1227 }
1228
1229 bl->props.max_brightness = ch->cfg.bl_info.max_brightness;
1230 bl->props.brightness = bl->props.max_brightness;
1231 backlight_update_status(bl);
1232
1233 return bl;
1234}
1235
1236static void sh_mobile_lcdc_bl_remove(struct backlight_device *bdev)
1237{
1238 backlight_device_unregister(bdev);
1239}
1240
1241static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp,
1242 int nonstd)
1243{
1244 if (nonstd) {
1245 switch (bpp) {
1246 case 12:
1247 case 16:
1248 case 24:
1249 var->bits_per_pixel = bpp;
1250 var->nonstd = nonstd;
1251 return 0;
1252 default:
1253 return -EINVAL;
1254 }
1255 }
1256
855 switch (bpp) { 1257 switch (bpp) {
856 case 16: /* PKF[4:0] = 00011 - RGB 565 */ 1258 case 16: /* PKF[4:0] = 00011 - RGB 565 */
857 var->red.offset = 11; 1259 var->red.offset = 11;
@@ -864,19 +1266,27 @@ static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
864 var->transp.length = 0; 1266 var->transp.length = 0;
865 break; 1267 break;
866 1268
867 case 32: /* PKF[4:0] = 00000 - RGB 888 1269 case 24: /* PKF[4:0] = 01011 - RGB 888 */
868 * sh7722 pdf says 00RRGGBB but reality is GGBB00RR 1270 var->red.offset = 16;
869 * this may be because LDDDSR has word swap enabled..
870 */
871 var->red.offset = 0;
872 var->red.length = 8; 1271 var->red.length = 8;
873 var->green.offset = 24; 1272 var->green.offset = 8;
874 var->green.length = 8; 1273 var->green.length = 8;
875 var->blue.offset = 16; 1274 var->blue.offset = 0;
876 var->blue.length = 8; 1275 var->blue.length = 8;
877 var->transp.offset = 0; 1276 var->transp.offset = 0;
878 var->transp.length = 0; 1277 var->transp.length = 0;
879 break; 1278 break;
1279
1280 case 32: /* PKF[4:0] = 00000 - RGBA 888 */
1281 var->red.offset = 16;
1282 var->red.length = 8;
1283 var->green.offset = 8;
1284 var->green.length = 8;
1285 var->blue.offset = 0;
1286 var->blue.length = 8;
1287 var->transp.offset = 24;
1288 var->transp.length = 8;
1289 break;
880 default: 1290 default:
881 return -EINVAL; 1291 return -EINVAL;
882 } 1292 }
@@ -958,6 +1368,7 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
958 .runtime_resume = sh_mobile_lcdc_runtime_resume, 1368 .runtime_resume = sh_mobile_lcdc_runtime_resume,
959}; 1369};
960 1370
1371/* locking: called with info->lock held */
961static int sh_mobile_lcdc_notify(struct notifier_block *nb, 1372static int sh_mobile_lcdc_notify(struct notifier_block *nb,
962 unsigned long action, void *data) 1373 unsigned long action, void *data)
963{ 1374{
@@ -965,53 +1376,36 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb,
965 struct fb_info *info = event->info; 1376 struct fb_info *info = event->info;
966 struct sh_mobile_lcdc_chan *ch = info->par; 1377 struct sh_mobile_lcdc_chan *ch = info->par;
967 struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; 1378 struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg;
968 struct fb_var_screeninfo *var;
969 1379
970 if (&ch->lcdc->notifier != nb) 1380 if (&ch->lcdc->notifier != nb)
971 return 0; 1381 return NOTIFY_DONE;
972 1382
973 dev_dbg(info->dev, "%s(): action = %lu, data = %p\n", 1383 dev_dbg(info->dev, "%s(): action = %lu, data = %p\n",
974 __func__, action, event->data); 1384 __func__, action, event->data);
975 1385
976 switch(action) { 1386 switch(action) {
977 case FB_EVENT_SUSPEND: 1387 case FB_EVENT_SUSPEND:
978 if (board_cfg->display_off) 1388 if (board_cfg->display_off && try_module_get(board_cfg->owner)) {
979 board_cfg->display_off(board_cfg->board_data); 1389 board_cfg->display_off(board_cfg->board_data);
980 pm_runtime_put(info->device); 1390 module_put(board_cfg->owner);
1391 }
1392 sh_mobile_lcdc_stop(ch->lcdc);
981 break; 1393 break;
982 case FB_EVENT_RESUME: 1394 case FB_EVENT_RESUME:
983 var = &info->var; 1395 mutex_lock(&ch->open_lock);
1396 sh_mobile_fb_reconfig(info);
1397 mutex_unlock(&ch->open_lock);
984 1398
985 /* HDMI must be enabled before LCDC configuration */ 1399 /* HDMI must be enabled before LCDC configuration */
986 if (board_cfg->display_on) 1400 if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
987 board_cfg->display_on(board_cfg->board_data, ch->info); 1401 board_cfg->display_on(board_cfg->board_data, info);
988 1402 module_put(board_cfg->owner);
989 /* Check if the new display is not in our modelist */
990 if (ch->info->modelist.next &&
991 !fb_match_mode(var, &ch->info->modelist)) {
992 struct fb_videomode mode;
993 int ret;
994
995 /* Can we handle this display? */
996 if (var->xres > ch->cfg.lcd_cfg.xres ||
997 var->yres > ch->cfg.lcd_cfg.yres)
998 return -ENOMEM;
999
1000 /* Add to the modelist */
1001 fb_var_to_videomode(&mode, var);
1002 ret = fb_add_videomode(&mode, &ch->info->modelist);
1003 if (ret < 0)
1004 return ret;
1005 } 1403 }
1006 1404
1007 pm_runtime_get_sync(info->device); 1405 sh_mobile_lcdc_start(ch->lcdc);
1008
1009 sh_mobile_lcdc_geometry(ch);
1010
1011 break;
1012 } 1406 }
1013 1407
1014 return 0; 1408 return NOTIFY_OK;
1015} 1409}
1016 1410
1017static int sh_mobile_lcdc_remove(struct platform_device *pdev); 1411static int sh_mobile_lcdc_remove(struct platform_device *pdev);
@@ -1020,14 +1414,13 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1020{ 1414{
1021 struct fb_info *info; 1415 struct fb_info *info;
1022 struct sh_mobile_lcdc_priv *priv; 1416 struct sh_mobile_lcdc_priv *priv;
1023 struct sh_mobile_lcdc_info *pdata; 1417 struct sh_mobile_lcdc_info *pdata = pdev->dev.platform_data;
1024 struct sh_mobile_lcdc_chan_cfg *cfg;
1025 struct resource *res; 1418 struct resource *res;
1026 int error; 1419 int error;
1027 void *buf; 1420 void *buf;
1028 int i, j; 1421 int i, j;
1029 1422
1030 if (!pdev->dev.platform_data) { 1423 if (!pdata) {
1031 dev_err(&pdev->dev, "no platform data defined\n"); 1424 dev_err(&pdev->dev, "no platform data defined\n");
1032 return -EINVAL; 1425 return -EINVAL;
1033 } 1426 }
@@ -1055,31 +1448,37 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1055 } 1448 }
1056 1449
1057 priv->irq = i; 1450 priv->irq = i;
1058 pdata = pdev->dev.platform_data; 1451 atomic_set(&priv->hw_usecnt, -1);
1059 1452
1060 j = 0; 1453 j = 0;
1061 for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) { 1454 for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) {
1062 priv->ch[j].lcdc = priv; 1455 struct sh_mobile_lcdc_chan *ch = priv->ch + j;
1063 memcpy(&priv->ch[j].cfg, &pdata->ch[i], sizeof(pdata->ch[i])); 1456
1457 ch->lcdc = priv;
1458 memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i]));
1064 1459
1065 error = sh_mobile_lcdc_check_interface(&priv->ch[j]); 1460 error = sh_mobile_lcdc_check_interface(ch);
1066 if (error) { 1461 if (error) {
1067 dev_err(&pdev->dev, "unsupported interface type\n"); 1462 dev_err(&pdev->dev, "unsupported interface type\n");
1068 goto err1; 1463 goto err1;
1069 } 1464 }
1070 init_waitqueue_head(&priv->ch[j].frame_end_wait); 1465 init_waitqueue_head(&ch->frame_end_wait);
1071 init_completion(&priv->ch[j].vsync_completion); 1466 init_completion(&ch->vsync_completion);
1072 priv->ch[j].pan_offset = 0; 1467 ch->pan_offset = 0;
1468
1469 /* probe the backlight is there is one defined */
1470 if (ch->cfg.bl_info.max_brightness)
1471 ch->bl = sh_mobile_lcdc_bl_probe(&pdev->dev, ch);
1073 1472
1074 switch (pdata->ch[i].chan) { 1473 switch (pdata->ch[i].chan) {
1075 case LCDC_CHAN_MAINLCD: 1474 case LCDC_CHAN_MAINLCD:
1076 priv->ch[j].enabled = 1 << 1; 1475 ch->enabled = 1 << 1;
1077 priv->ch[j].reg_offs = lcdc_offs_mainlcd; 1476 ch->reg_offs = lcdc_offs_mainlcd;
1078 j++; 1477 j++;
1079 break; 1478 break;
1080 case LCDC_CHAN_SUBLCD: 1479 case LCDC_CHAN_SUBLCD:
1081 priv->ch[j].enabled = 1 << 2; 1480 ch->enabled = 1 << 2;
1082 priv->ch[j].reg_offs = lcdc_offs_sublcd; 1481 ch->reg_offs = lcdc_offs_sublcd;
1083 j++; 1482 j++;
1084 break; 1483 break;
1085 } 1484 }
@@ -1091,6 +1490,10 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1091 goto err1; 1490 goto err1;
1092 } 1491 }
1093 1492
1493 /* for dual channel LCDC (MAIN + SUB) force shared bpp setting */
1494 if (j == 2)
1495 priv->forced_bpp = pdata->ch[0].bpp;
1496
1094 priv->base = ioremap_nocache(res->start, resource_size(res)); 1497 priv->base = ioremap_nocache(res->start, resource_size(res));
1095 if (!priv->base) 1498 if (!priv->base)
1096 goto err1; 1499 goto err1;
@@ -1101,71 +1504,112 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1101 goto err1; 1504 goto err1;
1102 } 1505 }
1103 1506
1507 priv->meram_dev = pdata->meram_dev;
1508
1104 for (i = 0; i < j; i++) { 1509 for (i = 0; i < j; i++) {
1105 struct fb_var_screeninfo *var; 1510 struct fb_var_screeninfo *var;
1106 struct fb_videomode *lcd_cfg; 1511 const struct fb_videomode *lcd_cfg, *max_cfg = NULL;
1107 cfg = &priv->ch[i].cfg; 1512 struct sh_mobile_lcdc_chan *ch = priv->ch + i;
1108 1513 struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg;
1109 priv->ch[i].info = framebuffer_alloc(0, &pdev->dev); 1514 const struct fb_videomode *mode = cfg->lcd_cfg;
1110 if (!priv->ch[i].info) { 1515 unsigned long max_size = 0;
1516 int k;
1517 int num_cfg;
1518
1519 ch->info = framebuffer_alloc(0, &pdev->dev);
1520 if (!ch->info) {
1111 dev_err(&pdev->dev, "unable to allocate fb_info\n"); 1521 dev_err(&pdev->dev, "unable to allocate fb_info\n");
1112 error = -ENOMEM; 1522 error = -ENOMEM;
1113 break; 1523 break;
1114 } 1524 }
1115 1525
1116 info = priv->ch[i].info; 1526 info = ch->info;
1117 var = &info->var; 1527 var = &info->var;
1118 lcd_cfg = &cfg->lcd_cfg;
1119 info->fbops = &sh_mobile_lcdc_ops; 1528 info->fbops = &sh_mobile_lcdc_ops;
1120 var->xres = var->xres_virtual = lcd_cfg->xres; 1529 info->par = ch;
1121 var->yres = lcd_cfg->yres; 1530
1122 /* Default Y virtual resolution is 2x panel size */ 1531 mutex_init(&ch->open_lock);
1123 var->yres_virtual = var->yres * 2; 1532
1533 for (k = 0, lcd_cfg = mode;
1534 k < cfg->num_cfg && lcd_cfg;
1535 k++, lcd_cfg++) {
1536 unsigned long size = lcd_cfg->yres * lcd_cfg->xres;
1537 /* NV12 buffers must have even number of lines */
1538 if ((cfg->nonstd) && cfg->bpp == 12 &&
1539 (lcd_cfg->yres & 0x1)) {
1540 dev_err(&pdev->dev, "yres must be multiple of 2"
1541 " for YCbCr420 mode.\n");
1542 error = -EINVAL;
1543 goto err1;
1544 }
1545
1546 if (size > max_size) {
1547 max_cfg = lcd_cfg;
1548 max_size = size;
1549 }
1550 }
1551
1552 if (!mode)
1553 max_size = MAX_XRES * MAX_YRES;
1554 else if (max_cfg)
1555 dev_dbg(&pdev->dev, "Found largest videomode %ux%u\n",
1556 max_cfg->xres, max_cfg->yres);
1557
1558 info->fix = sh_mobile_lcdc_fix;
1559 info->fix.smem_len = max_size * 2 * cfg->bpp / 8;
1560
1561 /* Only pan in 2 line steps for NV12 */
1562 if (cfg->nonstd && cfg->bpp == 12)
1563 info->fix.ypanstep = 2;
1564
1565 if (!mode) {
1566 mode = &default_720p;
1567 num_cfg = 1;
1568 } else {
1569 num_cfg = cfg->num_cfg;
1570 }
1571
1572 fb_videomode_to_modelist(mode, num_cfg, &info->modelist);
1573
1574 fb_videomode_to_var(var, mode);
1124 var->width = cfg->lcd_size_cfg.width; 1575 var->width = cfg->lcd_size_cfg.width;
1125 var->height = cfg->lcd_size_cfg.height; 1576 var->height = cfg->lcd_size_cfg.height;
1577 /* Default Y virtual resolution is 2x panel size */
1578 var->yres_virtual = var->yres * 2;
1126 var->activate = FB_ACTIVATE_NOW; 1579 var->activate = FB_ACTIVATE_NOW;
1127 var->left_margin = lcd_cfg->left_margin; 1580
1128 var->right_margin = lcd_cfg->right_margin; 1581 error = sh_mobile_lcdc_set_bpp(var, cfg->bpp, cfg->nonstd);
1129 var->upper_margin = lcd_cfg->upper_margin;
1130 var->lower_margin = lcd_cfg->lower_margin;
1131 var->hsync_len = lcd_cfg->hsync_len;
1132 var->vsync_len = lcd_cfg->vsync_len;
1133 var->sync = lcd_cfg->sync;
1134 var->pixclock = lcd_cfg->pixclock;
1135
1136 error = sh_mobile_lcdc_set_bpp(var, cfg->bpp);
1137 if (error) 1582 if (error)
1138 break; 1583 break;
1139 1584
1140 info->fix = sh_mobile_lcdc_fix;
1141 info->fix.line_length = lcd_cfg->xres * (cfg->bpp / 8);
1142 info->fix.smem_len = info->fix.line_length *
1143 var->yres_virtual;
1144
1145 buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len, 1585 buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len,
1146 &priv->ch[i].dma_handle, GFP_KERNEL); 1586 &ch->dma_handle, GFP_KERNEL);
1147 if (!buf) { 1587 if (!buf) {
1148 dev_err(&pdev->dev, "unable to allocate buffer\n"); 1588 dev_err(&pdev->dev, "unable to allocate buffer\n");
1149 error = -ENOMEM; 1589 error = -ENOMEM;
1150 break; 1590 break;
1151 } 1591 }
1152 1592
1153 info->pseudo_palette = &priv->ch[i].pseudo_palette; 1593 info->pseudo_palette = &ch->pseudo_palette;
1154 info->flags = FBINFO_FLAG_DEFAULT; 1594 info->flags = FBINFO_FLAG_DEFAULT;
1155 1595
1156 error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); 1596 error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
1157 if (error < 0) { 1597 if (error < 0) {
1158 dev_err(&pdev->dev, "unable to allocate cmap\n"); 1598 dev_err(&pdev->dev, "unable to allocate cmap\n");
1159 dma_free_coherent(&pdev->dev, info->fix.smem_len, 1599 dma_free_coherent(&pdev->dev, info->fix.smem_len,
1160 buf, priv->ch[i].dma_handle); 1600 buf, ch->dma_handle);
1161 break; 1601 break;
1162 } 1602 }
1163 1603
1164 memset(buf, 0, info->fix.smem_len); 1604 info->fix.smem_start = ch->dma_handle;
1165 info->fix.smem_start = priv->ch[i].dma_handle; 1605 if (var->nonstd)
1606 info->fix.line_length = var->xres;
1607 else
1608 info->fix.line_length = var->xres * (cfg->bpp / 8);
1609
1166 info->screen_base = buf; 1610 info->screen_base = buf;
1167 info->device = &pdev->dev; 1611 info->device = &pdev->dev;
1168 info->par = &priv->ch[i]; 1612 ch->display_var = *var;
1169 } 1613 }
1170 1614
1171 if (error) 1615 if (error)
@@ -1191,6 +1635,8 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1191 } 1635 }
1192 } 1636 }
1193 1637
1638 info->bl_dev = ch->bl;
1639
1194 error = register_framebuffer(info); 1640 error = register_framebuffer(info);
1195 if (error < 0) 1641 if (error < 0)
1196 goto err1; 1642 goto err1;
@@ -1200,8 +1646,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1200 pdev->name, 1646 pdev->name,
1201 (ch->cfg.chan == LCDC_CHAN_MAINLCD) ? 1647 (ch->cfg.chan == LCDC_CHAN_MAINLCD) ?
1202 "mainlcd" : "sublcd", 1648 "mainlcd" : "sublcd",
1203 (int) ch->cfg.lcd_cfg.xres, 1649 info->var.xres, info->var.yres,
1204 (int) ch->cfg.lcd_cfg.yres,
1205 ch->cfg.bpp); 1650 ch->cfg.bpp);
1206 1651
1207 /* deferred io mode: disable clock to save power */ 1652 /* deferred io mode: disable clock to save power */
@@ -1243,12 +1688,19 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
1243 if (priv->ch[i].sglist) 1688 if (priv->ch[i].sglist)
1244 vfree(priv->ch[i].sglist); 1689 vfree(priv->ch[i].sglist);
1245 1690
1246 dma_free_coherent(&pdev->dev, info->fix.smem_len, 1691 if (info->screen_base)
1247 info->screen_base, priv->ch[i].dma_handle); 1692 dma_free_coherent(&pdev->dev, info->fix.smem_len,
1693 info->screen_base,
1694 priv->ch[i].dma_handle);
1248 fb_dealloc_cmap(&info->cmap); 1695 fb_dealloc_cmap(&info->cmap);
1249 framebuffer_release(info); 1696 framebuffer_release(info);
1250 } 1697 }
1251 1698
1699 for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
1700 if (priv->ch[i].bl)
1701 sh_mobile_lcdc_bl_remove(priv->ch[i].bl);
1702 }
1703
1252 if (priv->dot_clk) 1704 if (priv->dot_clk)
1253 clk_put(priv->dot_clk); 1705 clk_put(priv->dot_clk);
1254 1706