- search for additional detailed timings in the EDID extension block

- rework sunxi DE2 driver and accompanying DW-HDMI platform driver
    to drop redundant device specific code, and later use the DT as a
    source of information
 -----BEGIN PGP SIGNATURE-----
 
 iGwEABECACwWIQSC4hxrSoIUVfFO0kRM6ATMmsalXAUCYIQmGw4cYWd1c3RAZGVu
 eC5kZQAKCRBM6ATMmsalXKPqAJsGVBUQ1+vFaUrdGKtGZk8TOjtyKwCfe9vuH/E6
 88T+ybssGrorMQwyOvQ=
 =hyBc
 -----END PGP SIGNATURE-----

Merge tag 'video-2021-07-rc1-2' of https://source.denx.de/u-boot/custodians/u-boot-video

 - search for additional detailed timings in the EDID extension block
 - rework sunxi DE2 driver and accompanying DW-HDMI platform driver
   to drop redundant device specific code, and later use the DT as a
   source of information
This commit is contained in:
Tom Rini 2021-04-24 13:30:57 -04:00
commit 2937f71206
3 changed files with 72 additions and 88 deletions

View File

@ -169,6 +169,29 @@ static bool cea_is_hdmi_vsdb_present(struct edid_cea861_info *info)
return false; return false;
} }
static bool edid_find_valid_timing(void *buf, int count,
struct display_timing *timing,
bool (*mode_valid)(void *priv,
const struct display_timing *timing),
void *mode_valid_priv)
{
struct edid_detailed_timing *t = buf;
bool found = false;
int i;
for (i = 0; i < count && !found; i++, t++)
if (EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) != 0) {
decode_timing((u8 *)t, timing);
if (mode_valid)
found = mode_valid(mode_valid_priv,
timing);
else
found = true;
}
return found;
}
int edid_get_timing_validate(u8 *buf, int buf_size, int edid_get_timing_validate(u8 *buf, int buf_size,
struct display_timing *timing, struct display_timing *timing,
int *panel_bits_per_colourp, int *panel_bits_per_colourp,
@ -177,44 +200,47 @@ int edid_get_timing_validate(u8 *buf, int buf_size,
void *mode_valid_priv) void *mode_valid_priv)
{ {
struct edid1_info *edid = (struct edid1_info *)buf; struct edid1_info *edid = (struct edid1_info *)buf;
bool timing_done; bool found;
int i;
if (buf_size < sizeof(*edid) || edid_check_info(edid)) { if (buf_size < sizeof(*edid) || edid_check_info(edid)) {
debug("%s: Invalid buffer\n", __func__); debug("%s: Invalid buffer\n", __func__);
return -EINVAL; return -EINVAL;
} }
if (!EDID1_INFO_VIDEO_INPUT_DIGITAL(*edid)) {
debug("%s: Not a digital display\n", __func__);
return -ENOSYS;
}
if (!EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(*edid)) { if (!EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(*edid)) {
debug("%s: No preferred timing\n", __func__); debug("%s: No preferred timing\n", __func__);
return -ENOENT; return -ENOENT;
} }
/* Look for detailed timing */ /* Look for detailed timing in base EDID */
timing_done = false; found = edid_find_valid_timing(edid->monitor_details.descriptor, 4,
for (i = 0; i < 4; i++) { timing, mode_valid, mode_valid_priv);
struct edid_monitor_descriptor *desc;
desc = &edid->monitor_details.descriptor[i]; /* Look for detailed timing in CTA-861 Extension Block */
if (desc->zero_flag_1 != 0) { if (!found && edid->extension_flag && buf_size >= EDID_EXT_SIZE) {
decode_timing((u8 *)desc, timing); struct edid_cea861_info *info =
if (mode_valid) (struct edid_cea861_info *)(buf + sizeof(*edid));
timing_done = mode_valid(mode_valid_priv,
timing);
else
timing_done = true;
if (timing_done) if (info->extension_tag == EDID_CEA861_EXTENSION_TAG) {
break; int count = EDID_CEA861_DTD_COUNT(*info);
int offset = info->dtd_offset;
int size = count * sizeof(struct edid_detailed_timing);
if (offset >= 4 && offset + size < EDID_SIZE)
found = edid_find_valid_timing(
(u8 *)info + offset, count, timing,
mode_valid, mode_valid_priv);
} }
} }
if (!timing_done)
if (!found)
return -EINVAL; return -EINVAL;
if (!EDID1_INFO_VIDEO_INPUT_DIGITAL(*edid)) {
debug("%s: Not a digital display\n", __func__);
return -ENOSYS;
}
if (edid->version != 1 || edid->revision < 4) { if (edid->version != 1 || edid->revision < 4) {
debug("%s: EDID version %d.%d does not have required info\n", debug("%s: EDID version %d.%d does not have required info\n",
__func__, edid->version, edid->revision); __func__, edid->version, edid->revision);

View File

@ -19,8 +19,6 @@
#include <asm/io.h> #include <asm/io.h>
#include <asm/arch/clock.h> #include <asm/arch/clock.h>
#include <asm/arch/display2.h> #include <asm/arch/display2.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include "simplefb_common.h" #include "simplefb_common.h"
@ -198,13 +196,6 @@ static int sunxi_de2_init(struct udevice *dev, ulong fbbase,
disp_uc_plat->source_id = mux; disp_uc_plat->source_id = mux;
ret = device_probe(disp);
if (ret) {
debug("%s: device '%s' display won't probe (ret=%d)\n",
__func__, dev->name, ret);
return ret;
}
ret = display_read_timing(disp, &timing); ret = display_read_timing(disp, &timing);
if (ret) { if (ret) {
debug("%s: Failed to read timings\n", __func__); debug("%s: Failed to read timings\n", __func__);
@ -245,8 +236,8 @@ static int sunxi_de2_probe(struct udevice *dev)
if (!(gd->flags & GD_FLG_RELOC)) if (!(gd->flags & GD_FLG_RELOC))
return 0; return 0;
ret = uclass_find_device_by_name(UCLASS_DISPLAY, ret = uclass_get_device_by_driver(UCLASS_DISPLAY,
"sunxi_lcd", &disp); DM_DRIVER_GET(sunxi_lcd), &disp);
if (!ret) { if (!ret) {
int mux; int mux;
@ -262,8 +253,8 @@ static int sunxi_de2_probe(struct udevice *dev)
debug("%s: lcd display not found (ret=%d)\n", __func__, ret); debug("%s: lcd display not found (ret=%d)\n", __func__, ret);
ret = uclass_find_device_by_name(UCLASS_DISPLAY, ret = uclass_get_device_by_driver(UCLASS_DISPLAY,
"sunxi_dw_hdmi", &disp); DM_DRIVER_GET(sunxi_dw_hdmi), &disp);
if (!ret) { if (!ret) {
int mux; int mux;
if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5)) if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5))
@ -281,20 +272,7 @@ static int sunxi_de2_probe(struct udevice *dev)
debug("%s: hdmi display not found (ret=%d)\n", __func__, ret); debug("%s: hdmi display not found (ret=%d)\n", __func__, ret);
ret = uclass_find_device_by_name(UCLASS_DISPLAY, return -ENODEV;
"sunxi_tve", &disp);
if (ret) {
debug("%s: tv not found (ret=%d)\n", __func__, ret);
return ret;
}
ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, 1, true);
if (ret)
return ret;
video_set_flush_dcache(dev, 1);
return 0;
} }
static int sunxi_de2_bind(struct udevice *dev) static int sunxi_de2_bind(struct udevice *dev)
@ -345,8 +323,8 @@ int sunxi_simplefb_setup(void *blob)
mux = 1; mux = 1;
/* Skip simplefb setting if DE2 / HDMI is not present */ /* Skip simplefb setting if DE2 / HDMI is not present */
ret = uclass_find_device_by_name(UCLASS_VIDEO, ret = uclass_get_device_by_driver(UCLASS_VIDEO,
"sunxi_de2", &de2); DM_DRIVER_GET(sunxi_de2), &de2);
if (ret) { if (ret) {
debug("DE2 not present\n"); debug("DE2 not present\n");
return 0; return 0;
@ -355,8 +333,8 @@ int sunxi_simplefb_setup(void *blob)
return 0; return 0;
} }
ret = uclass_find_device_by_name(UCLASS_DISPLAY, ret = uclass_get_device_by_driver(UCLASS_DISPLAY,
"sunxi_dw_hdmi", &hdmi); DM_DRIVER_GET(sunxi_dw_hdmi), &hdmi);
if (ret) { if (ret) {
debug("HDMI not present\n"); debug("HDMI not present\n");
} else if (device_active(hdmi)) { } else if (device_active(hdmi)) {
@ -368,8 +346,8 @@ int sunxi_simplefb_setup(void *blob)
debug("HDMI present but not probed\n"); debug("HDMI present but not probed\n");
} }
ret = uclass_find_device_by_name(UCLASS_DISPLAY, ret = uclass_get_device_by_driver(UCLASS_DISPLAY,
"sunxi_lcd", &lcd); DM_DRIVER_GET(sunxi_lcd), &lcd);
if (ret) if (ret)
debug("LCD not present\n"); debug("LCD not present\n");
else if (device_active(lcd)) else if (device_active(lcd))

View File

@ -20,7 +20,6 @@
struct sunxi_dw_hdmi_priv { struct sunxi_dw_hdmi_priv {
struct dw_hdmi hdmi; struct dw_hdmi hdmi;
int mux;
}; };
struct sunxi_hdmi_phy { struct sunxi_hdmi_phy {
@ -114,28 +113,6 @@ static void sunxi_dw_hdmi_phy_init(void)
writel(0x42494E47, &phy->unscramble); writel(0x42494E47, &phy->unscramble);
} }
static int sunxi_dw_hdmi_get_plug_in_status(void)
{
struct sunxi_hdmi_phy * const phy =
(struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS);
return !!(readl(&phy->status) & (1 << 19));
}
static int sunxi_dw_hdmi_wait_for_hpd(void)
{
ulong start;
start = get_timer(0);
do {
if (sunxi_dw_hdmi_get_plug_in_status())
return 0;
udelay(100);
} while (get_timer(start) < 300);
return -1;
}
static void sunxi_dw_hdmi_phy_set(uint clock, int phy_div) static void sunxi_dw_hdmi_phy_set(uint clock, int phy_div)
{ {
struct sunxi_hdmi_phy * const phy = struct sunxi_hdmi_phy * const phy =
@ -305,11 +282,18 @@ static int sunxi_dw_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size)
return dw_hdmi_read_edid(&priv->hdmi, buf, buf_size); return dw_hdmi_read_edid(&priv->hdmi, buf, buf_size);
} }
static bool sunxi_dw_hdmi_mode_valid(struct udevice *dev,
const struct display_timing *timing)
{
return timing->pixelclock.typ <= 297000000;
}
static int sunxi_dw_hdmi_enable(struct udevice *dev, int panel_bpp, static int sunxi_dw_hdmi_enable(struct udevice *dev, int panel_bpp,
const struct display_timing *edid) const struct display_timing *edid)
{ {
struct sunxi_hdmi_phy * const phy = struct sunxi_hdmi_phy * const phy =
(struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS); (struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS);
struct display_plat *uc_plat = dev_get_uclass_plat(dev);
struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev); struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev);
int ret; int ret;
@ -317,7 +301,7 @@ static int sunxi_dw_hdmi_enable(struct udevice *dev, int panel_bpp,
if (ret) if (ret)
return ret; return ret;
sunxi_dw_hdmi_lcdc_init(priv->mux, edid, panel_bpp); sunxi_dw_hdmi_lcdc_init(uc_plat->source_id, edid, panel_bpp);
if (edid->flags & DISPLAY_FLAGS_VSYNC_LOW) if (edid->flags & DISPLAY_FLAGS_VSYNC_LOW)
setbits_le32(&phy->pol, 0x200); setbits_le32(&phy->pol, 0x200);
@ -340,7 +324,6 @@ static int sunxi_dw_hdmi_enable(struct udevice *dev, int panel_bpp,
static int sunxi_dw_hdmi_probe(struct udevice *dev) static int sunxi_dw_hdmi_probe(struct udevice *dev)
{ {
struct display_plat *uc_plat = dev_get_uclass_plat(dev);
struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev); struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev);
struct sunxi_ccm_reg * const ccm = struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE; (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
@ -364,21 +347,17 @@ static int sunxi_dw_hdmi_probe(struct udevice *dev)
sunxi_dw_hdmi_phy_init(); sunxi_dw_hdmi_phy_init();
ret = sunxi_dw_hdmi_wait_for_hpd();
if (ret < 0) {
debug("hdmi can not get hpd signal\n");
return -1;
}
priv->hdmi.ioaddr = SUNXI_HDMI_BASE; priv->hdmi.ioaddr = SUNXI_HDMI_BASE;
priv->hdmi.i2c_clk_high = 0xd8; priv->hdmi.i2c_clk_high = 0xd8;
priv->hdmi.i2c_clk_low = 0xfe; priv->hdmi.i2c_clk_low = 0xfe;
priv->hdmi.reg_io_width = 1; priv->hdmi.reg_io_width = 1;
priv->hdmi.phy_set = sunxi_dw_hdmi_phy_cfg; priv->hdmi.phy_set = sunxi_dw_hdmi_phy_cfg;
priv->mux = uc_plat->source_id;
uclass_get_device_by_phandle(UCLASS_I2C, dev, "ddc-i2c-bus", ret = dw_hdmi_phy_wait_for_hpd(&priv->hdmi);
&priv->hdmi.ddc_bus); if (ret < 0) {
debug("hdmi can not get hpd signal\n");
return -1;
}
dw_hdmi_init(&priv->hdmi); dw_hdmi_init(&priv->hdmi);
@ -388,6 +367,7 @@ static int sunxi_dw_hdmi_probe(struct udevice *dev)
static const struct dm_display_ops sunxi_dw_hdmi_ops = { static const struct dm_display_ops sunxi_dw_hdmi_ops = {
.read_edid = sunxi_dw_hdmi_read_edid, .read_edid = sunxi_dw_hdmi_read_edid,
.enable = sunxi_dw_hdmi_enable, .enable = sunxi_dw_hdmi_enable,
.mode_valid = sunxi_dw_hdmi_mode_valid,
}; };
U_BOOT_DRIVER(sunxi_dw_hdmi) = { U_BOOT_DRIVER(sunxi_dw_hdmi) = {