summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJari Helaakoski <tekkuli@gmail.com>2013-01-07 22:56:31 +0400
committerAlejandro Mery <amery@geeks.cl>2013-01-14 14:36:05 +0400
commit62ce7bb52f9511e1638b256a382de21315669560 (patch)
tree2d9bc47a7f080767906441816f0a6bab6c9a1308
parent89a5378dd5ef60c1d3fba0aa96cc543dfe65266c (diff)
downloadlinux-sunxi-62ce7bb52f9511e1638b256a382de21315669560.tar.xz
video:sunxi:disp:Dynamic mode switching
This patch improves Hans De Goede's EDID patch by allowing user to set new mode at runtime. Example: echo D:1280x720p-60 > /sys/devices/platform/disp/graphics/fb0/mode But precondition is that EDID support must be activated like before. Ie. by using following modprobe configuration: options disp screen0_output_mode=EDID:1280x720p60 Signed-off-by: Jari Helaakoski <tekkuli@gmail.com> Acked-by: Hans de Goede <hdegoede@redhat.com>
-rw-r--r--drivers/video/sunxi/disp/bsp_display.h4
-rw-r--r--drivers/video/sunxi/disp/dev_fb.c29
-rw-r--r--drivers/video/sunxi/disp/disp_hdmi.c81
-rw-r--r--drivers/video/sunxi/disp/disp_lcd.c73
-rw-r--r--drivers/video/sunxi/hdmi/drv_hdmi.c22
-rw-r--r--include/video/sunxi_disp_ioctl.h1
6 files changed, 209 insertions, 1 deletions
diff --git a/drivers/video/sunxi/disp/bsp_display.h b/drivers/video/sunxi/disp/bsp_display.h
index a46f3ad9cf68..09c02fdbf9e5 100644
--- a/drivers/video/sunxi/disp/bsp_display.h
+++ b/drivers/video/sunxi/disp/bsp_display.h
@@ -77,6 +77,7 @@ typedef struct {
void (*tve_interrup) (__u32 sel);
__s32(*hdmi_set_mode) (__disp_tv_mode_t mode);
+ __s32(*hdmi_set_videomode) (const struct __disp_video_timing *mode);
__s32(*hdmi_wait_edid) (void);
__s32(*Hdmi_open) (void);
__s32(*Hdmi_close) (void);
@@ -235,6 +236,7 @@ extern __s32 LCD_PWM_EN(__u32 sel, __bool b_en);
extern __s32 LCD_BL_EN(__u32 sel, __bool b_en);
extern __s32 BSP_disp_lcd_user_defined_func(__u32 sel, __u32 para1, __u32 para2,
__u32 para3);
+extern __s32 BSP_disp_get_videomode(__u32 sel, struct fb_videomode *videomode);
extern __s32 BSP_disp_get_timing(__u32 sel, __disp_tcon_timing_t *tt);
extern __u32 BSP_disp_get_cur_line(__u32 sel);
#ifdef CONFIG_ARCH_SUN5I
@@ -257,6 +259,8 @@ extern __s32 BSP_disp_tv_get_dac_source(__u32 sel, __u32 index);
extern __s32 BSP_disp_hdmi_open(__u32 sel);
extern __s32 BSP_disp_hdmi_close(__u32 sel);
extern __s32 BSP_disp_hdmi_set_mode(__u32 sel, __disp_tv_mode_t mode);
+extern __s32 BSP_disp_set_videomode(__u32 sel,
+ const struct fb_videomode *mode);
extern __s32 BSP_disp_hdmi_get_mode(__u32 sel);
extern __s32 BSP_disp_hdmi_check_support_mode(__u32 sel, __u8 mode);
extern __s32 BSP_disp_hdmi_get_hpd_status(__u32 sel);
diff --git a/drivers/video/sunxi/disp/dev_fb.c b/drivers/video/sunxi/disp/dev_fb.c
index 3ac43412f917..db7638ca23eb 100644
--- a/drivers/video/sunxi/disp/dev_fb.c
+++ b/drivers/video/sunxi/disp/dev_fb.c
@@ -1052,9 +1052,20 @@ static int Fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
static int Fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
__disp_pixel_fmt_t fmt;
+ int dummy, sel;
__inf("Fb_check_var: %dx%d %dbits\n", var->xres, var->yres,
var->bits_per_pixel);
+ for (sel = 0; sel < 2; sel++) {
+ if (g_fbi.disp_init.output_type[sel] != DISP_OUTPUT_TYPE_HDMI)
+ continue;
+
+ /* Check that pll is found */
+ if (disp_get_pll_freq(PICOS2KHZ(var->pixclock) *
+ 1000, &dummy, &dummy))
+ return -EINVAL;
+ }
+
switch (var->bits_per_pixel) {
case 16:
if (var->transp.length == 1 && var->transp.offset == 15)
@@ -1104,11 +1115,26 @@ static int Fb_set_par(struct fb_info *info)
(g_fbi.fb_mode[info->node] != FB_MODE_SCREEN0))) {
struct fb_var_screeninfo *var = &info->var;
struct fb_fix_screeninfo *fix = &info->fix;
+ bool mode_changed = false;
__s32 layer_hdl = g_fbi.layer_hdl[info->node][sel];
__disp_layer_info_t layer_para;
__u32 buffer_num = 1;
__u32 y_offset = 0;
+
+ if (g_fbi.disp_init.output_type[sel] ==
+ DISP_OUTPUT_TYPE_HDMI) {
+ struct fb_videomode new_mode;
+ struct fb_videomode old_mode;
+ fb_var_to_videomode(&new_mode, var);
+ BSP_disp_get_videomode(sel, &old_mode);
+ if (!fb_mode_is_equal(&new_mode, &old_mode)) {
+ mode_changed = (BSP_disp_set_videomode(
+ sel, &new_mode) == 0);
+
+ }
+ }
+
if (g_fbi.fb_mode[info->node] ==
FB_MODE_DUAL_SAME_SCREEN_TB)
buffer_num = 2;
@@ -1124,7 +1150,8 @@ static int Fb_set_par(struct fb_info *info)
layer_para.src_win.y = var->yoffset + y_offset;
layer_para.src_win.width = var->xres;
layer_para.src_win.height = var->yres / buffer_num;
- if (layer_para.mode != DISP_LAYER_WORK_MODE_SCALER) {
+ if (layer_para.mode != DISP_LAYER_WORK_MODE_SCALER ||
+ mode_changed) {
layer_para.scn_win.width =
layer_para.src_win.width;
layer_para.scn_win.height =
diff --git a/drivers/video/sunxi/disp/disp_hdmi.c b/drivers/video/sunxi/disp/disp_hdmi.c
index 9a84ac776edb..763218e55f8e 100644
--- a/drivers/video/sunxi/disp/disp_hdmi.c
+++ b/drivers/video/sunxi/disp/disp_hdmi.c
@@ -191,6 +191,86 @@ __s32 BSP_disp_hdmi_set_mode(__u32 sel, __disp_tv_mode_t mode)
return DIS_SUCCESS;
}
+void videomode_to_video_timing(struct __disp_video_timing *video_timing,
+ const struct fb_videomode *mode)
+{
+ memset(video_timing, 0, sizeof(struct __disp_video_timing));
+ video_timing->VIC = 511;
+ video_timing->PCLK = (PICOS2KHZ(mode->pixclock) * 1000);
+ video_timing->AVI_PR = 0;
+ video_timing->INPUTX = mode->xres;
+ video_timing->INPUTY = mode->yres;
+ video_timing->HT = mode->xres + mode->left_margin +
+ mode->right_margin + mode->hsync_len;
+ video_timing->HBP = mode->left_margin + mode->hsync_len;
+ video_timing->HFP = mode->right_margin;
+ video_timing->HPSW = mode->hsync_len;
+ video_timing->VT = mode->yres + mode->upper_margin +
+ mode->lower_margin + mode->vsync_len;
+ video_timing->VBP = mode->upper_margin + mode->vsync_len;
+ video_timing->VFP = mode->lower_margin;
+ video_timing->VPSW = mode->vsync_len;
+ if (mode->vmode & FB_VMODE_INTERLACED)
+ video_timing->I = true;
+
+ if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
+ video_timing->HSYNC = true;
+
+ if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
+ video_timing->VSYNC = true;
+
+}
+
+__s32 BSP_disp_set_videomode(__u32 sel, const struct fb_videomode *mode)
+{
+ struct __disp_video_timing *old_video_timing =
+ kzalloc(sizeof(struct __disp_video_timing), GFP_KERNEL);
+ struct __disp_video_timing *new_video_timing =
+ kzalloc(sizeof(struct __disp_video_timing), GFP_KERNEL);
+ __disp_tv_mode_t hdmi_mode = gdisp.screen[sel].hdmi_mode;
+
+ if (!old_video_timing && !new_video_timing)
+ return DIS_FAIL;
+
+ if (!gdisp.init_para.hdmi_set_videomode)
+ return DIS_FAIL;
+
+ if (gdisp.init_para.hdmi_get_video_timing(hdmi_mode,
+ old_video_timing) != 0)
+ return DIS_FAIL;
+
+ videomode_to_video_timing(new_video_timing, mode);
+
+ if (gdisp.init_para.hdmi_set_videomode(new_video_timing) != 0)
+ return DIS_FAIL;
+
+ if (disp_clk_cfg(sel, DISP_OUTPUT_TYPE_HDMI, hdmi_mode) != 0)
+ goto failure;
+
+ if (DE_BE_set_display_size(sel, new_video_timing->INPUTX,
+ new_video_timing->INPUTY) != 0)
+ goto failure;
+
+ if (TCON1_set_hdmi_mode(sel, hdmi_mode) != 0)
+ goto failure;
+
+ gdisp.screen[sel].b_out_interlace = new_video_timing->I;
+
+ kfree(old_video_timing);
+ kfree(new_video_timing);
+ return DIS_SUCCESS;
+
+failure:
+ gdisp.init_para.hdmi_set_videomode(old_video_timing);
+ disp_clk_cfg(sel, DISP_OUTPUT_TYPE_HDMI, hdmi_mode);
+ DE_BE_set_display_size(sel, old_video_timing->INPUTX,
+ old_video_timing->INPUTY);
+ TCON1_set_hdmi_mode(sel, hdmi_mode);
+ kfree(old_video_timing);
+ kfree(new_video_timing);
+ return DIS_FAIL;
+}
+
__s32 BSP_disp_hdmi_get_mode(__u32 sel)
{
return gdisp.screen[sel].hdmi_mode;
@@ -253,6 +333,7 @@ __s32 BSP_disp_set_hdmi_func(__disp_hdmi_func *func)
gdisp.init_para.Hdmi_open = func->Hdmi_open;
gdisp.init_para.Hdmi_close = func->Hdmi_close;
gdisp.init_para.hdmi_set_mode = func->hdmi_set_mode;
+ gdisp.init_para.hdmi_set_videomode = func->hdmi_set_videomode;
gdisp.init_para.hdmi_mode_support = func->hdmi_mode_support;
gdisp.init_para.hdmi_get_video_timing = func->hdmi_get_video_timing;
gdisp.init_para.hdmi_get_HPD_status = func->hdmi_get_HPD_status;
diff --git a/drivers/video/sunxi/disp/disp_lcd.c b/drivers/video/sunxi/disp/disp_lcd.c
index 7fa6e5254cf0..cb4128ee023d 100644
--- a/drivers/video/sunxi/disp/disp_lcd.c
+++ b/drivers/video/sunxi/disp/disp_lcd.c
@@ -1816,6 +1816,79 @@ void LCD_set_panel_funs(__lcd_panel_fun_t *lcd0_cfg,
}
EXPORT_SYMBOL(LCD_set_panel_funs);
+__s32 BSP_disp_get_videomode(__u32 sel, struct fb_videomode *videomode)
+{
+ __disp_tcon_timing_t tt;
+ bool interlaced, hsync, vsync = false;
+ u32 pixclock, hfreq, htotal, vtotal;
+ memset(videomode, 0, sizeof(struct fb_videomode));
+
+ if (BSP_disp_get_timing(sel, &tt) != 0)
+ return DIS_FAIL;
+
+ if (gdisp.screen[sel].status & LCD_ON) {
+ interlaced = false;
+ } else if ((gdisp.screen[sel].status & TV_ON)) {
+ interlaced = Disp_get_screen_scan_mode(
+ gdisp.screen[sel].tv_mode);
+ } else if (gdisp.screen[sel].status & HDMI_ON) {
+ struct __disp_video_timing video_timing;
+ __disp_tv_mode_t hdmi_mode = gdisp.screen[sel].hdmi_mode;
+ if (gdisp.init_para.hdmi_get_video_timing(
+ hdmi_mode, &video_timing) != 0)
+ return DIS_FAIL;
+
+ interlaced = video_timing.I;
+ hsync = video_timing.HSYNC;
+ vsync = video_timing.VSYNC;
+ } else if (gdisp.screen[sel].status & VGA_ON) {
+ interlaced = Disp_get_screen_scan_mode(
+ gdisp.screen[sel].vga_mode);
+ } else {
+ DE_INF("get videomode fail because device is not output !\n");
+ return DIS_FAIL;
+ }
+
+ videomode->xres = BSP_disp_get_screen_width(sel);
+ videomode->yres = BSP_disp_get_screen_height(sel);
+ videomode->pixclock = KHZ2PICOS(tt.pixel_clk);
+ videomode->left_margin = tt.hor_back_porch;
+ videomode->right_margin = tt.hor_front_porch;
+ videomode->upper_margin = tt.ver_back_porch;
+ videomode->lower_margin = tt.ver_front_porch;
+ videomode->hsync_len = tt.hor_sync_time;
+ videomode->vsync_len = tt.ver_sync_time;
+
+
+ if (interlaced)
+ videomode->vmode = FB_VMODE_INTERLACED;
+
+ if (vsync)
+ videomode->sync = FB_SYNC_VERT_HIGH_ACT;
+
+ if (hsync)
+ videomode->sync |= FB_SYNC_HOR_HIGH_ACT;
+
+ if (!videomode->pixclock)
+ return DIS_SUCCESS;
+
+ pixclock = PICOS2KHZ(videomode->pixclock) * 1000;
+
+ htotal = videomode->xres + videomode->right_margin +
+ videomode->hsync_len + videomode->left_margin;
+ vtotal = videomode->yres + videomode->lower_margin +
+ videomode->vsync_len + videomode->upper_margin;
+
+ if (videomode->vmode & FB_VMODE_INTERLACED)
+ vtotal /= 2;
+ if (videomode->vmode & FB_VMODE_DOUBLE)
+ vtotal *= 2;
+
+ hfreq = pixclock/htotal;
+ videomode->refresh = hfreq/vtotal;
+ return DIS_SUCCESS;
+}
+
__s32 BSP_disp_get_timing(__u32 sel, __disp_tcon_timing_t *tt)
{
memset(tt, 0, sizeof(__disp_tcon_timing_t));
diff --git a/drivers/video/sunxi/hdmi/drv_hdmi.c b/drivers/video/sunxi/hdmi/drv_hdmi.c
index e8673770cb5b..7eb0987c8e2f 100644
--- a/drivers/video/sunxi/hdmi/drv_hdmi.c
+++ b/drivers/video/sunxi/hdmi/drv_hdmi.c
@@ -163,6 +163,27 @@ __s32 Hdmi_set_display_mode(__disp_tv_mode_t mode)
return 0;
}
+__s32 Hdmi_set_display_videomode(const struct __disp_video_timing *mode)
+{
+ __inf("[Hdmi_set_display_videomode]\n");
+
+ if (video_mode != HDMI_EDID)
+ return -1;
+
+ if (memcmp(mode, &video_timing[video_timing_edid],
+ sizeof(struct __disp_video_timing)) != 0) {
+
+ if (hdmi_state >= HDMI_State_Video_config)
+ hdmi_state = HDMI_State_Video_config;
+
+ memcpy(&video_timing[video_timing_edid], mode,
+ sizeof(struct __disp_video_timing));
+
+ }
+
+ return 0;
+}
+
__s32 Hdmi_Audio_Enable(__u8 mode, __u8 channel)
{
__inf("[Hdmi_Audio_Enable],ch:%d\n", channel);
@@ -316,6 +337,7 @@ __s32 Hdmi_init(void)
disp_func.Hdmi_open = Hdmi_open;
disp_func.Hdmi_close = Hdmi_close;
disp_func.hdmi_set_mode = Hdmi_set_display_mode;
+ disp_func.hdmi_set_videomode = Hdmi_set_display_videomode;
disp_func.hdmi_mode_support = Hdmi_mode_support;
disp_func.hdmi_get_video_timing = hdmi_get_video_timing;
disp_func.hdmi_get_HPD_status = Hdmi_get_HPD_status;
diff --git a/include/video/sunxi_disp_ioctl.h b/include/video/sunxi_disp_ioctl.h
index 295b8d853fa6..36fc7d3f0015 100644
--- a/include/video/sunxi_disp_ioctl.h
+++ b/include/video/sunxi_disp_ioctl.h
@@ -452,6 +452,7 @@ typedef struct {
__s32(*Hdmi_open) (void);
__s32(*Hdmi_close) (void);
__s32(*hdmi_set_mode) (__disp_tv_mode_t mode);
+ __s32(*hdmi_set_videomode) (const struct __disp_video_timing *mode);
__s32(*hdmi_mode_support) (__disp_tv_mode_t mode);
__s32(*hdmi_get_video_timing) (__disp_tv_mode_t mode,
struct __disp_video_timing *video_timing);