mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-11 07:34:31 +00:00
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 <hdegoede@redhat.com> Acked-by: Ian Campbell <ijc@hellion.org.uk> Acked-by: Anatolij Gustschin <agust@denx.de>
This commit is contained in:
parent
75481607c7
commit
5ee0bea49a
2 changed files with 84 additions and 15 deletions
|
@ -102,23 +102,30 @@ struct sunxi_hdmi_reg {
|
|||
u32 video_fp; /* 0x01c */
|
||||
u32 video_spw; /* 0x020 */
|
||||
u32 video_polarity; /* 0x024 */
|
||||
u8 res0[0x1d8]; /* 0x028 */
|
||||
u8 res0[0x58]; /* 0x028 */
|
||||
u8 avi_info_frame[0x14]; /* 0x080 */
|
||||
u8 res1[0x4c]; /* 0x094 */
|
||||
u32 qcp_packet0; /* 0x0e0 */
|
||||
u32 qcp_packet1; /* 0x0e4 */
|
||||
u8 res2[0x118]; /* 0x0e8 */
|
||||
u32 pad_ctrl0; /* 0x200 */
|
||||
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 */
|
||||
u8 res3[0x28]; /* 0x218 */
|
||||
u8 vendor_info_frame[0x14]; /* 0x240 */
|
||||
u8 res4[0x9c]; /* 0x254 */
|
||||
u32 pkt_ctrl0; /* 0x2f0 */
|
||||
u32 pkt_ctrl1; /* 0x2f4 */
|
||||
u8 res3[0x18]; /* 0x2f8 */
|
||||
u8 res5[0x8]; /* 0x2f8 */
|
||||
u32 unknown; /* 0x300 */
|
||||
u8 res6[0xc]; /* 0x304 */
|
||||
u32 audio_sample_count; /* 0x310 */
|
||||
u8 res4[0xec]; /* 0x314 */
|
||||
u8 res7[0xec]; /* 0x314 */
|
||||
u32 audio_tx_fifo; /* 0x400 */
|
||||
u8 res5[0xfc]; /* 0x404 */
|
||||
u8 res8[0xfc]; /* 0x404 */
|
||||
#ifndef CONFIG_MACH_SUN6I
|
||||
u32 ddc_ctrl; /* 0x500 */
|
||||
u32 ddc_addr; /* 0x504 */
|
||||
|
@ -131,7 +138,7 @@ struct sunxi_hdmi_reg {
|
|||
u32 ddc_cmnd; /* 0x520 */
|
||||
u32 ddc_exreg; /* 0x524 */
|
||||
u32 ddc_clock; /* 0x528 */
|
||||
u8 res6[0x14]; /* 0x52c */
|
||||
u8 res9[0x14]; /* 0x52c */
|
||||
u32 ddc_line_ctrl; /* 0x540 */
|
||||
#else
|
||||
u32 ddc_ctrl; /* 0x500 */
|
||||
|
@ -144,9 +151,9 @@ struct sunxi_hdmi_reg {
|
|||
u32 ddc_fifo_status; /* 0x51c */
|
||||
u32 ddc_clock; /* 0x520 */
|
||||
u32 ddc_timeout; /* 0x524 */
|
||||
u8 res6[0x18]; /* 0x528 */
|
||||
u8 res9[0x18]; /* 0x528 */
|
||||
u32 ddc_dbg; /* 0x540 */
|
||||
u8 res7[0x3c]; /* 0x544 */
|
||||
u8 res10[0x3c]; /* 0x544 */
|
||||
u32 ddc_fifo_data; /* 0x580 */
|
||||
#endif
|
||||
};
|
||||
|
@ -191,9 +198,12 @@ struct sunxi_hdmi_reg {
|
|||
#define SUNXI_HDMI_IRQ_STATUS_BITS 0x73
|
||||
#define SUNXI_HDMI_HPD_DETECT (1 << 0)
|
||||
#define SUNXI_HDMI_VIDEO_CTRL_ENABLE (1 << 31)
|
||||
#define SUNXI_HDMI_VIDEO_CTRL_HDMI (1 << 30)
|
||||
#define SUNXI_HDMI_VIDEO_POL_HOR (1 << 0)
|
||||
#define SUNXI_HDMI_VIDEO_POL_VER (1 << 1)
|
||||
#define SUNXI_HDMI_VIDEO_POL_TX_CLK (0x3e0 << 16)
|
||||
#define SUNXI_HDMI_QCP_PACKET0 3
|
||||
#define SUNXI_HDMI_QCP_PACKET1 0
|
||||
|
||||
#ifdef CONFIG_MACH_SUN6I
|
||||
#define SUNXI_HDMI_PAD_CTRL0_HDP 0x7e80000f
|
||||
|
@ -224,6 +234,9 @@ struct sunxi_hdmi_reg {
|
|||
#define SUNXI_HDMI_PLL_DBG0_PLL3 (0 << 21)
|
||||
#define SUNXI_HDMI_PLL_DBG0_PLL7 (1 << 21)
|
||||
|
||||
#define SUNXI_HDMI_PKT_CTRL0 0x00000f21
|
||||
#define SUNXI_HDMI_PKT_CTRL1 0x0000000f
|
||||
|
||||
#ifdef CONFIG_MACH_SUN6I
|
||||
#define SUNXI_HMDI_DDC_CTRL_ENABLE (1 << 0)
|
||||
#define SUNXI_HMDI_DDC_CTRL_SCL_ENABLE (1 << 4)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue