aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/sh_mobile_lcdcfb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/sh_mobile_lcdcfb.c')
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c348
1 files changed, 227 insertions, 121 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 7a1419279c8f..50963739a409 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,12 @@
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 <video/sh_mobile_lcdc.h> 24#include <video/sh_mobile_lcdc.h>
25#include <asm/atomic.h> 25#include <asm/atomic.h>
26 26
27#define PALETTE_NR 16 27#include "sh_mobile_lcdcfb.h"
28
28#define SIDE_B_OFFSET 0x1000 29#define SIDE_B_OFFSET 0x1000
29#define MIRROR_OFFSET 0x2000 30#define MIRROR_OFFSET 0x2000
30 31
@@ -53,11 +54,8 @@ static int lcdc_shared_regs[] = {
53}; 54};
54#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) 55#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs)
55 56
56/* per-channel registers */ 57#define DEFAULT_XRES 1280
57enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, 58#define DEFAULT_YRES 1024
58 LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR,
59 LDHAJR,
60 NR_CH_REGS };
61 59
62static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { 60static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
63 [LDDCKPAT1R] = 0x400, 61 [LDDCKPAT1R] = 0x400,
@@ -112,23 +110,21 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
112#define LDRCNTR_MRC 0x00000001 110#define LDRCNTR_MRC 0x00000001
113#define LDSR_MRS 0x00000100 111#define LDSR_MRS 0x00000100
114 112
115struct sh_mobile_lcdc_priv; 113static const struct fb_videomode default_720p = {
116struct sh_mobile_lcdc_chan { 114 .name = "HDMI 720p",
117 struct sh_mobile_lcdc_priv *lcdc; 115 .xres = 1280,
118 unsigned long *reg_offs; 116 .yres = 720,
119 unsigned long ldmt1r_value; 117
120 unsigned long enabled; /* ME and SE in LDCNT2R */ 118 .left_margin = 200,
121 struct sh_mobile_lcdc_chan_cfg cfg; 119 .right_margin = 88,
122 u32 pseudo_palette[PALETTE_NR]; 120 .hsync_len = 48,
123 unsigned long saved_ch_regs[NR_CH_REGS]; 121
124 struct fb_info *info; 122 .upper_margin = 20,
125 dma_addr_t dma_handle; 123 .lower_margin = 5,
126 struct fb_deferred_io defio; 124 .vsync_len = 5,
127 struct scatterlist *sglist; 125
128 unsigned long frame_end; 126 .pixclock = 13468,
129 unsigned long pan_offset; 127 .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT,
130 wait_queue_head_t frame_end_wait;
131 struct completion vsync_completion;
132}; 128};
133 129
134struct sh_mobile_lcdc_priv { 130struct sh_mobile_lcdc_priv {
@@ -409,8 +405,8 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
409 405
410static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) 406static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
411{ 407{
412 struct fb_var_screeninfo *var = &ch->info->var; 408 struct fb_var_screeninfo *var = &ch->info->var, *display_var = &ch->display_var;
413 unsigned long h_total, hsync_pos; 409 unsigned long h_total, hsync_pos, display_h_total;
414 u32 tmp; 410 u32 tmp;
415 411
416 tmp = ch->ldmt1r_value; 412 tmp = ch->ldmt1r_value;
@@ -428,31 +424,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); 424 lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
429 425
430 /* horizontal configuration */ 426 /* horizontal configuration */
431 h_total = var->xres + var->hsync_len + 427 h_total = display_var->xres + display_var->hsync_len +
432 var->left_margin + var->right_margin; 428 display_var->left_margin + display_var->right_margin;
433 tmp = h_total / 8; /* HTCN */ 429 tmp = h_total / 8; /* HTCN */
434 tmp |= (var->xres / 8) << 16; /* HDCN */ 430 tmp |= (min(display_var->xres, var->xres) / 8) << 16; /* HDCN */
435 lcdc_write_chan(ch, LDHCNR, tmp); 431 lcdc_write_chan(ch, LDHCNR, tmp);
436 432
437 hsync_pos = var->xres + var->right_margin; 433 hsync_pos = display_var->xres + display_var->right_margin;
438 tmp = hsync_pos / 8; /* HSYNP */ 434 tmp = hsync_pos / 8; /* HSYNP */
439 tmp |= (var->hsync_len / 8) << 16; /* HSYNW */ 435 tmp |= (display_var->hsync_len / 8) << 16; /* HSYNW */
440 lcdc_write_chan(ch, LDHSYNR, tmp); 436 lcdc_write_chan(ch, LDHSYNR, tmp);
441 437
442 /* vertical configuration */ 438 /* vertical configuration */
443 tmp = var->yres + var->vsync_len + 439 tmp = display_var->yres + display_var->vsync_len +
444 var->upper_margin + var->lower_margin; /* VTLN */ 440 display_var->upper_margin + display_var->lower_margin; /* VTLN */
445 tmp |= var->yres << 16; /* VDLN */ 441 tmp |= min(display_var->yres, var->yres) << 16; /* VDLN */
446 lcdc_write_chan(ch, LDVLNR, tmp); 442 lcdc_write_chan(ch, LDVLNR, tmp);
447 443
448 tmp = var->yres + var->lower_margin; /* VSYNP */ 444 tmp = display_var->yres + display_var->lower_margin; /* VSYNP */
449 tmp |= var->vsync_len << 16; /* VSYNW */ 445 tmp |= display_var->vsync_len << 16; /* VSYNW */
450 lcdc_write_chan(ch, LDVSYNR, tmp); 446 lcdc_write_chan(ch, LDVSYNR, tmp);
451 447
452 /* Adjust horizontal synchronisation for HDMI */ 448 /* Adjust horizontal synchronisation for HDMI */
453 tmp = ((var->xres & 7) << 24) | 449 display_h_total = display_var->xres + display_var->hsync_len +
454 ((h_total & 7) << 16) | 450 display_var->left_margin + display_var->right_margin;
455 ((var->hsync_len & 7) << 8) | 451 tmp = ((display_var->xres & 7) << 24) |
452 ((display_h_total & 7) << 16) |
453 ((display_var->hsync_len & 7) << 8) |
456 hsync_pos; 454 hsync_pos;
457 lcdc_write_chan(ch, LDHAJR, tmp); 455 lcdc_write_chan(ch, LDHAJR, tmp);
458} 456}
@@ -460,7 +458,6 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
460static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) 458static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
461{ 459{
462 struct sh_mobile_lcdc_chan *ch; 460 struct sh_mobile_lcdc_chan *ch;
463 struct fb_videomode *lcd_cfg;
464 struct sh_mobile_lcdc_board_cfg *board_cfg; 461 struct sh_mobile_lcdc_board_cfg *board_cfg;
465 unsigned long tmp; 462 unsigned long tmp;
466 int k, m; 463 int k, m;
@@ -503,7 +500,8 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
503 m = 1 << 6; 500 m = 1 << 6;
504 tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); 501 tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);
505 502
506 lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000); 503 /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider denominator */
504 lcdc_write_chan(ch, LDDCKPAT1R, 0);
507 lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); 505 lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
508 } 506 }
509 507
@@ -518,7 +516,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
518 516
519 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 517 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
520 ch = &priv->ch[k]; 518 ch = &priv->ch[k];
521 lcd_cfg = &ch->cfg.lcd_cfg;
522 519
523 if (!ch->enabled) 520 if (!ch->enabled)
524 continue; 521 continue;
@@ -547,7 +544,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
547 544
548 /* set bpp format in PKF[4:0] */ 545 /* set bpp format in PKF[4:0] */
549 tmp = lcdc_read_chan(ch, LDDFR); 546 tmp = lcdc_read_chan(ch, LDDFR);
550 tmp &= ~(0x0001001f); 547 tmp &= ~0x0001001f;
551 tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0; 548 tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0;
552 lcdc_write_chan(ch, LDDFR, tmp); 549 lcdc_write_chan(ch, LDDFR, tmp);
553 550
@@ -591,8 +588,10 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
591 continue; 588 continue;
592 589
593 board_cfg = &ch->cfg.board_cfg; 590 board_cfg = &ch->cfg.board_cfg;
594 if (board_cfg->display_on) 591 if (try_module_get(board_cfg->owner) && board_cfg->display_on) {
595 board_cfg->display_on(board_cfg->board_data, ch->info); 592 board_cfg->display_on(board_cfg->board_data, ch->info);
593 module_put(board_cfg->owner);
594 }
596 } 595 }
597 596
598 return 0; 597 return 0;
@@ -614,7 +613,7 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
614 * flush frame, and wait for frame end interrupt 613 * flush frame, and wait for frame end interrupt
615 * clean up deferred io and enable clock 614 * clean up deferred io and enable clock
616 */ 615 */
617 if (ch->info->fbdefio) { 616 if (ch->info && ch->info->fbdefio) {
618 ch->frame_end = 0; 617 ch->frame_end = 0;
619 schedule_delayed_work(&ch->info->deferred_work, 0); 618 schedule_delayed_work(&ch->info->deferred_work, 0);
620 wait_event(ch->frame_end_wait, ch->frame_end); 619 wait_event(ch->frame_end_wait, ch->frame_end);
@@ -624,8 +623,10 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
624 } 623 }
625 624
626 board_cfg = &ch->cfg.board_cfg; 625 board_cfg = &ch->cfg.board_cfg;
627 if (board_cfg->display_off) 626 if (try_module_get(board_cfg->owner) && board_cfg->display_off) {
628 board_cfg->display_off(board_cfg->board_data); 627 board_cfg->display_off(board_cfg->board_data);
628 module_put(board_cfg->owner);
629 }
629 } 630 }
630 631
631 /* stop the lcdc */ 632 /* stop the lcdc */
@@ -704,7 +705,6 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
704 return PTR_ERR(priv->dot_clk); 705 return PTR_ERR(priv->dot_clk);
705 } 706 }
706 } 707 }
707 atomic_set(&priv->hw_usecnt, -1);
708 708
709 /* Runtime PM support involves two step for this driver: 709 /* Runtime PM support involves two step for this driver:
710 * 1) Enable Runtime PM 710 * 1) Enable Runtime PM
@@ -837,6 +837,102 @@ static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
837 return retval; 837 return retval;
838} 838}
839 839
840static void sh_mobile_fb_reconfig(struct fb_info *info)
841{
842 struct sh_mobile_lcdc_chan *ch = info->par;
843 struct fb_videomode mode1, mode2;
844 struct fb_event event;
845 int evnt = FB_EVENT_MODE_CHANGE_ALL;
846
847 if (ch->use_count > 1 || (ch->use_count == 1 && !info->fbcon_par))
848 /* More framebuffer users are active */
849 return;
850
851 fb_var_to_videomode(&mode1, &ch->display_var);
852 fb_var_to_videomode(&mode2, &info->var);
853
854 if (fb_mode_is_equal(&mode1, &mode2))
855 return;
856
857 /* Display has been re-plugged, framebuffer is free now, reconfigure */
858 if (fb_set_var(info, &ch->display_var) < 0)
859 /* Couldn't reconfigure, hopefully, can continue as before */
860 return;
861
862 info->fix.line_length = mode2.xres * (ch->cfg.bpp / 8);
863
864 /*
865 * fb_set_var() calls the notifier change internally, only if
866 * FBINFO_MISC_USEREVENT flag is set. Since we do not want to fake a
867 * user event, we have to call the chain ourselves.
868 */
869 event.info = info;
870 event.data = &mode2;
871 fb_notifier_call_chain(evnt, &event);
872}
873
874/*
875 * Locking: both .fb_release() and .fb_open() are called with info->lock held if
876 * user == 1, or with console sem held, if user == 0.
877 */
878static int sh_mobile_release(struct fb_info *info, int user)
879{
880 struct sh_mobile_lcdc_chan *ch = info->par;
881
882 mutex_lock(&ch->open_lock);
883 dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count);
884
885 ch->use_count--;
886
887 /* Nothing to reconfigure, when called from fbcon */
888 if (user) {
889 acquire_console_sem();
890 sh_mobile_fb_reconfig(info);
891 release_console_sem();
892 }
893
894 mutex_unlock(&ch->open_lock);
895
896 return 0;
897}
898
899static int sh_mobile_open(struct fb_info *info, int user)
900{
901 struct sh_mobile_lcdc_chan *ch = info->par;
902
903 mutex_lock(&ch->open_lock);
904 ch->use_count++;
905
906 dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count);
907 mutex_unlock(&ch->open_lock);
908
909 return 0;
910}
911
912static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
913{
914 struct sh_mobile_lcdc_chan *ch = info->par;
915
916 if (var->xres < 160 || var->xres > 1920 ||
917 var->yres < 120 || var->yres > 1080 ||
918 var->left_margin < 32 || var->left_margin > 320 ||
919 var->right_margin < 12 || var->right_margin > 240 ||
920 var->upper_margin < 12 || var->upper_margin > 120 ||
921 var->lower_margin < 1 || var->lower_margin > 64 ||
922 var->hsync_len < 32 || var->hsync_len > 240 ||
923 var->vsync_len < 2 || var->vsync_len > 64 ||
924 var->pixclock < 6000 || var->pixclock > 40000 ||
925 var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) {
926 dev_warn(info->dev, "Invalid info: %u %u %u %u %u %u %u %u %u!\n",
927 var->xres, var->yres,
928 var->left_margin, var->right_margin,
929 var->upper_margin, var->lower_margin,
930 var->hsync_len, var->vsync_len,
931 var->pixclock);
932 return -EINVAL;
933 }
934 return 0;
935}
840 936
841static struct fb_ops sh_mobile_lcdc_ops = { 937static struct fb_ops sh_mobile_lcdc_ops = {
842 .owner = THIS_MODULE, 938 .owner = THIS_MODULE,
@@ -848,6 +944,9 @@ static struct fb_ops sh_mobile_lcdc_ops = {
848 .fb_imageblit = sh_mobile_lcdc_imageblit, 944 .fb_imageblit = sh_mobile_lcdc_imageblit,
849 .fb_pan_display = sh_mobile_fb_pan_display, 945 .fb_pan_display = sh_mobile_fb_pan_display,
850 .fb_ioctl = sh_mobile_ioctl, 946 .fb_ioctl = sh_mobile_ioctl,
947 .fb_open = sh_mobile_open,
948 .fb_release = sh_mobile_release,
949 .fb_check_var = sh_mobile_check_var,
851}; 950};
852 951
853static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) 952static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
@@ -958,6 +1057,7 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
958 .runtime_resume = sh_mobile_lcdc_runtime_resume, 1057 .runtime_resume = sh_mobile_lcdc_runtime_resume,
959}; 1058};
960 1059
1060/* locking: called with info->lock held */
961static int sh_mobile_lcdc_notify(struct notifier_block *nb, 1061static int sh_mobile_lcdc_notify(struct notifier_block *nb,
962 unsigned long action, void *data) 1062 unsigned long action, void *data)
963{ 1063{
@@ -965,53 +1065,40 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb,
965 struct fb_info *info = event->info; 1065 struct fb_info *info = event->info;
966 struct sh_mobile_lcdc_chan *ch = info->par; 1066 struct sh_mobile_lcdc_chan *ch = info->par;
967 struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; 1067 struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg;
968 struct fb_var_screeninfo *var; 1068 int ret;
969 1069
970 if (&ch->lcdc->notifier != nb) 1070 if (&ch->lcdc->notifier != nb)
971 return 0; 1071 return NOTIFY_DONE;
972 1072
973 dev_dbg(info->dev, "%s(): action = %lu, data = %p\n", 1073 dev_dbg(info->dev, "%s(): action = %lu, data = %p\n",
974 __func__, action, event->data); 1074 __func__, action, event->data);
975 1075
976 switch(action) { 1076 switch(action) {
977 case FB_EVENT_SUSPEND: 1077 case FB_EVENT_SUSPEND:
978 if (board_cfg->display_off) 1078 if (try_module_get(board_cfg->owner) && board_cfg->display_off) {
979 board_cfg->display_off(board_cfg->board_data); 1079 board_cfg->display_off(board_cfg->board_data);
1080 module_put(board_cfg->owner);
1081 }
980 pm_runtime_put(info->device); 1082 pm_runtime_put(info->device);
1083 sh_mobile_lcdc_stop(ch->lcdc);
981 break; 1084 break;
982 case FB_EVENT_RESUME: 1085 case FB_EVENT_RESUME:
983 var = &info->var; 1086 mutex_lock(&ch->open_lock);
1087 sh_mobile_fb_reconfig(info);
1088 mutex_unlock(&ch->open_lock);
984 1089
985 /* HDMI must be enabled before LCDC configuration */ 1090 /* HDMI must be enabled before LCDC configuration */
986 if (board_cfg->display_on) 1091 if (try_module_get(board_cfg->owner) && board_cfg->display_on) {
987 board_cfg->display_on(board_cfg->board_data, ch->info); 1092 board_cfg->display_on(board_cfg->board_data, info);
988 1093 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 } 1094 }
1006 1095
1007 pm_runtime_get_sync(info->device); 1096 ret = sh_mobile_lcdc_start(ch->lcdc);
1008 1097 if (!ret)
1009 sh_mobile_lcdc_geometry(ch); 1098 pm_runtime_get_sync(info->device);
1010
1011 break;
1012 } 1099 }
1013 1100
1014 return 0; 1101 return NOTIFY_OK;
1015} 1102}
1016 1103
1017static int sh_mobile_lcdc_remove(struct platform_device *pdev); 1104static int sh_mobile_lcdc_remove(struct platform_device *pdev);
@@ -1020,14 +1107,13 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1020{ 1107{
1021 struct fb_info *info; 1108 struct fb_info *info;
1022 struct sh_mobile_lcdc_priv *priv; 1109 struct sh_mobile_lcdc_priv *priv;
1023 struct sh_mobile_lcdc_info *pdata; 1110 struct sh_mobile_lcdc_info *pdata = pdev->dev.platform_data;
1024 struct sh_mobile_lcdc_chan_cfg *cfg;
1025 struct resource *res; 1111 struct resource *res;
1026 int error; 1112 int error;
1027 void *buf; 1113 void *buf;
1028 int i, j; 1114 int i, j;
1029 1115
1030 if (!pdev->dev.platform_data) { 1116 if (!pdata) {
1031 dev_err(&pdev->dev, "no platform data defined\n"); 1117 dev_err(&pdev->dev, "no platform data defined\n");
1032 return -EINVAL; 1118 return -EINVAL;
1033 } 1119 }
@@ -1055,31 +1141,33 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1055 } 1141 }
1056 1142
1057 priv->irq = i; 1143 priv->irq = i;
1058 pdata = pdev->dev.platform_data; 1144 atomic_set(&priv->hw_usecnt, -1);
1059 1145
1060 j = 0; 1146 j = 0;
1061 for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) { 1147 for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) {
1062 priv->ch[j].lcdc = priv; 1148 struct sh_mobile_lcdc_chan *ch = priv->ch + j;
1063 memcpy(&priv->ch[j].cfg, &pdata->ch[i], sizeof(pdata->ch[i])); 1149
1150 ch->lcdc = priv;
1151 memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i]));
1064 1152
1065 error = sh_mobile_lcdc_check_interface(&priv->ch[j]); 1153 error = sh_mobile_lcdc_check_interface(ch);
1066 if (error) { 1154 if (error) {
1067 dev_err(&pdev->dev, "unsupported interface type\n"); 1155 dev_err(&pdev->dev, "unsupported interface type\n");
1068 goto err1; 1156 goto err1;
1069 } 1157 }
1070 init_waitqueue_head(&priv->ch[j].frame_end_wait); 1158 init_waitqueue_head(&ch->frame_end_wait);
1071 init_completion(&priv->ch[j].vsync_completion); 1159 init_completion(&ch->vsync_completion);
1072 priv->ch[j].pan_offset = 0; 1160 ch->pan_offset = 0;
1073 1161
1074 switch (pdata->ch[i].chan) { 1162 switch (pdata->ch[i].chan) {
1075 case LCDC_CHAN_MAINLCD: 1163 case LCDC_CHAN_MAINLCD:
1076 priv->ch[j].enabled = 1 << 1; 1164 ch->enabled = 1 << 1;
1077 priv->ch[j].reg_offs = lcdc_offs_mainlcd; 1165 ch->reg_offs = lcdc_offs_mainlcd;
1078 j++; 1166 j++;
1079 break; 1167 break;
1080 case LCDC_CHAN_SUBLCD: 1168 case LCDC_CHAN_SUBLCD:
1081 priv->ch[j].enabled = 1 << 2; 1169 ch->enabled = 1 << 2;
1082 priv->ch[j].reg_offs = lcdc_offs_sublcd; 1170 ch->reg_offs = lcdc_offs_sublcd;
1083 j++; 1171 j++;
1084 break; 1172 break;
1085 } 1173 }
@@ -1103,69 +1191,83 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1103 1191
1104 for (i = 0; i < j; i++) { 1192 for (i = 0; i < j; i++) {
1105 struct fb_var_screeninfo *var; 1193 struct fb_var_screeninfo *var;
1106 struct fb_videomode *lcd_cfg; 1194 const struct fb_videomode *lcd_cfg, *max_cfg = NULL;
1107 cfg = &priv->ch[i].cfg; 1195 struct sh_mobile_lcdc_chan *ch = priv->ch + i;
1196 struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg;
1197 const struct fb_videomode *mode = cfg->lcd_cfg;
1198 unsigned long max_size = 0;
1199 int k;
1108 1200
1109 priv->ch[i].info = framebuffer_alloc(0, &pdev->dev); 1201 ch->info = framebuffer_alloc(0, &pdev->dev);
1110 if (!priv->ch[i].info) { 1202 if (!ch->info) {
1111 dev_err(&pdev->dev, "unable to allocate fb_info\n"); 1203 dev_err(&pdev->dev, "unable to allocate fb_info\n");
1112 error = -ENOMEM; 1204 error = -ENOMEM;
1113 break; 1205 break;
1114 } 1206 }
1115 1207
1116 info = priv->ch[i].info; 1208 info = ch->info;
1117 var = &info->var; 1209 var = &info->var;
1118 lcd_cfg = &cfg->lcd_cfg;
1119 info->fbops = &sh_mobile_lcdc_ops; 1210 info->fbops = &sh_mobile_lcdc_ops;
1120 var->xres = var->xres_virtual = lcd_cfg->xres; 1211 info->par = ch;
1121 var->yres = lcd_cfg->yres; 1212
1213 mutex_init(&ch->open_lock);
1214
1215 for (k = 0, lcd_cfg = mode;
1216 k < cfg->num_cfg && lcd_cfg;
1217 k++, lcd_cfg++) {
1218 unsigned long size = lcd_cfg->yres * lcd_cfg->xres;
1219
1220 if (size > max_size) {
1221 max_cfg = lcd_cfg;
1222 max_size = size;
1223 }
1224 }
1225
1226 if (!mode)
1227 max_size = DEFAULT_XRES * DEFAULT_YRES;
1228 else if (max_cfg)
1229 dev_dbg(&pdev->dev, "Found largest videomode %ux%u\n",
1230 max_cfg->xres, max_cfg->yres);
1231
1232 info->fix = sh_mobile_lcdc_fix;
1233 info->fix.smem_len = max_size * (cfg->bpp / 8) * 2;
1234
1235 if (!mode)
1236 mode = &default_720p;
1237
1238 fb_videomode_to_var(var, mode);
1122 /* Default Y virtual resolution is 2x panel size */ 1239 /* Default Y virtual resolution is 2x panel size */
1123 var->yres_virtual = var->yres * 2; 1240 var->yres_virtual = var->yres * 2;
1124 var->width = cfg->lcd_size_cfg.width;
1125 var->height = cfg->lcd_size_cfg.height;
1126 var->activate = FB_ACTIVATE_NOW; 1241 var->activate = FB_ACTIVATE_NOW;
1127 var->left_margin = lcd_cfg->left_margin;
1128 var->right_margin = lcd_cfg->right_margin;
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 1242
1136 error = sh_mobile_lcdc_set_bpp(var, cfg->bpp); 1243 error = sh_mobile_lcdc_set_bpp(var, cfg->bpp);
1137 if (error) 1244 if (error)
1138 break; 1245 break;
1139 1246
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, 1247 buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len,
1146 &priv->ch[i].dma_handle, GFP_KERNEL); 1248 &ch->dma_handle, GFP_KERNEL);
1147 if (!buf) { 1249 if (!buf) {
1148 dev_err(&pdev->dev, "unable to allocate buffer\n"); 1250 dev_err(&pdev->dev, "unable to allocate buffer\n");
1149 error = -ENOMEM; 1251 error = -ENOMEM;
1150 break; 1252 break;
1151 } 1253 }
1152 1254
1153 info->pseudo_palette = &priv->ch[i].pseudo_palette; 1255 info->pseudo_palette = &ch->pseudo_palette;
1154 info->flags = FBINFO_FLAG_DEFAULT; 1256 info->flags = FBINFO_FLAG_DEFAULT;
1155 1257
1156 error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); 1258 error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
1157 if (error < 0) { 1259 if (error < 0) {
1158 dev_err(&pdev->dev, "unable to allocate cmap\n"); 1260 dev_err(&pdev->dev, "unable to allocate cmap\n");
1159 dma_free_coherent(&pdev->dev, info->fix.smem_len, 1261 dma_free_coherent(&pdev->dev, info->fix.smem_len,
1160 buf, priv->ch[i].dma_handle); 1262 buf, ch->dma_handle);
1161 break; 1263 break;
1162 } 1264 }
1163 1265
1164 memset(buf, 0, info->fix.smem_len); 1266 info->fix.smem_start = ch->dma_handle;
1165 info->fix.smem_start = priv->ch[i].dma_handle; 1267 info->fix.line_length = var->xres * (cfg->bpp / 8);
1166 info->screen_base = buf; 1268 info->screen_base = buf;
1167 info->device = &pdev->dev; 1269 info->device = &pdev->dev;
1168 info->par = &priv->ch[i]; 1270 ch->display_var = *var;
1169 } 1271 }
1170 1272
1171 if (error) 1273 if (error)
@@ -1179,6 +1281,10 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1179 1281
1180 for (i = 0; i < j; i++) { 1282 for (i = 0; i < j; i++) {
1181 struct sh_mobile_lcdc_chan *ch = priv->ch + i; 1283 struct sh_mobile_lcdc_chan *ch = priv->ch + i;
1284 const struct fb_videomode *mode = ch->cfg.lcd_cfg;
1285
1286 if (!mode)
1287 mode = &default_720p;
1182 1288
1183 info = ch->info; 1289 info = ch->info;
1184 1290
@@ -1191,6 +1297,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1191 } 1297 }
1192 } 1298 }
1193 1299
1300 fb_videomode_to_modelist(mode, ch->cfg.num_cfg, &info->modelist);
1194 error = register_framebuffer(info); 1301 error = register_framebuffer(info);
1195 if (error < 0) 1302 if (error < 0)
1196 goto err1; 1303 goto err1;
@@ -1200,8 +1307,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1200 pdev->name, 1307 pdev->name,
1201 (ch->cfg.chan == LCDC_CHAN_MAINLCD) ? 1308 (ch->cfg.chan == LCDC_CHAN_MAINLCD) ?
1202 "mainlcd" : "sublcd", 1309 "mainlcd" : "sublcd",
1203 (int) ch->cfg.lcd_cfg.xres, 1310 info->var.xres, info->var.yres,
1204 (int) ch->cfg.lcd_cfg.yres,
1205 ch->cfg.bpp); 1311 ch->cfg.bpp);
1206 1312
1207 /* deferred io mode: disable clock to save power */ 1313 /* deferred io mode: disable clock to save power */