[U-Boot] [PATCH 1/3] edid: add function to convert edid to fb_videomode

There may be some custom boards in the field which have an seperate eeprom chip to store edid informations in it. To make use of those edid information in the board code this patch add a function to convert edid to fb_videomode.
Signed-off-by: Christian Gmeiner christian.gmeiner@gmail.com --- common/edid.c | 30 ++++++++++++++++++++++++++++++ include/edid.h | 9 +++++++++ 2 files changed, 39 insertions(+)
diff --git a/common/edid.c b/common/edid.c index e66108f..ea9419c 100644 --- a/common/edid.c +++ b/common/edid.c @@ -12,6 +12,7 @@
#include <common.h> #include <edid.h> +#include <linux/fb.h> #include <linux/ctype.h> #include <linux/string.h>
@@ -288,3 +289,32 @@ void edid_print_info(struct edid1_info *edid_info) if (!have_timing) printf("\tNone\n"); } + +void edid_to_fb_videomode(struct edid1_info *edid, struct fb_videomode *mode) +{ + struct edid_monitor_descriptor *monitor = + &edid->monitor_details.descriptor[0]; + struct edid_detailed_timing *timing = + (struct edid_detailed_timing *)monitor; + + uint32_t pixclock = EDID_DETAILED_TIMING_PIXEL_CLOCK(*timing); + uint32_t h_blanking = EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*timing); + uint32_t h_active = EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*timing); + uint32_t h_sync_offset = EDID_DETAILED_TIMING_HSYNC_OFFSET(*timing); + uint32_t h_sync_width = EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH(*timing); + uint32_t v_blanking = EDID_DETAILED_TIMING_VERTICAL_BLANKING(*timing); + uint32_t v_active = EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*timing); + uint32_t v_sync_offset = EDID_DETAILED_TIMING_VSYNC_OFFSET(*timing); + uint32_t v_sync_width = EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH(*timing); + + mode->name = "EDID"; + mode->pixclock = KHZ2PICOS(pixclock/1000); + mode->yres = v_active; + mode->xres = h_active; + mode->left_margin = h_blanking - h_sync_offset - h_sync_width; + mode->right_margin = h_sync_offset; + mode->upper_margin = v_blanking - v_sync_offset - v_sync_width; + mode->lower_margin = v_sync_offset; + mode->hsync_len = h_sync_width; + mode->vsync_len = v_sync_width; +} diff --git a/include/edid.h b/include/edid.h index 480a773..357d6d6 100644 --- a/include/edid.h +++ b/include/edid.h @@ -234,6 +234,15 @@ struct edid1_info { void edid_print_info(struct edid1_info *edid_info);
/** + * Convert EDID info to struct fb_videomode + * + * @param edid The EDID info to be converted + * @param mode The destination fb_videomode to be filled + */ +struct fb_videomode; +void edid_to_fb_videomode(struct edid1_info *edid, struct fb_videomode *mode); + +/** * Check the EDID info. * * @param info The EDID info to be checked

This new function is used to set all display-timings properties based on fb_videomode.
display-timings { timing0 { clock-frequency = <25000000>; hactive = <640>; vactive = <480>; hback-porch = <48>; hfront-porch = <16>; vback-porch = <31>; vfront-porch = <12>; hsync-len = <96>; vsync-len = <2>; }; };
Signed-off-by: Christian Gmeiner christian.gmeiner@gmail.com --- common/fdt_support.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/fdt_support.h | 5 +++++ 2 files changed, 61 insertions(+)
diff --git a/common/fdt_support.c b/common/fdt_support.c index 784a570..72004a3 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -11,6 +11,7 @@ #include <stdio_dev.h> #include <linux/ctype.h> #include <linux/types.h> +#include <linux/fb.h> #include <asm/global_data.h> #include <libfdt.h> #include <fdt_support.h> @@ -1373,6 +1374,61 @@ err_size: #endif
/* +* fdt_find_display_timings: finde node containing display-timings +* +* @fdt: fdt to device tree +* @compat: compatiable string to match +* @parent: parent node containing display-timings +*/ +int fdt_find_display_timings(void *fdt, const char *compat, const char *parent) +{ + int coff = fdt_node_offset_by_compatible(fdt, -1, compat); + int poff = fdt_subnode_offset(fdt, coff, parent); + int noff = fdt_subnode_offset(fdt, poff, "display-timings"); + + return noff; +} + +/* +* fdt_update_display_timings: update display-timings properties +* +* @fdt: fdt to device tree +* @compat: compatiable string to match +* @parent: parent node containing display-timings +* @mode: ptr to fb_videomode +*/ +int fdt_update_display_timings(void *fdt, const char *compat, const char *parent, + struct fb_videomode *mode) +{ + int noff = fdt_find_display_timings(fdt, compat, parent); + + /* check if display-timings subnode does exist */ + if (noff == -FDT_ERR_NOTFOUND) { + return noff; + } + + /* check if timing0 subnode exists */ + noff = fdt_subnode_offset(fdt, noff, "timing0"); + if (noff == -FDT_ERR_NOTFOUND) { + return noff; + } + + /* set all needed properties */ + fdt_setprop_u32(fdt, noff, "clock-frequency", + PICOS2KHZ(mode->pixclock) * 1000); + fdt_setprop_u32(fdt, noff, "hactive", mode->xres); + fdt_setprop_u32(fdt, noff, "vactive", mode->yres); + fdt_setprop_u32(fdt, noff, "hback-porch", mode->left_margin); + fdt_setprop_u32(fdt, noff, "hfront-porch", mode->right_margin); + fdt_setprop_u32(fdt, noff, "vback-porch", mode->upper_margin); + fdt_setprop_u32(fdt, noff, "vfront-porch", mode->lower_margin); + fdt_setprop_u32(fdt, noff, "hsync-len", mode->hsync_len); + fdt_setprop_u32(fdt, noff, "vsync-len", mode->vsync_len); + + return 0; +} + +/* * Verify the physical address of device tree node for a given alias * * This function locates the device tree node of a given alias, and then diff --git a/include/fdt_support.h b/include/fdt_support.h index 1bda686..4222ab4 100644 --- a/include/fdt_support.h +++ b/include/fdt_support.h @@ -91,6 +91,11 @@ int fdt_set_phandle(void *fdt, int nodeoffset, uint32_t phandle); unsigned int fdt_create_phandle(void *fdt, int nodeoffset); int fdt_add_edid(void *blob, const char *compat, unsigned char *buf);
+struct fb_videomode; +int fdt_find_display_timings(void *fdt, const char *compat, const char *parent); +int fdt_update_display_timings(void *fdt, const char *compat, const char *parent, + struct fb_videomode *mode); + int fdt_verify_alias_address(void *fdt, int anode, const char *alias, u64 addr); u64 fdt_get_base_address(void *fdt, int node);

HI Christian,
On 15 September 2014 07:06, Christian Gmeiner christian.gmeiner@gmail.com wrote:
This new function is used to set all display-timings properties based on fb_videomode.
display-timings { timing0 { clock-frequency = <25000000>; hactive = <640>; vactive = <480>; hback-porch = <48>; hfront-porch = <16>; vback-porch = <31>; vfront-porch = <12>; hsync-len = <96>; vsync-len = <2>; }; };
Signed-off-by: Christian Gmeiner christian.gmeiner@gmail.com
Thanks for the patch. There are a few style violations and I have a few minor comments below.
$ ./tools/patman/patman -c1 -n Cleaned 1 patches 0 errors, 4 warnings, 0 checks for 0001-fdt-add-fdt_add_display_timings-.-and-friends.patch: warning: common/fdt_support.c,1400: line over 80 characters warning: common/fdt_support.c,1406: braces {} are not necessary for single statement blocks warning: common/fdt_support.c,1412: braces {} are not necessary for single statement blocks warning: include/fdt_support.h,96: line over 80 characters
checkpatch.pl found 0 error(s), 4 warning(s), 0 checks(s)
common/fdt_support.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/fdt_support.h | 5 +++++ 2 files changed, 61 insertions(+)
diff --git a/common/fdt_support.c b/common/fdt_support.c index 784a570..72004a3 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -11,6 +11,7 @@ #include <stdio_dev.h> #include <linux/ctype.h> #include <linux/types.h> +#include <linux/fb.h> #include <asm/global_data.h> #include <libfdt.h> #include <fdt_support.h> @@ -1373,6 +1374,61 @@ err_size: #endif
/* +* fdt_find_display_timings: finde node containing display-timings +* +* @fdt: fdt to device tree +* @compat: compatiable string to match +* @parent: parent node containing display-timings
or -ve error code FDT_ERROR_...
+*/ +int fdt_find_display_timings(void *fdt, const char *compat, const char *parent) +{
int coff = fdt_node_offset_by_compatible(fdt, -1, compat);
int poff = fdt_subnode_offset(fdt, coff, parent);
int noff = fdt_subnode_offset(fdt, poff, "display-timings");
Can we return an error when we see one? Here it will return a somewhat meaningless error if (say) the first call finds no node.
return noff;
+}
+/* +* fdt_update_display_timings: update display-timings properties +* +* @fdt: fdt to device tree +* @compat: compatiable string to match
compatible
+* @parent: parent node containing display-timings
@parent: parent node containing display-timings subnode
+* @mode: ptr to fb_videomode
Well we know that from the code. Perhaps "display timings to add to the device tree"
+*/
This function is exported so the comment should go in the header file.
+int fdt_update_display_timings(void *fdt, const char *compat, const char *parent,
struct fb_videomode *mode)
+{
int noff = fdt_find_display_timings(fdt, compat, parent);
/* check if display-timings subnode does exist */
if (noff == -FDT_ERR_NOTFOUND) {
if (noff < 0)
would be better
return noff;
}
/* check if timing0 subnode exists */
noff = fdt_subnode_offset(fdt, noff, "timing0");
if (noff == -FDT_ERR_NOTFOUND) {
same here
return noff;
}
/* set all needed properties */
fdt_setprop_u32(fdt, noff, "clock-frequency",
PICOS2KHZ(mode->pixclock) * 1000);
fdt_setprop_u32(fdt, noff, "hactive", mode->xres);
fdt_setprop_u32(fdt, noff, "vactive", mode->yres);
fdt_setprop_u32(fdt, noff, "hback-porch", mode->left_margin);
fdt_setprop_u32(fdt, noff, "hfront-porch", mode->right_margin);
fdt_setprop_u32(fdt, noff, "vback-porch", mode->upper_margin);
fdt_setprop_u32(fdt, noff, "vfront-porch", mode->lower_margin);
fdt_setprop_u32(fdt, noff, "hsync-len", mode->hsync_len);
fdt_setprop_u32(fdt, noff, "vsync-len", mode->vsync_len);
Should you have error checking here? We might run out of space.
return 0;
+}
+/*
- Verify the physical address of device tree node for a given alias
- This function locates the device tree node of a given alias, and then
diff --git a/include/fdt_support.h b/include/fdt_support.h index 1bda686..4222ab4 100644 --- a/include/fdt_support.h +++ b/include/fdt_support.h @@ -91,6 +91,11 @@ int fdt_set_phandle(void *fdt, int nodeoffset, uint32_t phandle); unsigned int fdt_create_phandle(void *fdt, int nodeoffset); int fdt_add_edid(void *blob, const char *compat, unsigned char *buf);
+struct fb_videomode; +int fdt_find_display_timings(void *fdt, const char *compat, const char *parent); +int fdt_update_display_timings(void *fdt, const char *compat, const char *parent,
struct fb_videomode *mode);
int fdt_verify_alias_address(void *fdt, int anode, const char *alias, u64 addr); u64 fdt_get_base_address(void *fdt, int node); --
Regards, Simon

Hi Simon
On 15 September 2014 07:06, Christian Gmeiner christian.gmeiner@gmail.com wrote:
This new function is used to set all display-timings properties based on fb_videomode.
display-timings { timing0 { clock-frequency = <25000000>; hactive = <640>; vactive = <480>; hback-porch = <48>; hfront-porch = <16>; vback-porch = <31>; vfront-porch = <12>; hsync-len = <96>; vsync-len = <2>; }; };
Signed-off-by: Christian Gmeiner christian.gmeiner@gmail.com
The ot1200 base patch has landed in u-boot-imx and I will work to get this EDID stuff mainline too.
Thanks for the patch. There are a few style violations and I have a few minor comments below.
$ ./tools/patman/patman -c1 -n Cleaned 1 patches 0 errors, 4 warnings, 0 checks for 0001-fdt-add-fdt_add_display_timings-.-and-friends.patch: warning: common/fdt_support.c,1400: line over 80 characters warning: common/fdt_support.c,1406: braces {} are not necessary for single statement blocks warning: common/fdt_support.c,1412: braces {} are not necessary for single statement blocks warning: include/fdt_support.h,96: line over 80 characters
checkpatch.pl found 0 error(s), 4 warning(s), 0 checks(s)
Will fix them in the next patch series about this topic.
common/fdt_support.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/fdt_support.h | 5 +++++ 2 files changed, 61 insertions(+)
diff --git a/common/fdt_support.c b/common/fdt_support.c index 784a570..72004a3 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -11,6 +11,7 @@ #include <stdio_dev.h> #include <linux/ctype.h> #include <linux/types.h> +#include <linux/fb.h> #include <asm/global_data.h> #include <libfdt.h> #include <fdt_support.h> @@ -1373,6 +1374,61 @@ err_size: #endif
/* +* fdt_find_display_timings: finde node containing display-timings +* +* @fdt: fdt to device tree +* @compat: compatiable string to match +* @parent: parent node containing display-timings
or -ve error code FDT_ERROR_...
ok
+*/ +int fdt_find_display_timings(void *fdt, const char *compat, const char *parent) +{
int coff = fdt_node_offset_by_compatible(fdt, -1, compat);
int poff = fdt_subnode_offset(fdt, coff, parent);
int noff = fdt_subnode_offset(fdt, poff, "display-timings");
Can we return an error when we see one? Here it will return a somewhat meaningless error if (say) the first call finds no node.
I can return the return code of the three functions. Something like:
int coff, poff, noff;
coff = fdt_node_offset_by_compatible(..); if (coff < 0) return coff;
poff = fdt_subnode_offset(..); if (poff < 0) ....
Would that be better?
return noff;
+}
+/* +* fdt_update_display_timings: update display-timings properties +* +* @fdt: fdt to device tree +* @compat: compatiable string to match
compatible
ok
+* @parent: parent node containing display-timings
@parent: parent node containing display-timings subnode
yes... thats better.
+* @mode: ptr to fb_videomode
Well we know that from the code. Perhaps "display timings to add to the device tree"
sounds good to me.
+*/
This function is exported so the comment should go in the header file.
okay.
+int fdt_update_display_timings(void *fdt, const char *compat, const char *parent,
struct fb_videomode *mode)
+{
int noff = fdt_find_display_timings(fdt, compat, parent);
/* check if display-timings subnode does exist */
if (noff == -FDT_ERR_NOTFOUND) {
if (noff < 0)
would be better
ok
return noff;
}
/* check if timing0 subnode exists */
noff = fdt_subnode_offset(fdt, noff, "timing0");
if (noff == -FDT_ERR_NOTFOUND) {
same here
ok
return noff;
}
/* set all needed properties */
fdt_setprop_u32(fdt, noff, "clock-frequency",
PICOS2KHZ(mode->pixclock) * 1000);
fdt_setprop_u32(fdt, noff, "hactive", mode->xres);
fdt_setprop_u32(fdt, noff, "vactive", mode->yres);
fdt_setprop_u32(fdt, noff, "hback-porch", mode->left_margin);
fdt_setprop_u32(fdt, noff, "hfront-porch", mode->right_margin);
fdt_setprop_u32(fdt, noff, "vback-porch", mode->upper_margin);
fdt_setprop_u32(fdt, noff, "vfront-porch", mode->lower_margin);
fdt_setprop_u32(fdt, noff, "hsync-len", mode->hsync_len);
fdt_setprop_u32(fdt, noff, "vsync-len", mode->vsync_len);
Should you have error checking here? We might run out of space.
Sounds like a good idea - will change the current code.
return 0;
+}
+/*
- Verify the physical address of device tree node for a given alias
- This function locates the device tree node of a given alias, and then
diff --git a/include/fdt_support.h b/include/fdt_support.h index 1bda686..4222ab4 100644 --- a/include/fdt_support.h +++ b/include/fdt_support.h @@ -91,6 +91,11 @@ int fdt_set_phandle(void *fdt, int nodeoffset, uint32_t phandle); unsigned int fdt_create_phandle(void *fdt, int nodeoffset); int fdt_add_edid(void *blob, const char *compat, unsigned char *buf);
+struct fb_videomode; +int fdt_find_display_timings(void *fdt, const char *compat, const char *parent); +int fdt_update_display_timings(void *fdt, const char *compat, const char *parent,
struct fb_videomode *mode);
int fdt_verify_alias_address(void *fdt, int anode, const char *alias, u64 addr); u64 fdt_get_base_address(void *fdt, int node); --
greets -- Christian Gmeiner, MSc

We support one base print with different panel sizes. In order to keep it simple we use one devicetree for the linux kernel. The timing values for each panel is stored on an i2c EEPROM. This EDID gets transformed to fb_videomode and later stored in the dtb.
Signed-off-by: Christian Gmeiner christian.gmeiner@gmail.com --- board/bachmann/ot1200/ot1200.c | 58 ++++++++++++++++++++++++++++++++++++++++++ include/configs/ot1200.h | 8 ++++++ 2 files changed, 66 insertions(+)
diff --git a/board/bachmann/ot1200/ot1200.c b/board/bachmann/ot1200/ot1200.c index 1c04f45..dfb4be7 100644 --- a/board/bachmann/ot1200/ot1200.c +++ b/board/bachmann/ot1200/ot1200.c @@ -24,6 +24,9 @@ #include <pca953x.h> #include <asm/gpio.h> #include <phy.h> +#include <edid.h> +#include <linux/fb.h> +#include <fdt_support.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -145,6 +148,20 @@ int board_mmc_init(bd_t *bis)
#define PC MUX_PAD_CTRL(I2C_PAD_CTRL)
+/* I2C2 - EDID */ +static struct i2c_pads_info i2c_pad_info1 = { + .scl = { + .i2c_mode = MX6_PAD_EIM_EB2__I2C2_SCL | PC, + .gpio_mode = MX6_PAD_EIM_EB2__GPIO2_IO30 | PC, + .gp = IMX_GPIO_NR(2, 30) + }, + .sda = { + .i2c_mode = MX6_PAD_EIM_D16__I2C2_SDA | PC, + .gpio_mode = MX6_PAD_EIM_D16__GPIO3_IO16 | PC, + .gp = IMX_GPIO_NR(3, 16) + } +}; + /* I2C3 - IO expander */ static struct i2c_pads_info i2c_pad_info2 = { .scl = { @@ -216,6 +233,46 @@ int board_eth_init(bd_t *bis) return 0; }
+#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP) +void ft_board_setup(void *blob, bd_t *bd) +{ + struct edid1_info edid; + struct fb_videomode mode; + int ret; + + i2c_set_bus_num(1); + ret = i2c_read(CONFIG_EDID_ADDR, 0, 1, (uchar *)&edid, sizeof(edid)); + if (ret != 0) { + puts("Error reading EDID content.\n"); + return; + } + + if (edid_check_info(&edid)) { + puts("Content isn't valid EDID.\n"); + return; + } + + edid_to_fb_videomode(&edid, &mode); + + /* clean-up all the mess */ + int noff = fdt_find_display_timings(blob, "fsl,imx6q-ldb", "lvds-channel"); + fdt_del_node(blob, noff); + + /* start from scratch */ + noff = fdt_node_offset_by_compatible(blob, -1, "fsl,imx6q-ldb"); + noff = fdt_subnode_offset(blob, noff, "lvds-channel"); + + fdt_add_subnode(blob, noff, "display-timings"); + noff = fdt_subnode_offset(blob, noff, "display-timings"); + fdt_add_subnode(blob, noff, "timing0"); + + /* fill with useful values */ + fdt_update_display_timings(blob, "fsl,imx6q-ldb", "lvds-channel", &mode); + + return; +} +#endif /* defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP) */ + int board_init(void) { struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; @@ -225,6 +282,7 @@ int board_init(void)
backlight_lcd_off();
+ setup_i2c(1, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info1); setup_i2c(2, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info2);
leds_on(); diff --git a/include/configs/ot1200.h b/include/configs/ot1200.h index fa21874..263b6a6 100644 --- a/include/configs/ot1200.h +++ b/include/configs/ot1200.h @@ -57,6 +57,14 @@ #define CONFIG_CMD_PCA953X #define CONFIG_CMD_PCA953X_INFO
+/* EDID eeprom */ +#define CONFIG_EDID_ADDR 0x52 + +/* OF Configs */ +#define CONFIG_I2C_EDID +#define CONFIG_OF_LIBFDT +#define CONFIG_OF_BOARD_SETUP + /* I2C Configs */ #define CONFIG_CMD_I2C #define CONFIG_SYS_I2C
participants (2)
-
Christian Gmeiner
-
Simon Glass