diff --git a/arch/arm/boards/chumby_falconwing/falconwing.c b/arch/arm/boards/chumby_falconwing/falconwing.c index 2ae902fb6..24dc6e321 100644 --- a/arch/arm/boards/chumby_falconwing/falconwing.c +++ b/arch/arm/boards/chumby_falconwing/falconwing.c @@ -75,7 +75,6 @@ static struct fb_videomode falconwing_vmode = { .lower_margin = 8, .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, }; #define MAX_FB_SIZE SZ_1M diff --git a/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c b/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c index 3c8731160..912e13c32 100644 --- a/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c +++ b/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c @@ -76,7 +76,6 @@ static struct fb_videomode imxfb_mode = { .vsync_len = 3, .sync = 0, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, }; static void eukrea_cpuimx35_enable_display(int enable) diff --git a/arch/arm/boards/freescale-mx21-ads/imx21ads.c b/arch/arm/boards/freescale-mx21-ads/imx21ads.c index 1bbd8cbf2..bd7e46e16 100644 --- a/arch/arm/boards/freescale-mx21-ads/imx21ads.c +++ b/arch/arm/boards/freescale-mx21-ads/imx21ads.c @@ -62,7 +62,6 @@ static struct imx_fb_videomode imx_fb_modedata = { .vsync_len = 1, .sync = 0, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, }, .pcr = 0xfb108bc7, .bpp = 16, diff --git a/arch/arm/boards/freescale-mx28-evk/mx28-evk.c b/arch/arm/boards/freescale-mx28-evk/mx28-evk.c index 3bbdb1d5c..dce6d3103 100644 --- a/arch/arm/boards/freescale-mx28-evk/mx28-evk.c +++ b/arch/arm/boards/freescale-mx28-evk/mx28-evk.c @@ -201,7 +201,6 @@ static struct fb_videomode mx28_evk_vmodes[] = { .lower_margin = 10, .sync = FB_SYNC_DE_HIGH_ACT | FB_SYNC_CLK_INVERT, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, } }; diff --git a/arch/arm/boards/freescale-mx35-3ds/3stack.c b/arch/arm/boards/freescale-mx35-3ds/3stack.c index 2be1554b6..8f821adb7 100644 --- a/arch/arm/boards/freescale-mx35-3ds/3stack.c +++ b/arch/arm/boards/freescale-mx35-3ds/3stack.c @@ -95,7 +95,6 @@ static struct fb_videomode CTP_CLAA070LC0ACW = { .vsync_len = 1, /* note: DE only display */ .sync = FB_SYNC_CLK_IDLE_EN | FB_SYNC_OE_ACT_HIGH, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, }; static struct imx_ipu_fb_platform_data ipu_fb_data = { diff --git a/arch/arm/boards/friendlyarm-mini2440/mini2440.c b/arch/arm/boards/friendlyarm-mini2440/mini2440.c index 2c2426016..86e22ad13 100644 --- a/arch/arm/boards/friendlyarm-mini2440/mini2440.c +++ b/arch/arm/boards/friendlyarm-mini2440/mini2440.c @@ -85,7 +85,6 @@ static struct fb_videomode s3c24x0_fb_modes[] = { .pixclock = 115913, .sync = FB_SYNC_USE_PWREN, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, }, #endif #ifdef CONFIG_MINI2440_VIDEO_A70 diff --git a/arch/arm/boards/guf-cupid/board.c b/arch/arm/boards/guf-cupid/board.c index e171331a4..127edaa17 100644 --- a/arch/arm/boards/guf-cupid/board.c +++ b/arch/arm/boards/guf-cupid/board.c @@ -68,7 +68,6 @@ static struct fb_videomode guf_cupid_fb_mode = { .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_CLK_INVERT | FB_SYNC_OE_ACT_HIGH, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, }; #define GPIO_LCD_ENABLE (2 * 32 + 24) diff --git a/arch/arm/boards/karo-tx28/tx28-stk5.c b/arch/arm/boards/karo-tx28/tx28-stk5.c index 57cb0c390..2aaceb442 100644 --- a/arch/arm/boards/karo-tx28/tx28-stk5.c +++ b/arch/arm/boards/karo-tx28/tx28-stk5.c @@ -72,7 +72,6 @@ static struct fb_videomode tx28evk_vmodes[] = { .lower_margin = 10, .sync = FB_SYNC_DE_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, }, { /* * Emerging ETV570 640 x 480 display (directly connected) @@ -93,7 +92,6 @@ static struct fb_videomode tx28evk_vmodes[] = { .lower_margin = 10, .sync = FB_SYNC_DE_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, /* * This display is connected: * display side -> CPU side @@ -127,7 +125,6 @@ static struct fb_videomode tx28evk_vmodes[] = { .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_DE_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, }, { /* * Modeline "1024x768" x 60.0 @@ -149,7 +146,6 @@ static struct fb_videomode tx28evk_vmodes[] = { .lower_margin = 3, .sync = FB_SYNC_DE_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, }, { /* * Modeline "1280x1024" x 60.0 @@ -172,7 +168,6 @@ static struct fb_videomode tx28evk_vmodes[] = { .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_DE_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, }, }; diff --git a/arch/arm/boards/phytec-phycore-imx35/pcm043.c b/arch/arm/boards/phytec-phycore-imx35/pcm043.c index 46821a784..c1928cc8f 100644 --- a/arch/arm/boards/phytec-phycore-imx35/pcm043.c +++ b/arch/arm/boards/phytec-phycore-imx35/pcm043.c @@ -73,7 +73,6 @@ static struct fb_videomode pcm043_fb_mode[] = { .vsync_len = 1, .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_ACT_HIGH, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, }, { /* 240x320 @ 60 Hz */ .name = "Sharp-LQ035Q7", @@ -90,7 +89,6 @@ static struct fb_videomode pcm043_fb_mode[] = { .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | \ FB_SYNC_CLK_INVERT | FB_SYNC_CLK_IDLE_EN, .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, } }; diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 31edfca2b..d36d83d38 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_VIDEO) += fb.o +obj-$(CONFIG_OFDEVICE) += of_display_timing.o obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c new file mode 100644 index 000000000..5dfd5b3f1 --- /dev/null +++ b/drivers/video/of_display_timing.c @@ -0,0 +1,238 @@ +/* + * OF helpers for parsing display timings + * + * Copyright (c) 2012 Steffen Trumtrar , Pengutronix + * + * based on of_videomode.c by Sascha Hauer + * + * This file is released under the GPLv2 + */ +#include +#include +#include +#include + +void display_timings_release(struct display_timings *disp) +{ + free(disp->modes); + free(disp); +} +EXPORT_SYMBOL_GPL(display_timings_release); + +/** + * parse_timing_property - parse timing_entry from device_node + * @np: device_node with the property + * @name: name of the property + * @result: will be set to the return value + * + * DESCRIPTION: + * Every display_timing can be specified with either just the typical value or + * a range consisting of min/typ/max. This function helps handling this + **/ +static int parse_timing_property(const struct device_node *np, const char *name, + u32 *res) +{ + struct property *prop; + int length, cells, ret; + + prop = of_find_property(np, name, &length); + if (!prop) { + pr_err("%s: could not find property %s\n", + np->full_name, name); + return -EINVAL; + } + + cells = length / sizeof(u32); + if (cells == 1) { + ret = of_property_read_u32(np, name, res); + } else { + pr_err("%s: illegal timing specification in %s\n", + np->full_name, name); + return -EINVAL; + } + + return ret; +} + +/** + * of_parse_display_timing - parse display_timing entry from device_node + * @np: device_node with the properties + **/ +static int of_parse_display_timing(const struct device_node *np, + struct fb_videomode *mode) +{ + u32 val = 0, pixelclock = 0; + int ret = 0; + + memset(mode, 0, sizeof(*mode)); + + ret |= parse_timing_property(np, "hback-porch", &mode->left_margin); + ret |= parse_timing_property(np, "hfront-porch", &mode->right_margin); + ret |= parse_timing_property(np, "hactive", &mode->xres); + ret |= parse_timing_property(np, "hsync-len", &mode->hsync_len); + ret |= parse_timing_property(np, "vback-porch", &mode->upper_margin); + ret |= parse_timing_property(np, "vfront-porch", &mode->lower_margin); + ret |= parse_timing_property(np, "vactive", &mode->yres); + ret |= parse_timing_property(np, "vsync-len", &mode->vsync_len); + ret |= parse_timing_property(np, "clock-frequency", &pixelclock); + + mode->pixclock = pixelclock ? KHZ2PICOS(pixelclock / 1000) : 0; + + if (!of_property_read_u32(np, "vsync-active", &val)) + mode->sync |= val ? FB_SYNC_VERT_HIGH_ACT : 0; + if (!of_property_read_u32(np, "hsync-active", &val)) + mode->sync |= val ? FB_SYNC_HOR_HIGH_ACT : 0; + if (!of_property_read_u32(np, "de-active", &val)) + mode->display_flags |= val ? DISPLAY_FLAGS_DE_HIGH : + DISPLAY_FLAGS_DE_LOW; + if (!of_property_read_u32(np, "pixelclk-active", &val)) + mode->display_flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : + DISPLAY_FLAGS_PIXDATA_NEGEDGE; + + if (ret) { + pr_err("%s: error reading timing properties\n", + np->full_name); + return -EINVAL; + } + + return 0; +} + +/** + * of_get_display_timing - parse a display_timing entry + * @np: device_node with the timing subnode + * @name: name of the timing node + * @dt: display_timing struct to fill + **/ +int of_get_display_timing(struct device_node *np, const char *name, + struct fb_videomode *mode) +{ + struct device_node *timing_np; + + if (!np) { + pr_err("%s: no devicenode given\n", np->full_name); + return -EINVAL; + } + + timing_np = of_get_child_by_name(np, name); + if (!timing_np) { + pr_err("%s: could not find node '%s'\n", + np->full_name, name); + return -ENOENT; + } + + return of_parse_display_timing(timing_np, mode); +} +EXPORT_SYMBOL_GPL(of_get_display_timing); + +/** + * of_get_display_timings - parse all display_timing entries from a device_node + * @np: device_node with the subnodes + **/ +struct display_timings *of_get_display_timings(struct device_node *np) +{ + struct device_node *timings_np; + struct device_node *entry; + struct device_node *native_mode; + struct display_timings *disp; + + if (!np) { + pr_err("%s: no device node given\n", np->full_name); + return NULL; + } + + timings_np = of_get_child_by_name(np, "display-timings"); + if (!timings_np) { + pr_debug("%s: could not find display-timings node\n", + np->full_name); + return NULL; + } + + disp = xzalloc(sizeof(*disp)); + + entry = of_parse_phandle(timings_np, "native-mode", 0); + /* assume first child as native mode if none provided */ + if (!entry) + entry = of_get_next_available_child(np, NULL); + /* if there is no child, it is useless to go on */ + if (!entry) { + pr_err("%s: no timing specifications given\n", + np->full_name); + goto entryfail; + } + + pr_debug("%s: using %s as default timing\n", + np->full_name, entry->name); + + native_mode = entry; + + disp->num_modes = of_get_child_count(timings_np); + if (disp->num_modes == 0) { + /* should never happen, as entry was already found above */ + pr_err("%s: no timings specified\n", np->full_name); + goto entryfail; + } + + disp->modes = xzalloc(sizeof(struct fb_videomode) * disp->num_modes); + + disp->num_modes = 0; + disp->native_mode = 0; + + for_each_child_of_node(timings_np, entry) { + struct fb_videomode *mode; + int r; + + mode = &disp->modes[disp->num_modes]; + + r = of_parse_display_timing(entry, mode); + if (r) { + /* + * to not encourage wrong devicetrees, fail in case of + * an error + */ + pr_err("%s: error in timing %d\n", + np->full_name, disp->num_modes + 1); + goto timingfail; + } + + mode->name = xstrdup(entry->name); + + if (native_mode == entry) + disp->native_mode = disp->num_modes; + + disp->num_modes++; + } + + pr_debug("%s: got %d timings. Using timing #%d as default\n", + np->full_name, disp->num_modes, + disp->native_mode + 1); + + return disp; + +timingfail: + display_timings_release(disp); +entryfail: + free(disp); + + return NULL; +} +EXPORT_SYMBOL_GPL(of_get_display_timings); + +/** + * of_display_timings_exist - check if a display-timings node is provided + * @np: device_node with the timing + **/ +int of_display_timings_exist(struct device_node *np) +{ + struct device_node *timings_np; + + if (!np) + return -EINVAL; + + timings_np = of_parse_phandle(np, "display-timings", 0); + if (!timings_np) + return -EINVAL; + + return 1; +} +EXPORT_SYMBOL_GPL(of_display_timings_exist); diff --git a/include/fb.h b/include/fb.h index 91d3fe415..28e32fccd 100644 --- a/include/fb.h +++ b/include/fb.h @@ -4,6 +4,7 @@ #include #include #include +#include #define FB_VISUAL_TRUECOLOR 2 /* True color */ #define FB_VISUAL_PSEUDOCOLOR 3 /* Pseudo color (like atari) */ @@ -32,6 +33,16 @@ #define PICOS2KHZ(a) (1000000000UL/(a)) #define KHZ2PICOS(a) (1000000000UL/(a)) +enum display_flags { + /* data enable flag */ + DISPLAY_FLAGS_DE_LOW = BIT(4), + DISPLAY_FLAGS_DE_HIGH = BIT(5), + /* drive data on pos. edge */ + DISPLAY_FLAGS_PIXDATA_POSEDGE = BIT(6), + /* drive data on neg. edge */ + DISPLAY_FLAGS_PIXDATA_NEGEDGE = BIT(7), +}; + struct fb_videomode { const char *name; /* optional */ u32 refresh; /* optional */ @@ -46,7 +57,7 @@ struct fb_videomode { u32 vsync_len; u32 sync; u32 vmode; - u32 flag; + u32 display_flags; }; /* Interpretation of offset for color fields: All offsets are from the right, @@ -125,6 +136,8 @@ struct fb_info { */ }; +struct display_timings *of_get_display_timings(struct device_node *np); + int register_framebuffer(struct fb_info *info); #define FBIOGET_SCREENINFO _IOR('F', 1, loff_t)