Skip to content

Commit

Permalink
video:sunxi:disp:Dynamic mode switching
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
techn authored and amery committed Jan 14, 2013
1 parent f7768d3 commit cfea974
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 1 deletion.
4 changes: 4 additions & 0 deletions drivers/video/sunxi/disp/bsp_display.h
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand Down
29 changes: 28 additions & 1 deletion drivers/video/sunxi/disp/dev_fb.c
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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 =
Expand Down
81 changes: 81 additions & 0 deletions drivers/video/sunxi/disp/disp_hdmi.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
73 changes: 73 additions & 0 deletions drivers/video/sunxi/disp/disp_lcd.c
Expand Up @@ -1818,6 +1818,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));
Expand Down
22 changes: 22 additions & 0 deletions drivers/video/sunxi/hdmi/drv_hdmi.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions include/video/sunxi_disp_ioctl.h
Expand Up @@ -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);
Expand Down

0 comments on commit cfea974

Please sign in to comment.