* [U-Boot] [PATCH 0/5] sunxi: display: Add DDC & EDID support
@ 2014-11-24 16:14 Hans de Goede
2014-11-24 16:14 ` [U-Boot] [PATCH 1/5] sunxi: display: Remove unused HDMI register addr defines Hans de Goede
` (4 more replies)
0 siblings, 5 replies; 12+ messages in thread
From: Hans de Goede @ 2014-11-24 16:14 UTC (permalink / raw)
To: u-boot
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
^ permalink raw reply [flat|nested] 12+ messages in thread
* [U-Boot] [PATCH 1/5] sunxi: display: Remove unused HDMI register addr defines
2014-11-24 16:14 [U-Boot] [PATCH 0/5] sunxi: display: Add DDC & EDID support Hans de Goede
@ 2014-11-24 16:14 ` Hans de Goede
2014-11-24 16:21 ` Ian Campbell
2014-11-24 19:39 ` Anatolij Gustschin
2014-11-24 16:14 ` [U-Boot] [PATCH 2/5] sunxi: display: Do not use eprintf in early boot Hans de Goede
` (3 subsequent siblings)
4 siblings, 2 replies; 12+ messages in thread
From: Hans de Goede @ 2014-11-24 16:14 UTC (permalink / raw)
To: u-boot
All hdmi code uses struct sunxi_hdmi_reg, so these are not needed.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/include/asm/arch-sunxi/display.h | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/arch/arm/include/asm/arch-sunxi/display.h b/arch/arm/include/asm/arch-sunxi/display.h
index c17c3c3..ddb71c1 100644
--- a/arch/arm/include/asm/arch-sunxi/display.h
+++ b/arch/arm/include/asm/arch-sunxi/display.h
@@ -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 0x20C
struct sunxi_hdmi_reg {
u32 version_id; /* 0x000 */
u32 ctrl; /* 0x004 */
--
2.1.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [U-Boot] [PATCH 2/5] sunxi: display: Do not use eprintf in early boot
2014-11-24 16:14 [U-Boot] [PATCH 0/5] sunxi: display: Add DDC & EDID support Hans de Goede
2014-11-24 16:14 ` [U-Boot] [PATCH 1/5] sunxi: display: Remove unused HDMI register addr defines Hans de Goede
@ 2014-11-24 16:14 ` Hans de Goede
2014-11-24 16:21 ` Ian Campbell
2014-11-24 19:40 ` Anatolij Gustschin
2014-11-24 16:14 ` [U-Boot] [PATCH 3/5] edid: Add an edid_dtd_to_fbmode() helper function Hans de Goede
` (2 subsequent siblings)
4 siblings, 2 replies; 12+ messages in thread
From: Hans de Goede @ 2014-11-24 16:14 UTC (permalink / raw)
To: u-boot
stderr is not hooked up yet as this point, so use a regular printf.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/video/sunxi_display.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c
index fb28c23..5154084 100644
--- a/drivers/video/sunxi_display.c
+++ b/drivers/video/sunxi_display.c
@@ -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");
}
}
--
2.1.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [U-Boot] [PATCH 3/5] edid: Add an edid_dtd_to_fbmode() helper function
2014-11-24 16:14 [U-Boot] [PATCH 0/5] sunxi: display: Add DDC & EDID support Hans de Goede
2014-11-24 16:14 ` [U-Boot] [PATCH 1/5] sunxi: display: Remove unused HDMI register addr defines Hans de Goede
2014-11-24 16:14 ` [U-Boot] [PATCH 2/5] sunxi: display: Do not use eprintf in early boot Hans de Goede
@ 2014-11-24 16:14 ` Hans de Goede
2014-11-24 21:27 ` [U-Boot] [U-Boot, " Hans de Goede
2014-11-24 16:14 ` [U-Boot] [PATCH 4/5] edid: Add an edid_check_checksum() " Hans de Goede
2014-11-24 16:14 ` [U-Boot] [PATCH 5/5] sunxi: display: Add DDC & EDID support Hans de Goede
4 siblings, 1 reply; 12+ messages in thread
From: Hans de Goede @ 2014-11-24 16:14 UTC (permalink / raw)
To: u-boot
Various 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.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
common/edid.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
include/edid.h | 18 ++++++++++++++++
2 files changed, 83 insertions(+)
diff --git a/common/edid.c b/common/edid.c
index e66108f..e41cd3e 100644
--- a/common/edid.c
+++ b/common/edid.c
@@ -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;
+}
diff --git a/include/edid.h b/include/edid.h
index 480a773..d66f76b 100644
--- a/include/edid.h
+++ b/include/edid.h
@@ -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_ */
--
2.1.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [U-Boot] [PATCH 4/5] edid: Add an edid_check_checksum() helper function
2014-11-24 16:14 [U-Boot] [PATCH 0/5] sunxi: display: Add DDC & EDID support Hans de Goede
` (2 preceding siblings ...)
2014-11-24 16:14 ` [U-Boot] [PATCH 3/5] edid: Add an edid_dtd_to_fbmode() helper function Hans de Goede
@ 2014-11-24 16:14 ` Hans de Goede
2014-11-24 16:14 ` [U-Boot] [PATCH 5/5] sunxi: display: Add DDC & EDID support Hans de Goede
4 siblings, 0 replies; 12+ messages in thread
From: Hans de Goede @ 2014-11-24 16:14 UTC (permalink / raw)
To: u-boot
Add a helper function to check the checksum of an EDID data block.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
common/edid.c | 11 +++++++++++
include/edid.h | 9 +++++++++
2 files changed, 20 insertions(+)
diff --git a/common/edid.c b/common/edid.c
index e41cd3e..e5d35b2 100644
--- a/common/edid.c
+++ b/common/edid.c
@@ -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;
+}
diff --git a/include/edid.h b/include/edid.h
index d66f76b..b32f42c 100644
--- a/include/edid.h
+++ b/include/edid.h
@@ -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_ */
--
2.1.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [U-Boot] [PATCH 5/5] sunxi: display: Add DDC & EDID support
2014-11-24 16:14 [U-Boot] [PATCH 0/5] sunxi: display: Add DDC & EDID support Hans de Goede
` (3 preceding siblings ...)
2014-11-24 16:14 ` [U-Boot] [PATCH 4/5] edid: Add an edid_check_checksum() " Hans de Goede
@ 2014-11-24 16:14 ` Hans de Goede
2014-11-25 8:53 ` Ian Campbell
4 siblings, 1 reply; 12+ messages in thread
From: Hans de Goede @ 2014-11-24 16:14 UTC (permalink / raw)
To: u-boot
Add 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.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
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(-)
diff --git a/arch/arm/include/asm/arch-sunxi/display.h b/arch/arm/include/asm/arch-sunxi/display.h
index ddb71c1..8c4835e 100644
--- a/arch/arm/include/asm/arch-sunxi/display.h
+++ b/arch/arm/include/asm/arch-sunxi/display.h
@@ -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 */
diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c
index 5154084..9f31ac5 100644
--- a/drivers/video/sunxi_display.c
+++ b/drivers/video/sunxi_display.c
@@ -13,6 +13,8 @@
#include <asm/arch/display.h>
#include <asm/global_data.h>
#include <asm/io.h>
+#include <edid.h>
+#include <errno.h>
#include <fdtdec.h>
#include <fdt_support.h>
#include <linux/fb.h>
@@ -25,6 +27,22 @@ struct sunxi_display {
bool enabled;
} sunxi_display;
+/*
+ * Wait up to 200ms for value to be set in given part of reg.
+ */
+static int await_completion(u32 *reg, u32 mask, u32 val)
+{
+ unsigned long tmo = timer_get_us() + 200000;
+
+ while ((readl(reg) & mask) != val) {
+ if (timer_get_us() > tmo) {
+ printf("DDC: timeout reading EDID\n");
+ return -ETIME;
+ }
+ }
+ return 0;
+}
+
static int sunxi_hdmi_hpd_detect(void)
{
struct sunxi_ccm_reg * const ccm =
@@ -68,6 +86,140 @@ static int sunxi_hdmi_hpd_detect(void)
return 0;
}
+static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n)
+{
+ struct sunxi_hdmi_reg * const hdmi =
+ (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
+
+ setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR);
+ writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) |
+ SUNXI_HMDI_DDC_ADDR_EDDC_ADDR |
+ SUNXI_HMDI_DDC_ADDR_OFFSET(offset) |
+ SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr);
+#ifndef CONFIG_MACH_SUN6I
+ writel(n, &hdmi->ddc_byte_count);
+ writel(cmnd, &hdmi->ddc_cmnd);
+#else
+ writel(n << 16 | cmnd, &hdmi->ddc_cmnd);
+#endif
+ setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START);
+
+ return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0);
+}
+
+static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count)
+{
+ struct sunxi_hdmi_reg * const hdmi =
+ (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
+ int i, n;
+
+ while (count > 0) {
+ if (count > 16)
+ n = 16;
+ else
+ n = count;
+
+ if (sunxi_hdmi_ddc_do_command(
+ SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ,
+ offset, n))
+ return -ETIME;
+
+ for (i = 0; i < n; i++)
+ *buf++ = readb(&hdmi->ddc_fifo_data);
+
+ offset += n;
+ count -= n;
+ }
+
+ return 0;
+}
+
+static int sunxi_hdmi_edid_get_mode(struct fb_videomode *mode)
+{
+ struct edid1_info edid1;
+ struct edid_detailed_timing *t =
+ (struct edid_detailed_timing *)edid1.monitor_details.timing;
+ struct sunxi_hdmi_reg * const hdmi =
+ (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
+ struct sunxi_ccm_reg * const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+ static char name[16];
+ int i, r, retries = 2;
+
+ /* SUNXI_HDMI_CTRL_ENABLE & PAD_CTRL0 are already set by hpd_detect */
+ writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE,
+ &hdmi->pad_ctrl1);
+ writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15),
+ &hdmi->pll_ctrl);
+ writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
+
+ /* Reset i2c controller */
+ setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
+ writel(SUNXI_HMDI_DDC_CTRL_ENABLE |
+ SUNXI_HMDI_DDC_CTRL_SDA_ENABLE |
+ SUNXI_HMDI_DDC_CTRL_SCL_ENABLE |
+ SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl);
+ if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0))
+ return -EIO;
+
+ writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock);
+#ifndef CONFIG_MACH_SUN6I
+ writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE |
+ SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl);
+#endif
+
+ do {
+ r = sunxi_hdmi_ddc_read(0, (u8 *)&edid1, 128);
+ if (r)
+ continue;
+ r = edid_check_checksum((u8 *)&edid1);
+ if (r) {
+ printf("EDID: checksum error%s\n",
+ retries ? ", retrying" : "");
+ }
+ } while (r && retries--);
+
+ /* Disable DDC engine, no longer needed */
+ clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE);
+ clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
+
+ if (r)
+ return r;
+
+ r = edid_check_info(&edid1);
+ if (r) {
+ printf("EDID: invalid EDID data\n");
+ return -EINVAL;
+ }
+
+ /* We want version 1.3 or 1.2 with detailed timing info */
+ if (edid1.version != 1 || (edid1.revision < 3 &&
+ !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) {
+ printf("EDID: unsupported version %d.%d\n",
+ edid1.version, edid1.revision);
+ return -EINVAL;
+ }
+
+ /* Take the first usable detailed timing */
+ for (i = 0; i < 4; i++, t++) {
+ /* Skip interlaced (not implemented) or stereo modes. */
+ if (EDID_DETAILED_TIMING_FLAG_INTERLACED(*t) ||
+ EDID_DETAILED_TIMING_FLAG_STEREO(*t) ||
+ EDID_DETAILED_TIMING_FLAG_INTERLEAVED(*t))
+ continue;
+
+ r = edid_dtd_to_fbmode(t, mode, name, sizeof(name));
+ if (r == 0)
+ break;
+ }
+ if (i == 4) {
+ printf("EDID: no usable detailed timing found\n");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
/*
* This is the entity that mixes and matches the different layers and inputs.
* Allwinner calls it the back-end, but i like composer better.
@@ -446,6 +598,7 @@ void *video_hw_init(void)
{
static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
struct fb_videomode mode;
+ char *modestr;
int ret;
memset(&sunxi_display, 0, sizeof(struct sunxi_display));
@@ -459,7 +612,16 @@ void *video_hw_init(void)
return NULL;
sunxi_display.enabled = true;
- video_get_mode(getenv("hdmi_mode"), &mode);
+
+ modestr = getenv("hdmi_mode");
+
+ if (modestr) {
+ video_get_mode(modestr, &mode);
+ } else {
+ ret = sunxi_hdmi_edid_get_mode(&mode);
+ if (ret)
+ video_get_mode(NULL, &mode);
+ }
printf("HDMI connected, setting up a %s console.\n", mode.name);
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h
index a6cdffb..cf56b64 100644
--- a/include/configs/sunxi-common.h
+++ b/include/configs/sunxi-common.h
@@ -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
--
2.1.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [U-Boot] [PATCH 1/5] sunxi: display: Remove unused HDMI register addr defines
2014-11-24 16:14 ` [U-Boot] [PATCH 1/5] sunxi: display: Remove unused HDMI register addr defines Hans de Goede
@ 2014-11-24 16:21 ` Ian Campbell
2014-11-24 19:39 ` Anatolij Gustschin
1 sibling, 0 replies; 12+ messages in thread
From: Ian Campbell @ 2014-11-24 16:21 UTC (permalink / raw)
To: u-boot
On Mon, 2014-11-24 at 17:14 +0100, Hans de Goede wrote:
> All hdmi code uses struct sunxi_hdmi_reg, so these are not needed.
>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Ian Campbell <ijc@hellion.org.uk>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [U-Boot] [PATCH 2/5] sunxi: display: Do not use eprintf in early boot
2014-11-24 16:14 ` [U-Boot] [PATCH 2/5] sunxi: display: Do not use eprintf in early boot Hans de Goede
@ 2014-11-24 16:21 ` Ian Campbell
2014-11-24 19:40 ` Anatolij Gustschin
1 sibling, 0 replies; 12+ messages in thread
From: Ian Campbell @ 2014-11-24 16:21 UTC (permalink / raw)
To: u-boot
On Mon, 2014-11-24 at 17:14 +0100, Hans de Goede wrote:
> stderr is not hooked up yet as this point, so use a regular printf.
>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Ian Campbell <ijc@hellion.org.uk>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [U-Boot] [PATCH 1/5] sunxi: display: Remove unused HDMI register addr defines
2014-11-24 16:14 ` [U-Boot] [PATCH 1/5] sunxi: display: Remove unused HDMI register addr defines Hans de Goede
2014-11-24 16:21 ` Ian Campbell
@ 2014-11-24 19:39 ` Anatolij Gustschin
1 sibling, 0 replies; 12+ messages in thread
From: Anatolij Gustschin @ 2014-11-24 19:39 UTC (permalink / raw)
To: u-boot
On Mon, 24 Nov 2014 17:14:15 +0100
Hans de Goede <hdegoede@redhat.com> wrote:
> All hdmi code uses struct sunxi_hdmi_reg, so these are not needed.
>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Anatolij Gustschin <agust@denx.de>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [U-Boot] [PATCH 2/5] sunxi: display: Do not use eprintf in early boot
2014-11-24 16:14 ` [U-Boot] [PATCH 2/5] sunxi: display: Do not use eprintf in early boot Hans de Goede
2014-11-24 16:21 ` Ian Campbell
@ 2014-11-24 19:40 ` Anatolij Gustschin
1 sibling, 0 replies; 12+ messages in thread
From: Anatolij Gustschin @ 2014-11-24 19:40 UTC (permalink / raw)
To: u-boot
On Mon, 24 Nov 2014 17:14:16 +0100
Hans de Goede <hdegoede@redhat.com> wrote:
> stderr is not hooked up yet as this point, so use a regular printf.
>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Anatolij Gustschin <agust@denx.de>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [U-Boot] [U-Boot, 3/5] edid: Add an edid_dtd_to_fbmode() helper function
2014-11-24 16:14 ` [U-Boot] [PATCH 3/5] edid: Add an edid_dtd_to_fbmode() helper function Hans de Goede
@ 2014-11-24 21:27 ` Hans de Goede
0 siblings, 0 replies; 12+ messages in thread
From: Hans de Goede @ 2014-11-24 21:27 UTC (permalink / raw)
To: u-boot
Hi,
On 11/24/2014 05:14 PM, Hans de Goede wrote:
> Various 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.
>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
> common/edid.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> include/edid.h | 18 ++++++++++++++++
> 2 files changed, 83 insertions(+)
>
> diff --git a/common/edid.c b/common/edid.c
> index e66108f..e41cd3e 100644
> --- a/common/edid.c
> +++ b/common/edid.c
> @@ -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;
Note left and right margin are swapped here, I've this fixed in my
local tree.
Regards,
Hans
> +
> + 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;
> +}
> diff --git a/include/edid.h b/include/edid.h
> index 480a773..d66f76b 100644
> --- a/include/edid.h
> +++ b/include/edid.h
> @@ -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_ */
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [U-Boot] [PATCH 5/5] sunxi: display: Add DDC & EDID support
2014-11-24 16:14 ` [U-Boot] [PATCH 5/5] sunxi: display: Add DDC & EDID support Hans de Goede
@ 2014-11-25 8:53 ` Ian Campbell
0 siblings, 0 replies; 12+ messages in thread
From: Ian Campbell @ 2014-11-25 8:53 UTC (permalink / raw)
To: u-boot
On Mon, 2014-11-24 at 17:14 +0100, Hans de Goede wrote:
> This can be overriden through the hdmi_mode env.
> 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.
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2014-11-25 8:53 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-11-24 16:14 [U-Boot] [PATCH 0/5] sunxi: display: Add DDC & EDID support Hans de Goede
2014-11-24 16:14 ` [U-Boot] [PATCH 1/5] sunxi: display: Remove unused HDMI register addr defines Hans de Goede
2014-11-24 16:21 ` Ian Campbell
2014-11-24 19:39 ` Anatolij Gustschin
2014-11-24 16:14 ` [U-Boot] [PATCH 2/5] sunxi: display: Do not use eprintf in early boot Hans de Goede
2014-11-24 16:21 ` Ian Campbell
2014-11-24 19:40 ` Anatolij Gustschin
2014-11-24 16:14 ` [U-Boot] [PATCH 3/5] edid: Add an edid_dtd_to_fbmode() helper function Hans de Goede
2014-11-24 21:27 ` [U-Boot] [U-Boot, " Hans de Goede
2014-11-24 16:14 ` [U-Boot] [PATCH 4/5] edid: Add an edid_check_checksum() " Hans de Goede
2014-11-24 16:14 ` [U-Boot] [PATCH 5/5] sunxi: display: Add DDC & EDID support Hans de Goede
2014-11-25 8:53 ` Ian Campbell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox