aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/sh_mobile_lcdcfb.c
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2010-09-14 10:48:54 -0400
committerPaul Mundt <lethal@linux-sh.org>2010-09-16 03:36:17 -0400
commitdd210503b77ae04adfdb25ca45536c4f7e33edb1 (patch)
tree14f3e8775c17906f8216618d1f7a97ee6927a50e /drivers/video/sh_mobile_lcdcfb.c
parent52d5ac0073eb5faf284574bd98a25a65053eaae0 (diff)
fbdev: sh_mobile_lcdc: reconfigure the framebuffer, when free
Currently the sh_mobile_lcdc driver only reconfigures the hardware interface, when a new monitor is plugged in. This patch adds support for dynamic framebuffer reconfiguration, when no user is holding the framebuffer device node open. Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/video/sh_mobile_lcdcfb.c')
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c112
1 files changed, 111 insertions, 1 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index b4a878624510..d6e9ff512443 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -20,6 +20,7 @@
20#include <linux/vmalloc.h> 20#include <linux/vmalloc.h>
21#include <linux/ioctl.h> 21#include <linux/ioctl.h>
22#include <linux/slab.h> 22#include <linux/slab.h>
23#include <linux/console.h>
23#include <video/sh_mobile_lcdc.h> 24#include <video/sh_mobile_lcdc.h>
24#include <asm/atomic.h> 25#include <asm/atomic.h>
25 26
@@ -479,6 +480,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
479 m = 1 << 6; 480 m = 1 << 6;
480 tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); 481 tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);
481 482
483 /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider denominator */
482 lcdc_write_chan(ch, LDDCKPAT1R, 0); 484 lcdc_write_chan(ch, LDDCKPAT1R, 0);
483 lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); 485 lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
484 } 486 }
@@ -815,6 +817,103 @@ static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
815 return retval; 817 return retval;
816} 818}
817 819
820static void sh_mobile_fb_reconfig(struct fb_info *info)
821{
822 struct sh_mobile_lcdc_chan *ch = info->par;
823 struct fb_videomode mode1, mode2;
824 struct fb_event event;
825 int evnt = FB_EVENT_MODE_CHANGE_ALL;
826
827 if (ch->use_count > 1 || (ch->use_count == 1 && !info->fbcon_par))
828 /* More framebuffer users are active */
829 return;
830
831 fb_var_to_videomode(&mode1, &ch->display_var);
832 fb_var_to_videomode(&mode2, &info->var);
833
834 if (fb_mode_is_equal(&mode1, &mode2))
835 return;
836
837 /* Display has been re-plugged, framebuffer is free now, reconfigure */
838 if (fb_set_var(info, &ch->display_var) < 0)
839 /* Couldn't reconfigure, hopefully, can continue as before */
840 return;
841
842 info->fix.line_length = mode2.xres * (ch->cfg.bpp / 8);
843
844 /*
845 * fb_set_var() calls the notifier change internally, only if
846 * FBINFO_MISC_USEREVENT flag is set. Since we do not want to fake a
847 * user event, we have to call the chain ourselves.
848 */
849 event.info = info;
850 event.data = &mode2;
851 fb_notifier_call_chain(evnt, &event);
852}
853
854/*
855 * Locking: both .fb_release() and .fb_open() are called with info->lock held if
856 * user == 1, or with console sem held, if user == 0.
857 */
858static int sh_mobile_release(struct fb_info *info, int user)
859{
860 struct sh_mobile_lcdc_chan *ch = info->par;
861
862 mutex_lock(&ch->open_lock);
863 dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count);
864
865 ch->use_count--;
866
867 /* Nothing to reconfigure, when called from fbcon */
868 if (user) {
869 acquire_console_sem();
870 sh_mobile_fb_reconfig(info);
871 release_console_sem();
872 }
873
874 mutex_unlock(&ch->open_lock);
875
876 return 0;
877}
878
879static int sh_mobile_open(struct fb_info *info, int user)
880{
881 struct sh_mobile_lcdc_chan *ch = info->par;
882
883 mutex_lock(&ch->open_lock);
884 ch->use_count++;
885
886 dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count);
887 mutex_unlock(&ch->open_lock);
888
889 return 0;
890}
891
892static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
893{
894 struct sh_mobile_lcdc_chan *ch = info->par;
895
896 if (var->xres < 160 || var->xres > 1920 ||
897 var->yres < 120 || var->yres > 1080 ||
898 var->left_margin < 32 || var->left_margin > 320 ||
899 var->right_margin < 12 || var->right_margin > 240 ||
900 var->upper_margin < 12 || var->upper_margin > 120 ||
901 var->lower_margin < 1 || var->lower_margin > 64 ||
902 var->hsync_len < 32 || var->hsync_len > 120 ||
903 var->vsync_len < 2 || var->vsync_len > 64 ||
904 var->pixclock < 6000 || var->pixclock > 40000 ||
905 var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) {
906 dev_warn(info->dev, "Invalid info: %u %u %u %u %u %u %u %u %u!\n",
907 var->xres, var->yres,
908 var->left_margin, var->right_margin,
909 var->upper_margin, var->lower_margin,
910 var->hsync_len, var->vsync_len,
911 var->pixclock);
912 return -EINVAL;
913 }
914 return 0;
915}
916
818static struct fb_ops sh_mobile_lcdc_ops = { 917static struct fb_ops sh_mobile_lcdc_ops = {
819 .owner = THIS_MODULE, 918 .owner = THIS_MODULE,
820 .fb_setcolreg = sh_mobile_lcdc_setcolreg, 919 .fb_setcolreg = sh_mobile_lcdc_setcolreg,
@@ -825,6 +924,9 @@ static struct fb_ops sh_mobile_lcdc_ops = {
825 .fb_imageblit = sh_mobile_lcdc_imageblit, 924 .fb_imageblit = sh_mobile_lcdc_imageblit,
826 .fb_pan_display = sh_mobile_fb_pan_display, 925 .fb_pan_display = sh_mobile_fb_pan_display,
827 .fb_ioctl = sh_mobile_ioctl, 926 .fb_ioctl = sh_mobile_ioctl,
927 .fb_open = sh_mobile_open,
928 .fb_release = sh_mobile_release,
929 .fb_check_var = sh_mobile_check_var,
828}; 930};
829 931
830static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) 932static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
@@ -964,9 +1066,13 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb,
964 case FB_EVENT_RESUME: 1066 case FB_EVENT_RESUME:
965 var = &info->var; 1067 var = &info->var;
966 1068
1069 mutex_lock(&ch->open_lock);
1070 sh_mobile_fb_reconfig(info);
1071 mutex_unlock(&ch->open_lock);
1072
967 /* HDMI must be enabled before LCDC configuration */ 1073 /* HDMI must be enabled before LCDC configuration */
968 if (try_module_get(board_cfg->owner) && board_cfg->display_on) { 1074 if (try_module_get(board_cfg->owner) && board_cfg->display_on) {
969 board_cfg->display_on(board_cfg->board_data, ch->info); 1075 board_cfg->display_on(board_cfg->board_data, info);
970 module_put(board_cfg->owner); 1076 module_put(board_cfg->owner);
971 } 1077 }
972 1078
@@ -1086,9 +1192,13 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1086 info = ch->info; 1192 info = ch->info;
1087 var = &info->var; 1193 var = &info->var;
1088 info->fbops = &sh_mobile_lcdc_ops; 1194 info->fbops = &sh_mobile_lcdc_ops;
1195
1196 mutex_init(&ch->open_lock);
1197
1089 fb_videomode_to_var(var, &cfg->lcd_cfg[0]); 1198 fb_videomode_to_var(var, &cfg->lcd_cfg[0]);
1090 /* Default Y virtual resolution is 2x panel size */ 1199 /* Default Y virtual resolution is 2x panel size */
1091 var->yres_virtual = var->yres * 2; 1200 var->yres_virtual = var->yres * 2;
1201 var->activate = FB_ACTIVATE_NOW;
1092 1202
1093 error = sh_mobile_lcdc_set_bpp(var, cfg->bpp); 1203 error = sh_mobile_lcdc_set_bpp(var, cfg->bpp);
1094 if (error) 1204 if (error)