From 5ee0bea49ab8a150fc3c1f4def7b72df101db662 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Dec 2014 13:38:06 +0100 Subject: sunxi: video: Add hdmi support So far we've been programming the hdmi-encoder to send out dvi data over the hdmi connector. This works well for most devices, including hdmi devices, but not all devices accept dvi data on a hdmi input. Add support for sending proper hdmi data over the hdmi output found on most sunxi boards. This can be turned on by adding monitor=hdmi as option to the video-mode env. variable. A follow up patch will determine whether to send dvi or hdmi automatically when EDID is used. Signed-off-by: Hans de Goede Acked-by: Ian Campbell Acked-by: Anatolij Gustschin --- drivers/video/sunxi_display.c | 66 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 5 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index 0997740..394153a 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -403,8 +403,55 @@ static void sunxi_drc_init(void) } #endif +static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode) +{ + struct sunxi_hdmi_reg * const hdmi = + (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; + u8 checksum = 0; + u8 avi_info_frame[17] = { + 0x82, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x88, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 + }; + u8 vendor_info_frame[19] = { + 0x81, 0x01, 0x06, 0x29, 0x03, 0x0c, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 + }; + int i; + + if (mode->pixclock_khz <= 27000) + avi_info_frame[5] = 0x40; /* SD-modes, ITU601 colorspace */ + else + avi_info_frame[5] = 0x80; /* HD-modes, ITU709 colorspace */ + + if (mode->xres * 100 / mode->yres < 156) + avi_info_frame[5] |= 0x18; /* 4 : 3 */ + else + avi_info_frame[5] |= 0x28; /* 16 : 9 */ + + for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++) + checksum += avi_info_frame[i]; + + avi_info_frame[3] = 0x100 - checksum; + + for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++) + writeb(avi_info_frame[i], &hdmi->avi_info_frame[i]); + + writel(SUNXI_HDMI_QCP_PACKET0, &hdmi->qcp_packet0); + writel(SUNXI_HDMI_QCP_PACKET1, &hdmi->qcp_packet1); + + for (i = 0; i < ARRAY_SIZE(vendor_info_frame); i++) + writeb(vendor_info_frame[i], &hdmi->vendor_info_frame[i]); + + writel(SUNXI_HDMI_PKT_CTRL0, &hdmi->pkt_ctrl0); + writel(SUNXI_HDMI_PKT_CTRL1, &hdmi->pkt_ctrl1); + + setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_HDMI); +} + static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode, - int clk_div, int clk_double) + bool hdmi_mode, int clk_div, int clk_double) { struct sunxi_hdmi_reg * const hdmi = (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; @@ -413,6 +460,9 @@ static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode, /* Write clear interrupt status bits */ writel(SUNXI_HDMI_IRQ_STATUS_BITS, &hdmi->irq); + if (hdmi_mode) + sunxi_hdmi_setup_info_frames(mode); + /* Init various registers, select pll3 as clock source */ writel(SUNXI_HDMI_VIDEO_POL_TX_CLK, &hdmi->video_polarity); writel(SUNXI_HDMI_PAD_CTRL0_RUN, &hdmi->pad_ctrl0); @@ -458,7 +508,8 @@ static void sunxi_engines_init(void) #endif } -static void sunxi_mode_set(const struct ctfb_res_modes *mode, unsigned int address) +static void sunxi_mode_set(const struct ctfb_res_modes *mode, char *monitor, + unsigned int address) { struct sunxi_de_be_reg * const de_be = (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE; @@ -468,6 +519,7 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode, unsigned int addre (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; int clk_div, clk_double; int retries = 3; + bool hdmi_mode = strcmp(monitor, "hdmi") == 0; retry: clrbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_ENABLE); @@ -476,7 +528,7 @@ retry: sunxi_composer_mode_set(mode, address); sunxi_lcdc_mode_set(mode, &clk_div, &clk_double); - sunxi_hdmi_mode_set(mode, clk_div, clk_double); + sunxi_hdmi_mode_set(mode, hdmi_mode, clk_div, clk_double); setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS); setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START); @@ -511,6 +563,7 @@ void *video_hw_init(void) const char *options; unsigned int depth; int ret, hpd, edid; + char monitor[16]; memset(&sunxi_display, 0, sizeof(struct sunxi_display)); @@ -521,6 +574,8 @@ void *video_hw_init(void) video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode, &depth, &options); hpd = video_get_option_int(options, "hpd", 1); edid = video_get_option_int(options, "edid", 1); + video_get_option_string(options, "monitor", monitor, sizeof(monitor), + "dvi"); /* Always call hdp_detect, as it also enables various clocks, etc. */ ret = sunxi_hdmi_hpd_detect(); @@ -541,12 +596,13 @@ void *video_hw_init(void) printf("Only non-interlaced modes supported, falling back to 1024x768\n"); mode = &res_mode_init[RES_MODE_1024x768]; } else { - printf("Setting up a %dx%d console\n", mode->xres, mode->yres); + printf("Setting up a %dx%d %s console\n", + mode->xres, mode->yres, monitor); } sunxi_display.enabled = true; sunxi_engines_init(); - sunxi_mode_set(mode, gd->fb_base - CONFIG_SYS_SDRAM_BASE); + sunxi_mode_set(mode, monitor, gd->fb_base - CONFIG_SYS_SDRAM_BASE); /* * These are the only members of this structure that are used. All the -- cgit v1.1