sunxi: display: Add DDC & EDID support
Subsystems (3)
cover sunxi: display: Add DDC & EDID support
Hi Ian and Anatolij,
The first 2 patches here are minor fixes to the existing sunxi display code, a quick review would be appreciated, so that I can queue them up for the next pull-req.
The other 3 patches are for next, so you can take your time reviewing them.
Anatolij, patches 3 and 4 add 2 new edid utility functions, so a review from you would be appreciated. Patch 5 uses them, so your input there would be appreciated too.
Regards,
Hans
PATCH 1/5 sunxi: display: Remove unused HDMI register addr defines
All hdmi code uses struct sunxi_hdmi_reg, so these are not needed.
--- arch/arm/include/asm/arch-sunxi/display.h | 13 ------------- 1 file changed, 13 deletions(-)
arch/arm/include/asm/arch-sunxi/display.h +0 -13
@@ -91,19 +91,6 @@ struct sunxi_lcdc_reg {u32 tcon1_io_tristate;/* 0xf4 */ }; -#define SUNXI_HDMI_CTRL 0x004 -#define SUNXI_HDMI_INT_CTRL 0x008 -#define SUNXI_HDMI_HPD 0x00c -#define SUNXI_HDMI_VIDEO_CTRL 0x010 -#define SUNXI_HDMI_VIDEO_SIZE 0x014 -#define SUNXI_HDMI_VIDEO_BP 0x018 -#define SUNXI_HDMI_VIDEO_FP 0x01c -#define SUNXI_HDMI_VIDEO_SPW 0x020 -#define SUNXI_HDMI_VIDEO_POLARITY 0x024 -#define SUNXI_HDMI_TX_DRIVER0 0x200 -#define SUNXI_HDMI_TX_DRIVER1 0x204 -#define SUNXI_HDMI_TX_DRIVER2 0x208 -#define SUNXI_HDMI_TX_DRIVER3 0x20Cstruct sunxi_hdmi_reg {u32 version_id;/* 0x000 */ u32 ctrl;/* 0x004 */
Signature · 1 line
2.1.0
PATCH 1/5 sunxi: display: Remove unused HDMI register addr defines
Quoted 3 lines · Hans de Goede
All hdmi code uses struct
sunxi_hdmi_reg, so these are not needed.Signed-off-by Hans de Goede <[email protected]>
PATCH 1/5 sunxi: display: Remove unused HDMI register addr defines
In reply to Hans de GoedeQuoted 3 lines · On Mon, 24 Nov 2014 17:14:15 +0100 Hans de Goede <[email protected]> wrote:
All hdmi code uses struct
sunxi_hdmi_reg, so these are not needed.Signed-off-by Hans de Goede <[email protected]>
PATCH 2/5 sunxi: display: Do not use eprintf in early boot
In reply to Hans de Goedestderr is not hooked up yet as this point, so use a regular printf.
--- drivers/video/sunxi_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/video/sunxi_display.c +1 -1
@@ -351,7 +351,7 @@ retry: (SUNXI_HDMI_IRQ_STATUS_FIFO_UF | SUNXI_HDMI_IRQ_STATUS_FIFO_OF)) {if (retries--) goto retry; -eprintf ("HDMI fifo under or overrun\n" ); +printf ("HDMI fifo under or overrun\n" ); } }
Signature · 1 line
2.1.0
PATCH 2/5 sunxi: display: Do not use eprintf in early boot
Quoted 3 lines · Hans de Goede
stderr is not hooked up yet as this point, so use a regular printf.
Signed-off-by Hans de Goede <[email protected]>
PATCH 2/5 sunxi: display: Do not use eprintf in early boot
In reply to Hans de GoedeQuoted 3 lines · On Mon, 24 Nov 2014 17:14:16 +0100 Hans de Goede <[email protected]> wrote:
stderr is not hooked up yet as this point, so use a regular printf.
Signed-off-by Hans de Goede <[email protected]>
PATCH 3/5 edid: Add an edid_dtd_to_fbmode() helper function
In reply to Hans de GoedeVarious u-boot video drivers use fb_videomode structs to store timing info, add a helper function to convert an EDID detailed timing into a fb_videomode struct.
--- common/edid.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/edid.h | 18 ++++++++++++++++ 2 files changed, 83 insertions(+)
common/edid.c +65 -0
@@ -12,6 +12,7 @@#include <common.h> #include <edid.h> +#include <errno.h> #include <linux/ctype.h> #include <linux/string.h>
@@ -288,3 +289,67 @@ void edid_print_info(struct edid1_info *edid_info)if (!have_timing)printf ("\tNone\n" ); } + +int edid_dtd_to_fbmode (struct edid_detailed_timing *t, +struct fb_videomode *mode,char *name,int name_len) +{ +int margin, h_total, v_total; + +if (EDID_DETAILED_TIMING_PIXEL_CLOCK (*t) ==0 || +EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE (*t) ==0 || +EDID_DETAILED_TIMING_HORIZONTAL_BLANKING (*t) ==0 || +EDID_DETAILED_TIMING_VERTICAL_ACTIVE (*t) ==0 || +EDID_DETAILED_TIMING_VERTICAL_BLANKING (*t) ==0 || +EDID_DETAILED_TIMING_HSYNC_OFFSET (*t) ==0 || +EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH (*t) ==0 || +EDID_DETAILED_TIMING_VSYNC_OFFSET (*t) ==0 || +EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH (*t) ==0 ) +return -EINVAL ; + + mode->name = name; + mode->xres =EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE (*t); + mode->yres =EDID_DETAILED_TIMING_VERTICAL_ACTIVE (*t); + mode->pixclock = (EDID_DETAILED_TIMING_PIXEL_CLOCK (*t) +500 ) / +1000 ; + + mode->left_margin =EDID_DETAILED_TIMING_HSYNC_OFFSET (*t); + mode->hsync_len =EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH (*t); + margin =EDID_DETAILED_TIMING_HORIZONTAL_BLANKING (*t) - + (mode->left_margin + mode->hsync_len); +if (margin <=0 ) +return -EINVAL ; + + mode->right_margin = margin; + + mode->lower_margin =EDID_DETAILED_TIMING_VSYNC_OFFSET (*t); + mode->vsync_len =EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH (*t); + margin =EDID_DETAILED_TIMING_VERTICAL_BLANKING (*t) - + (mode->lower_margin + mode->vsync_len); +if (margin <=0 ) +return -EINVAL ; + + mode->upper_margin = margin; + +if (EDID_DETAILED_TIMING_FLAG_INTERLEAVED (*t)) + mode->vmode =FB_VMODE_INTERLACED ; +else + mode->vmode =FB_VMODE_NONINTERLACED ; + + mode->sync =0 ; +if (EDID_DETAILED_TIMING_FLAG_HSYNC_POLARITY (*t)) + mode->sync |=FB_SYNC_HOR_HIGH_ACT ; +if (EDID_DETAILED_TIMING_FLAG_VSYNC_POLARITY (*t)) + mode->sync |=FB_SYNC_VERT_HIGH_ACT ; + + mode->flag =0 ; + + h_total = mode->xres +EDID_DETAILED_TIMING_HORIZONTAL_BLANKING (*t); + v_total = mode->yres +EDID_DETAILED_TIMING_VERTICAL_BLANKING (*t); + mode->refresh =EDID_DETAILED_TIMING_PIXEL_CLOCK (*t) / + (h_total * v_total); + +snprintf (name, name_len,"%dx%d@%d" , mode->xres, mode->yres, + mode->refresh); + +return 0 ; +}
include/edid.h +18 -0
@@ -13,6 +13,7 @@#ifndef __EDID_H_#define __EDID_H_ +#include <linux/fb.h> #include <linux/types.h> #define GET_BIT (_x, _pos) \
@@ -86,6 +87,10 @@ struct edid_detailed_timing { GET_BITS((_x).flags, 4, 3)#define EDID_DETAILED_TIMING_FLAG_POLARITY (_x) \ GET_BITS((_x).flags, 2, 1) +#define EDID_DETAILED_TIMING_FLAG_VSYNC_POLARITY (_x) \ + GET_BIT((_x).flags, 2) +#define EDID_DETAILED_TIMING_FLAG_HSYNC_POLARITY (_x) \ + GET_BIT((_x).flags, 1)#define EDID_DETAILED_TIMING_FLAG_INTERLEAVED (_x) \ GET_BIT((_x).flags, 0) } __attribute__ ((__packed__));
@@ -255,4 +260,17 @@ int edid_get_ranges(struct edid1_info *edid, unsigned int *hmin, unsigned int *hmax, unsigned int *vmin, unsigned int *vmax); +/** +* Convert an EDID detailed timing to a fb_videomode +* +* @param t The EDID detailed timing to be converted +* @param mode Returns the converted timing +* @param name Buffer for the mode name mode->name will be set to this +* @param name_len Length of name +* +* @return 0 on success, or a negative errno on error +*/ +int edid_dtd_to_fbmode (struct edid_detailed_timing *t, +struct fb_videomode *mode,char *name,int name_len); +#endif /* __EDID_H_ */
Signature · 1 line
2.1.0
PATCH 3/5 edid: Add an edid_dtd_to_fbmode() helper function
Hi,
Quoted 47 lines · Hans de Goede
Various u-boot video drivers use
fb_videomodestructs to store timing info, add a helper function to convert an EDID detailed timing into afb_videomodestruct.Signed-off-by Hans de Goede <[email protected]>--- common/edid.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/edid.h | 18 ++++++++++++++++ 2 files changed, 83 insertions(+)common/edid.c +22
-0@@ -12,6 +12,7 @@#include <common.h> #include <edid.h> +#include <errno.h> #include <linux/ctype.h> #include <linux/string.h> @@ -288,3 +289,67 @@ void edid_print_info(struct edid1_info *edid_info)if (!have_timing)printf ("\tNone\n" ); } + +int edid_dtd_to_fbmode (struct edid_detailed_timing *t, +struct fb_videomode *mode,char *name,int name_len) +{ +int margin, h_total, v_total; + +if (EDID_DETAILED_TIMING_PIXEL_CLOCK (*t) ==0 || +EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE (*t) ==0 || +EDID_DETAILED_TIMING_HORIZONTAL_BLANKING (*t) ==0 || +EDID_DETAILED_TIMING_VERTICAL_ACTIVE (*t) ==0 || +EDID_DETAILED_TIMING_VERTICAL_BLANKING (*t) ==0 || +EDID_DETAILED_TIMING_HSYNC_OFFSET (*t) ==0 || +EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH (*t) ==0 || +EDID_DETAILED_TIMING_VSYNC_OFFSET (*t) ==0 || +EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH (*t) ==0 ) +return -EINVAL ; + + mode->name = name; + mode->xres =EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE (*t); + mode->yres =EDID_DETAILED_TIMING_VERTICAL_ACTIVE (*t); + mode->pixclock = (EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) + 500) /
common/edid.c void edid_print_info(struct edid1_info *edid_info) · line 322
Quoted 10 lines
common/edid.c +10
-0+1000 ; + + mode->left_margin =EDID_DETAILED_TIMING_HSYNC_OFFSET (*t); + mode->hsync_len =EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH (*t); + margin =EDID_DETAILED_TIMING_HORIZONTAL_BLANKING (*t) - + (mode->left_margin + mode->hsync_len); +if (margin <=0 ) +return -EINVAL ; + + mode->right_margin = margin;
Note left and right margin are swapped here, I've this fixed in my local tree.
Regards,
Hans
Quoted 75 lines
common/edid.c +33
-0+ + mode->lower_margin =EDID_DETAILED_TIMING_VSYNC_OFFSET (*t); + mode->vsync_len =EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH (*t); + margin =EDID_DETAILED_TIMING_VERTICAL_BLANKING (*t) - + (mode->lower_margin + mode->vsync_len); +if (margin <=0 ) +return -EINVAL ; + + mode->upper_margin = margin; + +if (EDID_DETAILED_TIMING_FLAG_INTERLEAVED (*t)) + mode->vmode =FB_VMODE_INTERLACED ; +else + mode->vmode =FB_VMODE_NONINTERLACED ; + + mode->sync =0 ; +if (EDID_DETAILED_TIMING_FLAG_HSYNC_POLARITY (*t)) + mode->sync |=FB_SYNC_HOR_HIGH_ACT ; +if (EDID_DETAILED_TIMING_FLAG_VSYNC_POLARITY (*t)) + mode->sync |=FB_SYNC_VERT_HIGH_ACT ; + + mode->flag =0 ; + + h_total = mode->xres +EDID_DETAILED_TIMING_HORIZONTAL_BLANKING (*t); + v_total = mode->yres +EDID_DETAILED_TIMING_VERTICAL_BLANKING (*t); + mode->refresh =EDID_DETAILED_TIMING_PIXEL_CLOCK (*t) / + (h_total * v_total); + +snprintf (name, name_len,"%dx%d@%d" , mode->xres, mode->yres, + mode->refresh); + +return 0 ; +}include/edid.h +18
-0@@ -13,6 +13,7 @@#ifndef __EDID_H_#define __EDID_H_ +#include <linux/fb.h> #include <linux/types.h> #define GET_BIT (_x, _pos) \@@ -86,6 +87,10 @@ struct edid_detailed_timing { GET_BITS((_x).flags, 4, 3)#define EDID_DETAILED_TIMING_FLAG_POLARITY (_x) \ GET_BITS((_x).flags, 2, 1) +#define EDID_DETAILED_TIMING_FLAG_VSYNC_POLARITY (_x) \ + GET_BIT((_x).flags, 2) +#define EDID_DETAILED_TIMING_FLAG_HSYNC_POLARITY (_x) \ + GET_BIT((_x).flags, 1)#define EDID_DETAILED_TIMING_FLAG_INTERLEAVED (_x) \ GET_BIT((_x).flags, 0) } __attribute__ ((__packed__));@@ -255,4 +260,17 @@ int edid_get_ranges(struct edid1_info *edid, unsigned int *hmin, unsigned int *hmax, unsigned int *vmin, unsigned int *vmax); +/** +* Convert an EDID detailed timing to a fb_videomode +* +* @param t The EDID detailed timing to be converted +* @param mode Returns the converted timing +* @param name Buffer for the mode name mode->name will be set to this +* @param name_len Length of name +* +* @return 0 on success, or a negative errno on error +*/ +int edid_dtd_to_fbmode (struct edid_detailed_timing *t, +struct fb_videomode *mode,char *name,int name_len); +#endif /* __EDID_H_ */
PATCH 4/5 edid: Add an edid_check_checksum() helper function
In reply to Hans de GoedeAdd a helper function to check the checksum of an EDID data block.
--- common/edid.c | 11 +++++++++++ include/edid.h | 9 +++++++++ 2 files changed, 20 insertions(+)
common/edid.c +11 -0
@@ -353,3 +353,14 @@ int edid_dtd_to_fbmode(struct edid_detailed_timing *t,return 0 ; } + +int edid_check_checksum (u8 *edid_block) +{ +u8 checksum =0 ; +int i; + +for (i =0 ; i <128 ; i++) + checksum += edid_block[i]; + +return (checksum ==0 ) ?0 : -EINVAL ; +}
include/edid.h +9 -0
@@ -273,4 +273,13 @@ int edid_get_ranges(struct edid1_info *edid, unsigned int *hmin,int edid_dtd_to_fbmode (struct edid_detailed_timing *t,struct fb_videomode *mode,char *name,int name_len); +/** +* Check checksum of a 128 bytes EDID data block +* +* @param edid_block EDID block data +* +* @return 0 on success, or a negative errno on error +*/ +int edid_check_checksum (u8 *edid_block); +#endif /* __EDID_H_ */
Signature · 1 line
2.1.0
PATCH 5/5 sunxi: display: Add DDC & EDID support
In reply to Hans de GoedeAdd DDC & EDID support and use it to automatically select the native mode of the attached monitor. This can be overriden through the hdmi_mode env. variable.
--- arch/arm/include/asm/arch-sunxi/display.h | 85 ++++++++++++++++ drivers/video/sunxi_display.c | 164 +++++++++++++++++++++++++++++- include/configs/sunxi-common.h | 1 + 3 files changed, 249 insertions(+), 1 deletion(-)
arch/arm/include/asm/arch-sunxi/display.h +85 -0
@@ -107,6 +107,48 @@ struct sunxi_hdmi_reg {u32 pad_ctrl1;/* 0x204 */ u32 pll_ctrl;/* 0x208 */ u32 pll_dbg0;/* 0x20c */ +u32 pll_dbg1;/* 0x210 */ +u32 hpd_cec;/* 0x214 */ +u8 res1[0x28 ];/* 0x218 */ +u32 spd_pkt;/* 0x240 */ +u8 res2[0xac ];/* 0x244 */ +u32 pkt_ctrl0;/* 0x2f0 */ +u32 pkt_ctrl1;/* 0x2f4 */ +u8 res3[0x18 ];/* 0x2f8 */ +u32 audio_sample_count;/* 0x310 */ +u8 res4[0xec ];/* 0x314 */ +u32 audio_tx_fifo;/* 0x400 */ +u8 res5[0xfc ];/* 0x404 */ +#ifndef CONFIG_MACH_SUN6I +u32 ddc_ctrl;/* 0x500 */ +u32 ddc_addr;/* 0x504 */ +u32 ddc_int_mask;/* 0x508 */ +u32 ddc_int_status;/* 0x50c */ +u32 ddc_fifo_ctrl;/* 0x510 */ +u32 ddc_fifo_status;/* 0x514 */ +u32 ddc_fifo_data;/* 0x518 */ +u32 ddc_byte_count;/* 0x51c */ +u32 ddc_cmnd;/* 0x520 */ +u32 ddc_exreg;/* 0x524 */ +u32 ddc_clock;/* 0x528 */ +u8 res6[0x14 ];/* 0x52c */ +u32 ddc_line_ctrl;/* 0x540 */ +#else +u32 ddc_ctrl;/* 0x500 */ +u32 ddc_exreg;/* 0x504 */ +u32 ddc_cmnd;/* 0x508 */ +u32 ddc_addr;/* 0x50c */ +u32 ddc_int_mask;/* 0x510 */ +u32 ddc_int_status;/* 0x514 */ +u32 ddc_fifo_ctrl;/* 0x518 */ +u32 ddc_fifo_status;/* 0x51c */ +u32 ddc_clock;/* 0x520 */ +u32 ddc_timeout;/* 0x524 */ +u8 res6[0x18 ];/* 0x528 */ +u32 ddc_dbg;/* 0x540 */ +u8 res7[0x3c ];/* 0x544 */ +u32 ddc_fifo_data;/* 0x580 */ +#endif };/*
@@ -182,6 +224,49 @@ struct sunxi_hdmi_reg {#define SUNXI_HDMI_PLL_DBG0_PLL3 (0 << 21)#define SUNXI_HDMI_PLL_DBG0_PLL7 (1 << 21) +#ifdef CONFIG_MACH_SUN6I +#define SUNXI_HMDI_DDC_CTRL_ENABLE (1 << 0) +#define SUNXI_HMDI_DDC_CTRL_SCL_ENABLE (1 << 4) +#define SUNXI_HMDI_DDC_CTRL_SDA_ENABLE (1 << 6) +#define SUNXI_HMDI_DDC_CTRL_START (1 << 27) +#define SUNXI_HMDI_DDC_CTRL_RESET (1 << 31) +#else +#define SUNXI_HMDI_DDC_CTRL_RESET (1 << 0) +/* sun4i / sun5i / sun7i do not have a separate line_ctrl reg */ +#define SUNXI_HMDI_DDC_CTRL_SDA_ENABLE 0 +#define SUNXI_HMDI_DDC_CTRL_SCL_ENABLE 0 +#define SUNXI_HMDI_DDC_CTRL_START (1 << 30) +#define SUNXI_HMDI_DDC_CTRL_ENABLE (1 << 31) +#endif + +#ifdef CONFIG_MACH_SUN6I +#define SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR (0xa0 << 0) +#else +#define SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR (0x50 << 0) +#endif +#define SUNXI_HMDI_DDC_ADDR_OFFSET (n) (((n) & 0xff) << 8) +#define SUNXI_HMDI_DDC_ADDR_EDDC_ADDR (0x60 << 16) +#define SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT (n) ((n) << 24) + +#ifdef CONFIG_MACH_SUN6I +#define SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR (1 << 15) +#else +#define SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR (1 << 31) +#endif + +#define SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ 6 +#define SUNXI_HDMI_DDC_CMND_IMPLICIT_EDDC_READ 7 + +#ifdef CONFIG_MACH_SUN6I +#define SUNXI_HDMI_DDC_CLOCK 0x61 +#else +/* N = 5,M=1 Fscl= Ftmds/2/10/2^N/(M+1) */ +#define SUNXI_HDMI_DDC_CLOCK 0x0d +#endif + +#define SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE (1 << 8) +#define SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE (1 << 9) +int sunxi_simplefb_setup (void *blob);#endif /* _SUNXI_DISPLAY_H */
drivers/video/sunxi_display.c +163 -1
194 lines not on this page · raw message
include/configs/sunxi-common.h +1 -0
@@ -212,6 +212,7 @@#define CONFIG_CFB_CONSOLE #define CONFIG_VIDEO_SW_CURSOR #define CONFIG_VIDEO_LOGO +#define CONFIG_I2C_EDID /* allow both serial and cfb console. */ #define CONFIG_CONSOLE_MUX
Signature · 1 line
2.1.0
PATCH 5/5 sunxi: display: Add DDC & EDID support
Quoted 2 lines · Hans de Goede
This can be overriden through the
hdmi_modeenv.
variable.
Apart from the question regarding the variable name I asked on "sunxi:
video: Add extra modes and allow selecting the mode via hdmi_mode env" the code here looks fine to me.