diff mbox series

[v2,3/3] video: sunxi_display: Convert to DM_VIDEO

Message ID 20200617202437.301108-4-jagan@amarulasolutions.com
State Changes Requested
Delegated to: Anatolij Gustschin
Headers show
Series video: sunxi: Convert DM_VIDEO | expand

Commit Message

Jagan Teki June 17, 2020, 8:24 p.m. UTC
DM_VIDEO migration deadline is already expired, but around
80 Allwinner boards are still using video in a legacy way.

===================== WARNING ======================
This board does not use CONFIG_DM_VIDEO Please update
the board to use CONFIG_DM_VIDEO before the v2019.07 release.
Failure to update by the deadline may result in board removal.
See doc/driver-model/migration.rst for more info.
====================================================

So let's live these boards on the tree before the video maintainer
removes it by converting in to DM_VIDEO.

Tested in Bananapi M1+ Plus 1920x1200 HDMI out.

Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
Changes for v2:
- add BMP support

 arch/arm/mach-sunxi/Kconfig         |   4 +-
 drivers/video/sunxi/sunxi_display.c | 264 ++++++++++++++++------------
 include/configs/sunxi-common.h      |  17 --
 scripts/config_whitelist.txt        |   1 -
 4 files changed, 157 insertions(+), 129 deletions(-)

Comments

Maxime Ripard June 18, 2020, 7:24 a.m. UTC | #1
On Thu, Jun 18, 2020 at 01:54:37AM +0530, Jagan Teki wrote:
> DM_VIDEO migration deadline is already expired, but around
> 80 Allwinner boards are still using video in a legacy way.
> 
> ===================== WARNING ======================
> This board does not use CONFIG_DM_VIDEO Please update
> the board to use CONFIG_DM_VIDEO before the v2019.07 release.
> Failure to update by the deadline may result in board removal.
> See doc/driver-model/migration.rst for more info.
> ====================================================
> 
> So let's live these boards on the tree before the video maintainer
> removes it by converting in to DM_VIDEO.
> 
> Tested in Bananapi M1+ Plus 1920x1200 HDMI out.
> 
> Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
> ---
> Changes for v2:
> - add BMP support
> 
>  arch/arm/mach-sunxi/Kconfig         |   4 +-
>  drivers/video/sunxi/sunxi_display.c | 264 ++++++++++++++++------------
>  include/configs/sunxi-common.h      |  17 --
>  scripts/config_whitelist.txt        |   1 -
>  4 files changed, 157 insertions(+), 129 deletions(-)
> 
> diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> index be0822bfb7..7d2fb55ff0 100644
> --- a/arch/arm/mach-sunxi/Kconfig
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -758,8 +758,10 @@ config VIDEO_SUNXI
>  	depends on !MACH_SUN9I
>  	depends on !MACH_SUN50I
>  	depends on !MACH_SUN50I_H6
> -	select VIDEO
> +	select DM_VIDEO
> +	select DISPLAY
>  	imply VIDEO_DT_SIMPLEFB
> +	imply CMD_BMP
>  	default y
>  	---help---
>  	Say Y here to add support for using a cfb console on the HDMI, LCD
> diff --git a/drivers/video/sunxi/sunxi_display.c b/drivers/video/sunxi/sunxi_display.c
> index f52aba4d21..fb51b0c5ba 100644
> --- a/drivers/video/sunxi/sunxi_display.c
> +++ b/drivers/video/sunxi/sunxi_display.c
> @@ -7,6 +7,8 @@
>   */
>  
>  #include <common.h>
> +#include <display.h>
> +#include <dm.h>
>  #include <cpu_func.h>
>  #include <efi_loader.h>
>  #include <init.h>
> @@ -28,7 +30,9 @@
>  #include <fdt_support.h>
>  #include <i2c.h>
>  #include <malloc.h>
> +#include <video.h>
>  #include <video_fb.h>
> +#include <dm/uclass-internal.h>
>  #include "../videomodes.h"
>  #include "../anx9804.h"
>  #include "../hitachi_tx18d42vm_lcd.h"
> @@ -45,6 +49,13 @@
>  
>  DECLARE_GLOBAL_DATA_PTR;
>  
> +enum {
> +	/* Maximum LCD size we support */
> +	LCD_MAX_WIDTH		= 3840,
> +	LCD_MAX_HEIGHT		= 2160,
> +	LCD_MAX_LOG2_BPP	= VIDEO_BPP32,
> +};

Why is that an enum? Those three values don't have any relationship with
each other.

>  enum sunxi_monitor {
>  	sunxi_monitor_none,
>  	sunxi_monitor_dvi,
> @@ -59,12 +70,11 @@ enum sunxi_monitor {
>  #define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc
>  
>  struct sunxi_display {
> -	GraphicDevice graphic_device;
>  	enum sunxi_monitor monitor;
>  	unsigned int depth;
>  	unsigned int fb_addr;
>  	unsigned int fb_size;
> -} sunxi_display;
> +};

This looks unrelated?

>  const struct ctfb_res_modes composite_video_modes[2] = {
>  	/*  x     y  hz  pixclk ps/kHz   le   ri  up  lo   hs vs  s  vmode */
> @@ -214,7 +224,8 @@ static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
>  	return r;
>  }
>  
> -static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode,
> +static int sunxi_hdmi_edid_get_mode(struct sunxi_display *sunxi_display,
> +				    struct ctfb_res_modes *mode,
>  				    bool verbose_mode)
>  {
>  	struct edid1_info edid1;
> @@ -291,14 +302,14 @@ static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode,
>  	}
>  
>  	/* Check for basic audio support, if found enable hdmi output */
> -	sunxi_display.monitor = sunxi_monitor_dvi;
> +	sunxi_display->monitor = sunxi_monitor_dvi;
>  	for (i = 0; i < ext_blocks; i++) {
>  		if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
>  		    cea681[i].revision < 2)
>  			continue;
>  
>  		if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
> -			sunxi_display.monitor = sunxi_monitor_hdmi;
> +			sunxi_display->monitor = sunxi_monitor_hdmi;
>  	}
>  
>  	return 0;
> @@ -414,9 +425,9 @@ static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
>  static void sunxi_frontend_enable(void) {}
>  #endif
>  
> -static bool sunxi_is_composite(void)
> +static bool sunxi_is_composite(enum sunxi_monitor monitor)
>  {
> -	switch (sunxi_display.monitor) {
> +	switch (monitor) {
>  	case sunxi_monitor_none:
>  	case sunxi_monitor_dvi:
>  	case sunxi_monitor_hdmi:
> @@ -473,7 +484,8 @@ static const u32 sunxi_rgb2yuv_coef[12] = {
>  };
>  
>  static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
> -				    unsigned int address)
> +				    unsigned int address,
> +				    enum sunxi_monitor monitor)
>  {
>  	struct sunxi_de_be_reg * const de_be =
>  		(struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
> @@ -502,7 +514,7 @@ static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
>  #endif
>  			     SUNXI_DE_BE_MODE_INTERLACE_ENABLE);
>  
> -	if (sunxi_is_composite()) {
> +	if (sunxi_is_composite(monitor)) {
>  		writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE,
>  		       &de_be->output_color_ctrl);
>  		for (i = 0; i < 12; i++)
> @@ -616,7 +628,8 @@ static void sunxi_lcdc_backlight_enable(void)
>  		gpio_direction_output(pin, PWM_ON);
>  }
>  
> -static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
> +static void sunxi_lcdc_tcon0_mode_set(struct sunxi_display *sunxi_display,
> +				      const struct ctfb_res_modes *mode,
>  				      bool for_ext_vga_dac)
>  {
>  	struct sunxi_lcdc_reg * const lcdc =
> @@ -643,17 +656,18 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
>  	}
>  
>  	lcdc_pll_set(ccm, 0, mode->pixclock_khz, &clk_div, &clk_double,
> -		     sunxi_is_composite());
> +		     sunxi_is_composite(sunxi_display->monitor));
>  
>  	video_ctfb_mode_to_display_timing(mode, &timing);
>  	lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
> -			    sunxi_display.depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
> +			    sunxi_display->depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
>  }
>  
>  #if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
>  static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
>  				      int *clk_div, int *clk_double,
> -				      bool use_portd_hvsync)
> +				      bool use_portd_hvsync,
> +				      enum sunxi_monitor monitor)
>  {
>  	struct sunxi_lcdc_reg * const lcdc =
>  		(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
> @@ -663,7 +677,7 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
>  
>  	video_ctfb_mode_to_display_timing(mode, &timing);
>  	lcdc_tcon1_mode_set(lcdc, &timing, use_portd_hvsync,
> -			    sunxi_is_composite());
> +			    sunxi_is_composite(monitor));
>  
>  	if (use_portd_hvsync) {
>  		sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD_LCD0);
> @@ -671,7 +685,7 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
>  	}
>  
>  	lcdc_pll_set(ccm, 1, mode->pixclock_khz, clk_div, clk_double,
> -		     sunxi_is_composite());
> +		     sunxi_is_composite(monitor));
>  }
>  #endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */
>  
> @@ -725,7 +739,8 @@ static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
>  }
>  
>  static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
> -				int clk_div, int clk_double)
> +				int clk_div, int clk_double,
> +				enum sunxi_monitor monitor)
>  {
>  	struct sunxi_hdmi_reg * const hdmi =
>  		(struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
> @@ -734,7 +749,7 @@ 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 (sunxi_display.monitor == sunxi_monitor_hdmi)
> +	if (monitor == sunxi_monitor_hdmi)
>  		sunxi_hdmi_setup_info_frames(mode);
>  
>  	/* Set input sync enable */
> @@ -789,7 +804,7 @@ static void sunxi_hdmi_enable(void)
>  
>  #if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
>  
> -static void sunxi_tvencoder_mode_set(void)
> +static void sunxi_tvencoder_mode_set(enum sunxi_monitor monitor)
>  {
>  	struct sunxi_ccm_reg * const ccm =
>  		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> @@ -801,7 +816,7 @@ static void sunxi_tvencoder_mode_set(void)
>  	/* Clock on */
>  	setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
>  
> -	switch (sunxi_display.monitor) {
> +	switch (monitor) {
>  	case sunxi_monitor_vga:
>  		tvencoder_mode_set(tve, tve_mode_vga);
>  		break;
> @@ -896,26 +911,28 @@ static void sunxi_engines_init(void)
>  	sunxi_drc_init();
>  }
>  
> -static void sunxi_mode_set(const struct ctfb_res_modes *mode,
> +static void sunxi_mode_set(struct sunxi_display *sunxi_display,
> +			   const struct ctfb_res_modes *mode,
>  			   unsigned int address)
>  {
> +	enum sunxi_monitor monitor = sunxi_display->monitor;
>  	int __maybe_unused clk_div, clk_double;
>  	struct sunxi_lcdc_reg * const lcdc =
>  		(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
>  	struct sunxi_tve_reg * __maybe_unused const tve =
>  		(struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
>  
> -	switch (sunxi_display.monitor) {
> +	switch (sunxi_display->monitor) {
>  	case sunxi_monitor_none:
>  		break;
>  	case sunxi_monitor_dvi:
>  	case sunxi_monitor_hdmi:
>  #ifdef CONFIG_VIDEO_HDMI
> -		sunxi_composer_mode_set(mode, address);
> -		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
> -		sunxi_hdmi_mode_set(mode, clk_div, clk_double);
> +		sunxi_composer_mode_set(mode, address, monitor);
> +		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0, monitor);
> +		sunxi_hdmi_mode_set(mode, clk_div, clk_double, monitor);
>  		sunxi_composer_enable();
> -		lcdc_enable(lcdc, sunxi_display.depth);
> +		lcdc_enable(lcdc, sunxi_display->depth);
>  		sunxi_hdmi_enable();
>  #endif
>  		break;
> @@ -930,7 +947,7 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
>  			axp_set_eldo(3, 1800);
>  			anx9804_init(CONFIG_VIDEO_LCD_I2C_BUS, 4,
>  				     ANX9804_DATA_RATE_1620M,
> -				     sunxi_display.depth);
> +				     sunxi_display->depth);
>  		}
>  		if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
>  			mdelay(50); /* Wait for lcd controller power on */
> @@ -942,10 +959,10 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
>  			i2c_reg_write(0x5c, 0x04, 0x42); /* Turn on the LCD */
>  			i2c_set_bus_num(orig_i2c_bus);
>  		}
> -		sunxi_composer_mode_set(mode, address);
> -		sunxi_lcdc_tcon0_mode_set(mode, false);
> +		sunxi_composer_mode_set(mode, address, monitor);
> +		sunxi_lcdc_tcon0_mode_set(sunxi_display, mode, false);
>  		sunxi_composer_enable();
> -		lcdc_enable(lcdc, sunxi_display.depth);
> +		lcdc_enable(lcdc, sunxi_display->depth);
>  #ifdef CONFIG_VIDEO_LCD_SSD2828
>  		sunxi_ssd2828_init(mode);
>  #endif
> @@ -953,17 +970,17 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
>  		break;
>  	case sunxi_monitor_vga:
>  #ifdef CONFIG_VIDEO_VGA
> -		sunxi_composer_mode_set(mode, address);
> -		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
> -		sunxi_tvencoder_mode_set();
> +		sunxi_composer_mode_set(mode, address, monitor);
> +		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1, monitor);
> +		sunxi_tvencoder_mode_set(monitor);
>  		sunxi_composer_enable();
> -		lcdc_enable(lcdc, sunxi_display.depth);
> +		lcdc_enable(lcdc, sunxi_display->depth);
>  		tvencoder_enable(tve);
>  #elif defined CONFIG_VIDEO_VGA_VIA_LCD
> -		sunxi_composer_mode_set(mode, address);
> -		sunxi_lcdc_tcon0_mode_set(mode, true);
> +		sunxi_composer_mode_set(mode, address, monitor);
> +		sunxi_lcdc_tcon0_mode_set(sunxi_display, mode, true);
>  		sunxi_composer_enable();
> -		lcdc_enable(lcdc, sunxi_display.depth);
> +		lcdc_enable(lcdc, sunxi_display->depth);
>  		sunxi_vga_external_dac_enable();
>  #endif
>  		break;
> @@ -972,11 +989,11 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
>  	case sunxi_monitor_composite_pal_m:
>  	case sunxi_monitor_composite_pal_nc:
>  #ifdef CONFIG_VIDEO_COMPOSITE
> -		sunxi_composer_mode_set(mode, address);
> -		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
> -		sunxi_tvencoder_mode_set();
> +		sunxi_composer_mode_set(mode, address, monitor);
> +		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0, monitor);
> +		sunxi_tvencoder_mode_set(monitor);
>  		sunxi_composer_enable();
> -		lcdc_enable(lcdc, sunxi_display.depth);
> +		lcdc_enable(lcdc, sunxi_display->depth);
>  		tvencoder_enable(tve);
>  #endif
>  		break;
> @@ -999,11 +1016,6 @@ static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
>  	}
>  }
>  
> -ulong board_get_usable_ram_top(ulong total_size)
> -{
> -	return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE;
> -}
> -

I couldn't find how you're allocating and reserving the video buffer now
for simplefb to use later on.

>  static bool sunxi_has_hdmi(void)
>  {
>  #ifdef CONFIG_VIDEO_HDMI
> @@ -1052,9 +1064,11 @@ static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
>  		return sunxi_monitor_none;
>  }
>  
> -void *video_hw_init(void)
> +static int sunxi_de_probe(struct udevice *dev)
>  {
> -	static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
> +	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
> +	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
> +	struct sunxi_display *sunxi_display = dev_get_priv(dev);
>  	const struct ctfb_res_modes *mode;
>  	struct ctfb_res_modes custom;
>  	const char *options;
> @@ -1067,10 +1081,8 @@ void *video_hw_init(void)
>  	char mon[16];
>  	char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
>  
> -	memset(&sunxi_display, 0, sizeof(struct sunxi_display));
> -
>  	video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
> -				 &sunxi_display.depth, &options);
> +				 &sunxi_display->depth, &options);
>  #ifdef CONFIG_VIDEO_HDMI
>  	hpd = video_get_option_int(options, "hpd", 1);
>  	hpd_delay = video_get_option_int(options, "hpd_delay", 500);
> @@ -1078,35 +1090,35 @@ void *video_hw_init(void)
>  #endif
>  	overscan_x = video_get_option_int(options, "overscan_x", -1);
>  	overscan_y = video_get_option_int(options, "overscan_y", -1);
> -	sunxi_display.monitor = sunxi_get_default_mon(true);
> +	sunxi_display->monitor = sunxi_get_default_mon(true);
>  	video_get_option_string(options, "monitor", mon, sizeof(mon),
> -				sunxi_get_mon_desc(sunxi_display.monitor));
> +				sunxi_get_mon_desc(sunxi_display->monitor));
>  	for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
>  		if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
> -			sunxi_display.monitor = i;
> +			sunxi_display->monitor = i;
>  			break;
>  		}
>  	}
>  	if (i > SUNXI_MONITOR_LAST)
>  		printf("Unknown monitor: '%s', falling back to '%s'\n",
> -		       mon, sunxi_get_mon_desc(sunxi_display.monitor));
> +		       mon, sunxi_get_mon_desc(sunxi_display->monitor));
>  
>  #ifdef CONFIG_VIDEO_HDMI
>  	/* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
> -	if (sunxi_display.monitor == sunxi_monitor_dvi ||
> -	    sunxi_display.monitor == sunxi_monitor_hdmi) {
> +	if (sunxi_display->monitor == sunxi_monitor_dvi ||
> +	    sunxi_display->monitor == sunxi_monitor_hdmi) {
>  		/* Always call hdp_detect, as it also enables clocks, etc. */
>  		hdmi_present = (sunxi_hdmi_hpd_detect(hpd_delay) == 1);
>  		if (hdmi_present && edid) {
>  			printf("HDMI connected: ");
> -			if (sunxi_hdmi_edid_get_mode(&custom, true) == 0)
> +			if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, true) == 0)
>  				mode = &custom;
>  			else
>  				hdmi_present = false;
>  		}
>  		/* Fall back to EDID in case HPD failed */
>  		if (edid && !hdmi_present) {
> -			if (sunxi_hdmi_edid_get_mode(&custom, false) == 0) {
> +			if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, false) == 0) {
>  				mode = &custom;
>  				hdmi_present = true;
>  			}
> @@ -1114,38 +1126,39 @@ void *video_hw_init(void)
>  		/* Shut down when display was not found */
>  		if ((hpd || edid) && !hdmi_present) {
>  			sunxi_hdmi_shutdown();
> -			sunxi_display.monitor = sunxi_get_default_mon(false);
> +			sunxi_display->monitor = sunxi_get_default_mon(false);
>  		} /* else continue with hdmi/dvi without a cable connected */
>  	}
>  #endif
>  
> -	switch (sunxi_display.monitor) {
> +	switch (sunxi_display->monitor) {
>  	case sunxi_monitor_none:
> -		return NULL;
> +		printf("Unknown monitor\n");
> +		return -EINVAL;
>  	case sunxi_monitor_dvi:
>  	case sunxi_monitor_hdmi:
>  		if (!sunxi_has_hdmi()) {
>  			printf("HDMI/DVI not supported on this board\n");
> -			sunxi_display.monitor = sunxi_monitor_none;
> -			return NULL;
> +			sunxi_display->monitor = sunxi_monitor_none;
> +			return -EINVAL;
>  		}
>  		break;
>  	case sunxi_monitor_lcd:
>  		if (!sunxi_has_lcd()) {
>  			printf("LCD not supported on this board\n");
> -			sunxi_display.monitor = sunxi_monitor_none;
> -			return NULL;
> +			sunxi_display->monitor = sunxi_monitor_none;
> +			return -EINVAL;
>  		}
> -		sunxi_display.depth = video_get_params(&custom, lcd_mode);
> +		sunxi_display->depth = video_get_params(&custom, lcd_mode);
>  		mode = &custom;
>  		break;
>  	case sunxi_monitor_vga:
>  		if (!sunxi_has_vga()) {
>  			printf("VGA not supported on this board\n");
> -			sunxi_display.monitor = sunxi_monitor_none;
> -			return NULL;
> +			sunxi_display->monitor = sunxi_monitor_none;
> +			return -EINVAL;
>  		}
> -		sunxi_display.depth = 18;
> +		sunxi_display->depth = 18;
>  		break;
>  	case sunxi_monitor_composite_pal:
>  	case sunxi_monitor_composite_ntsc:
> @@ -1153,85 +1166,103 @@ void *video_hw_init(void)
>  	case sunxi_monitor_composite_pal_nc:
>  		if (!sunxi_has_composite()) {
>  			printf("Composite video not supported on this board\n");
> -			sunxi_display.monitor = sunxi_monitor_none;
> -			return NULL;
> +			sunxi_display->monitor = sunxi_monitor_none;
> +			return -EINVAL;
>  		}
> -		if (sunxi_display.monitor == sunxi_monitor_composite_pal ||
> -		    sunxi_display.monitor == sunxi_monitor_composite_pal_nc)
> +		if (sunxi_display->monitor == sunxi_monitor_composite_pal ||
> +		    sunxi_display->monitor == sunxi_monitor_composite_pal_nc)
>  			mode = &composite_video_modes[0];
>  		else
>  			mode = &composite_video_modes[1];
> -		sunxi_display.depth = 24;
> +		sunxi_display->depth = 24;
>  		break;
>  	}
>  
>  	/* Yes these defaults are quite high, overscan on composite sucks... */
>  	if (overscan_x == -1)
> -		overscan_x = sunxi_is_composite() ? 32 : 0;
> +		overscan_x = sunxi_is_composite(sunxi_display->monitor) ? 32 : 0;
>  	if (overscan_y == -1)
> -		overscan_y = sunxi_is_composite() ? 20 : 0;
> +		overscan_y = sunxi_is_composite(sunxi_display->monitor) ? 20 : 0;
>  
> -	sunxi_display.fb_size =
> -		(mode->xres * mode->yres * 4 + 0xfff) & ~0xfff;
> +	sunxi_display->fb_size = plat->size;
>  	overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
>  	/* We want to keep the fb_base for simplefb page aligned, where as
>  	 * the sunxi dma engines will happily accept an unaligned address. */
>  	if (overscan_offset)
> -		sunxi_display.fb_size += 0x1000;
> -
> -	if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) {
> -		printf("Error need %dkB for fb, but only %dkB is reserved\n",
> -		       sunxi_display.fb_size >> 10,
> -		       CONFIG_SUNXI_MAX_FB_SIZE >> 10);
> -		return NULL;
> -	}
> +		sunxi_display->fb_size += 0x1000;
>  
>  	printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
>  	       mode->xres, mode->yres,
>  	       (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
> -	       sunxi_get_mon_desc(sunxi_display.monitor),
> +	       sunxi_get_mon_desc(sunxi_display->monitor),
>  	       overscan_x, overscan_y);
>  
> -	gd->fb_base = gd->bd->bi_dram[0].start +
> -		      gd->bd->bi_dram[0].size - sunxi_display.fb_size;
> +	sunxi_display->fb_addr = plat->base;
>  	sunxi_engines_init();
>  
>  #ifdef CONFIG_EFI_LOADER
> -	efi_add_memory_map(gd->fb_base, sunxi_display.fb_size,
> +	efi_add_memory_map(sunxi_display->fb_addr, sunxi_display->fb_size,
>  			   EFI_RESERVED_MEMORY_TYPE);
>  #endif
>  
> -	fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE;
> -	sunxi_display.fb_addr = gd->fb_base;
> +	fb_dma_addr = sunxi_display->fb_addr - CONFIG_SYS_SDRAM_BASE;
>  	if (overscan_offset) {
>  		fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
> -		sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff;
> -		memset((void *)gd->fb_base, 0, sunxi_display.fb_size);
> -		flush_cache(gd->fb_base, sunxi_display.fb_size);
> +		sunxi_display->fb_addr += (overscan_offset + 0xfff) & ~0xfff;
> +		memset((void *)sunxi_display->fb_addr, 0, sunxi_display->fb_size);
> +		flush_cache(sunxi_display->fb_addr, sunxi_display->fb_size);
>  	}
> -	sunxi_mode_set(mode, fb_dma_addr);
> +	sunxi_mode_set(sunxi_display, mode, fb_dma_addr);
>  
>  	/*
>  	 * These are the only members of this structure that are used. All the
>  	 * others are driver specific. The pitch is stored in plnSizeX.
>  	 */
> -	graphic_device->frameAdrs = sunxi_display.fb_addr;
> -	graphic_device->gdfIndex = GDF_32BIT_X888RGB;
> -	graphic_device->gdfBytesPP = 4;
> -	graphic_device->winSizeX = mode->xres - 2 * overscan_x;
> -	graphic_device->winSizeY = mode->yres - 2 * overscan_y;
> -	graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP;
> -
> -	return graphic_device;
> +	uc_priv->bpix = VIDEO_BPP32;
> +	uc_priv->xsize = mode->xres;
> +	uc_priv->ysize = mode->yres;
> +
> +	video_set_flush_dcache(dev, 1);

This is new. You should explain in a separate patch why the flush_cache
above isn't enough.

Maxime
Jagan Teki June 18, 2020, 4:07 p.m. UTC | #2
On Thu, Jun 18, 2020 at 12:54 PM Maxime Ripard <maxime@cerno.tech> wrote:
>
> On Thu, Jun 18, 2020 at 01:54:37AM +0530, Jagan Teki wrote:
> > DM_VIDEO migration deadline is already expired, but around
> > 80 Allwinner boards are still using video in a legacy way.
> >
> > ===================== WARNING ======================
> > This board does not use CONFIG_DM_VIDEO Please update
> > the board to use CONFIG_DM_VIDEO before the v2019.07 release.
> > Failure to update by the deadline may result in board removal.
> > See doc/driver-model/migration.rst for more info.
> > ====================================================
> >
> > So let's live these boards on the tree before the video maintainer
> > removes it by converting in to DM_VIDEO.
> >
> > Tested in Bananapi M1+ Plus 1920x1200 HDMI out.
> >
> > Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
> > ---
> > Changes for v2:
> > - add BMP support
> >
> >  arch/arm/mach-sunxi/Kconfig         |   4 +-
> >  drivers/video/sunxi/sunxi_display.c | 264 ++++++++++++++++------------
> >  include/configs/sunxi-common.h      |  17 --
> >  scripts/config_whitelist.txt        |   1 -
> >  4 files changed, 157 insertions(+), 129 deletions(-)
> >
> > diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> > index be0822bfb7..7d2fb55ff0 100644
> > --- a/arch/arm/mach-sunxi/Kconfig
> > +++ b/arch/arm/mach-sunxi/Kconfig
> > @@ -758,8 +758,10 @@ config VIDEO_SUNXI
> >       depends on !MACH_SUN9I
> >       depends on !MACH_SUN50I
> >       depends on !MACH_SUN50I_H6
> > -     select VIDEO
> > +     select DM_VIDEO
> > +     select DISPLAY
> >       imply VIDEO_DT_SIMPLEFB
> > +     imply CMD_BMP
> >       default y
> >       ---help---
> >       Say Y here to add support for using a cfb console on the HDMI, LCD
> > diff --git a/drivers/video/sunxi/sunxi_display.c b/drivers/video/sunxi/sunxi_display.c
> > index f52aba4d21..fb51b0c5ba 100644
> > --- a/drivers/video/sunxi/sunxi_display.c
> > +++ b/drivers/video/sunxi/sunxi_display.c
> > @@ -7,6 +7,8 @@
> >   */
> >
> >  #include <common.h>
> > +#include <display.h>
> > +#include <dm.h>
> >  #include <cpu_func.h>
> >  #include <efi_loader.h>
> >  #include <init.h>
> > @@ -28,7 +30,9 @@
> >  #include <fdt_support.h>
> >  #include <i2c.h>
> >  #include <malloc.h>
> > +#include <video.h>
> >  #include <video_fb.h>
> > +#include <dm/uclass-internal.h>
> >  #include "../videomodes.h"
> >  #include "../anx9804.h"
> >  #include "../hitachi_tx18d42vm_lcd.h"
> > @@ -45,6 +49,13 @@
> >
> >  DECLARE_GLOBAL_DATA_PTR;
> >
> > +enum {
> > +     /* Maximum LCD size we support */
> > +     LCD_MAX_WIDTH           = 3840,
> > +     LCD_MAX_HEIGHT          = 2160,
> > +     LCD_MAX_LOG2_BPP        = VIDEO_BPP32,
> > +};
>
> Why is that an enum? Those three values don't have any relationship with
> each other.

Not sure I understand your question. These are maximum resolution
followed by maximum bpp supporting. based on these the fbbuffer
allocation happens, please see sunxi_de_bind on this patch.

>
> >  enum sunxi_monitor {
> >       sunxi_monitor_none,
> >       sunxi_monitor_dvi,
> > @@ -59,12 +70,11 @@ enum sunxi_monitor {
> >  #define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc
> >
> >  struct sunxi_display {
> > -     GraphicDevice graphic_device;
> >       enum sunxi_monitor monitor;
> >       unsigned int depth;
> >       unsigned int fb_addr;
> >       unsigned int fb_size;
> > -} sunxi_display;
> > +};
>
> This looks unrelated?
>
> >  const struct ctfb_res_modes composite_video_modes[2] = {
> >       /*  x     y  hz  pixclk ps/kHz   le   ri  up  lo   hs vs  s  vmode */
> > @@ -214,7 +224,8 @@ static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
> >       return r;
> >  }
> >
> > -static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode,
> > +static int sunxi_hdmi_edid_get_mode(struct sunxi_display *sunxi_display,
> > +                                 struct ctfb_res_modes *mode,
> >                                   bool verbose_mode)
> >  {
> >       struct edid1_info edid1;
> > @@ -291,14 +302,14 @@ static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode,
> >       }
> >
> >       /* Check for basic audio support, if found enable hdmi output */
> > -     sunxi_display.monitor = sunxi_monitor_dvi;
> > +     sunxi_display->monitor = sunxi_monitor_dvi;
> >       for (i = 0; i < ext_blocks; i++) {
> >               if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
> >                   cea681[i].revision < 2)
> >                       continue;
> >
> >               if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
> > -                     sunxi_display.monitor = sunxi_monitor_hdmi;
> > +                     sunxi_display->monitor = sunxi_monitor_hdmi;
> >       }
> >
> >       return 0;
> > @@ -414,9 +425,9 @@ static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
> >  static void sunxi_frontend_enable(void) {}
> >  #endif
> >
> > -static bool sunxi_is_composite(void)
> > +static bool sunxi_is_composite(enum sunxi_monitor monitor)
> >  {
> > -     switch (sunxi_display.monitor) {
> > +     switch (monitor) {
> >       case sunxi_monitor_none:
> >       case sunxi_monitor_dvi:
> >       case sunxi_monitor_hdmi:
> > @@ -473,7 +484,8 @@ static const u32 sunxi_rgb2yuv_coef[12] = {
> >  };
> >
> >  static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
> > -                                 unsigned int address)
> > +                                 unsigned int address,
> > +                                 enum sunxi_monitor monitor)
> >  {
> >       struct sunxi_de_be_reg * const de_be =
> >               (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
> > @@ -502,7 +514,7 @@ static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
> >  #endif
> >                            SUNXI_DE_BE_MODE_INTERLACE_ENABLE);
> >
> > -     if (sunxi_is_composite()) {
> > +     if (sunxi_is_composite(monitor)) {
> >               writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE,
> >                      &de_be->output_color_ctrl);
> >               for (i = 0; i < 12; i++)
> > @@ -616,7 +628,8 @@ static void sunxi_lcdc_backlight_enable(void)
> >               gpio_direction_output(pin, PWM_ON);
> >  }
> >
> > -static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
> > +static void sunxi_lcdc_tcon0_mode_set(struct sunxi_display *sunxi_display,
> > +                                   const struct ctfb_res_modes *mode,
> >                                     bool for_ext_vga_dac)
> >  {
> >       struct sunxi_lcdc_reg * const lcdc =
> > @@ -643,17 +656,18 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
> >       }
> >
> >       lcdc_pll_set(ccm, 0, mode->pixclock_khz, &clk_div, &clk_double,
> > -                  sunxi_is_composite());
> > +                  sunxi_is_composite(sunxi_display->monitor));
> >
> >       video_ctfb_mode_to_display_timing(mode, &timing);
> >       lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
> > -                         sunxi_display.depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
> > +                         sunxi_display->depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
> >  }
> >
> >  #if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
> >  static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
> >                                     int *clk_div, int *clk_double,
> > -                                   bool use_portd_hvsync)
> > +                                   bool use_portd_hvsync,
> > +                                   enum sunxi_monitor monitor)
> >  {
> >       struct sunxi_lcdc_reg * const lcdc =
> >               (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
> > @@ -663,7 +677,7 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
> >
> >       video_ctfb_mode_to_display_timing(mode, &timing);
> >       lcdc_tcon1_mode_set(lcdc, &timing, use_portd_hvsync,
> > -                         sunxi_is_composite());
> > +                         sunxi_is_composite(monitor));
> >
> >       if (use_portd_hvsync) {
> >               sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD_LCD0);
> > @@ -671,7 +685,7 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
> >       }
> >
> >       lcdc_pll_set(ccm, 1, mode->pixclock_khz, clk_div, clk_double,
> > -                  sunxi_is_composite());
> > +                  sunxi_is_composite(monitor));
> >  }
> >  #endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */
> >
> > @@ -725,7 +739,8 @@ static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
> >  }
> >
> >  static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
> > -                             int clk_div, int clk_double)
> > +                             int clk_div, int clk_double,
> > +                             enum sunxi_monitor monitor)
> >  {
> >       struct sunxi_hdmi_reg * const hdmi =
> >               (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
> > @@ -734,7 +749,7 @@ 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 (sunxi_display.monitor == sunxi_monitor_hdmi)
> > +     if (monitor == sunxi_monitor_hdmi)
> >               sunxi_hdmi_setup_info_frames(mode);
> >
> >       /* Set input sync enable */
> > @@ -789,7 +804,7 @@ static void sunxi_hdmi_enable(void)
> >
> >  #if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
> >
> > -static void sunxi_tvencoder_mode_set(void)
> > +static void sunxi_tvencoder_mode_set(enum sunxi_monitor monitor)
> >  {
> >       struct sunxi_ccm_reg * const ccm =
> >               (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> > @@ -801,7 +816,7 @@ static void sunxi_tvencoder_mode_set(void)
> >       /* Clock on */
> >       setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
> >
> > -     switch (sunxi_display.monitor) {
> > +     switch (monitor) {
> >       case sunxi_monitor_vga:
> >               tvencoder_mode_set(tve, tve_mode_vga);
> >               break;
> > @@ -896,26 +911,28 @@ static void sunxi_engines_init(void)
> >       sunxi_drc_init();
> >  }
> >
> > -static void sunxi_mode_set(const struct ctfb_res_modes *mode,
> > +static void sunxi_mode_set(struct sunxi_display *sunxi_display,
> > +                        const struct ctfb_res_modes *mode,
> >                          unsigned int address)
> >  {
> > +     enum sunxi_monitor monitor = sunxi_display->monitor;
> >       int __maybe_unused clk_div, clk_double;
> >       struct sunxi_lcdc_reg * const lcdc =
> >               (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
> >       struct sunxi_tve_reg * __maybe_unused const tve =
> >               (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
> >
> > -     switch (sunxi_display.monitor) {
> > +     switch (sunxi_display->monitor) {
> >       case sunxi_monitor_none:
> >               break;
> >       case sunxi_monitor_dvi:
> >       case sunxi_monitor_hdmi:
> >  #ifdef CONFIG_VIDEO_HDMI
> > -             sunxi_composer_mode_set(mode, address);
> > -             sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
> > -             sunxi_hdmi_mode_set(mode, clk_div, clk_double);
> > +             sunxi_composer_mode_set(mode, address, monitor);
> > +             sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0, monitor);
> > +             sunxi_hdmi_mode_set(mode, clk_div, clk_double, monitor);
> >               sunxi_composer_enable();
> > -             lcdc_enable(lcdc, sunxi_display.depth);
> > +             lcdc_enable(lcdc, sunxi_display->depth);
> >               sunxi_hdmi_enable();
> >  #endif
> >               break;
> > @@ -930,7 +947,7 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
> >                       axp_set_eldo(3, 1800);
> >                       anx9804_init(CONFIG_VIDEO_LCD_I2C_BUS, 4,
> >                                    ANX9804_DATA_RATE_1620M,
> > -                                  sunxi_display.depth);
> > +                                  sunxi_display->depth);
> >               }
> >               if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
> >                       mdelay(50); /* Wait for lcd controller power on */
> > @@ -942,10 +959,10 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
> >                       i2c_reg_write(0x5c, 0x04, 0x42); /* Turn on the LCD */
> >                       i2c_set_bus_num(orig_i2c_bus);
> >               }
> > -             sunxi_composer_mode_set(mode, address);
> > -             sunxi_lcdc_tcon0_mode_set(mode, false);
> > +             sunxi_composer_mode_set(mode, address, monitor);
> > +             sunxi_lcdc_tcon0_mode_set(sunxi_display, mode, false);
> >               sunxi_composer_enable();
> > -             lcdc_enable(lcdc, sunxi_display.depth);
> > +             lcdc_enable(lcdc, sunxi_display->depth);
> >  #ifdef CONFIG_VIDEO_LCD_SSD2828
> >               sunxi_ssd2828_init(mode);
> >  #endif
> > @@ -953,17 +970,17 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
> >               break;
> >       case sunxi_monitor_vga:
> >  #ifdef CONFIG_VIDEO_VGA
> > -             sunxi_composer_mode_set(mode, address);
> > -             sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
> > -             sunxi_tvencoder_mode_set();
> > +             sunxi_composer_mode_set(mode, address, monitor);
> > +             sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1, monitor);
> > +             sunxi_tvencoder_mode_set(monitor);
> >               sunxi_composer_enable();
> > -             lcdc_enable(lcdc, sunxi_display.depth);
> > +             lcdc_enable(lcdc, sunxi_display->depth);
> >               tvencoder_enable(tve);
> >  #elif defined CONFIG_VIDEO_VGA_VIA_LCD
> > -             sunxi_composer_mode_set(mode, address);
> > -             sunxi_lcdc_tcon0_mode_set(mode, true);
> > +             sunxi_composer_mode_set(mode, address, monitor);
> > +             sunxi_lcdc_tcon0_mode_set(sunxi_display, mode, true);
> >               sunxi_composer_enable();
> > -             lcdc_enable(lcdc, sunxi_display.depth);
> > +             lcdc_enable(lcdc, sunxi_display->depth);
> >               sunxi_vga_external_dac_enable();
> >  #endif
> >               break;
> > @@ -972,11 +989,11 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
> >       case sunxi_monitor_composite_pal_m:
> >       case sunxi_monitor_composite_pal_nc:
> >  #ifdef CONFIG_VIDEO_COMPOSITE
> > -             sunxi_composer_mode_set(mode, address);
> > -             sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
> > -             sunxi_tvencoder_mode_set();
> > +             sunxi_composer_mode_set(mode, address, monitor);
> > +             sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0, monitor);
> > +             sunxi_tvencoder_mode_set(monitor);
> >               sunxi_composer_enable();
> > -             lcdc_enable(lcdc, sunxi_display.depth);
> > +             lcdc_enable(lcdc, sunxi_display->depth);
> >               tvencoder_enable(tve);
> >  #endif
> >               break;
> > @@ -999,11 +1016,6 @@ static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
> >       }
> >  }
> >
> > -ulong board_get_usable_ram_top(ulong total_size)
> > -{
> > -     return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE;
> > -}
> > -
>
> I couldn't find how you're allocating and reserving the video buffer now
> for simplefb to use later on.

Based on maximum supporting resolutions and bpp as explained above and
similar like what sunxi_de2 and other platforms like rockchip. via
plat->size.

>
> >  static bool sunxi_has_hdmi(void)
> >  {
> >  #ifdef CONFIG_VIDEO_HDMI
> > @@ -1052,9 +1064,11 @@ static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
> >               return sunxi_monitor_none;
> >  }
> >
> > -void *video_hw_init(void)
> > +static int sunxi_de_probe(struct udevice *dev)
> >  {
> > -     static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
> > +     struct video_priv *uc_priv = dev_get_uclass_priv(dev);
> > +     struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
> > +     struct sunxi_display *sunxi_display = dev_get_priv(dev);
> >       const struct ctfb_res_modes *mode;
> >       struct ctfb_res_modes custom;
> >       const char *options;
> > @@ -1067,10 +1081,8 @@ void *video_hw_init(void)
> >       char mon[16];
> >       char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
> >
> > -     memset(&sunxi_display, 0, sizeof(struct sunxi_display));
> > -
> >       video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
> > -                              &sunxi_display.depth, &options);
> > +                              &sunxi_display->depth, &options);
> >  #ifdef CONFIG_VIDEO_HDMI
> >       hpd = video_get_option_int(options, "hpd", 1);
> >       hpd_delay = video_get_option_int(options, "hpd_delay", 500);
> > @@ -1078,35 +1090,35 @@ void *video_hw_init(void)
> >  #endif
> >       overscan_x = video_get_option_int(options, "overscan_x", -1);
> >       overscan_y = video_get_option_int(options, "overscan_y", -1);
> > -     sunxi_display.monitor = sunxi_get_default_mon(true);
> > +     sunxi_display->monitor = sunxi_get_default_mon(true);
> >       video_get_option_string(options, "monitor", mon, sizeof(mon),
> > -                             sunxi_get_mon_desc(sunxi_display.monitor));
> > +                             sunxi_get_mon_desc(sunxi_display->monitor));
> >       for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
> >               if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
> > -                     sunxi_display.monitor = i;
> > +                     sunxi_display->monitor = i;
> >                       break;
> >               }
> >       }
> >       if (i > SUNXI_MONITOR_LAST)
> >               printf("Unknown monitor: '%s', falling back to '%s'\n",
> > -                    mon, sunxi_get_mon_desc(sunxi_display.monitor));
> > +                    mon, sunxi_get_mon_desc(sunxi_display->monitor));
> >
> >  #ifdef CONFIG_VIDEO_HDMI
> >       /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
> > -     if (sunxi_display.monitor == sunxi_monitor_dvi ||
> > -         sunxi_display.monitor == sunxi_monitor_hdmi) {
> > +     if (sunxi_display->monitor == sunxi_monitor_dvi ||
> > +         sunxi_display->monitor == sunxi_monitor_hdmi) {
> >               /* Always call hdp_detect, as it also enables clocks, etc. */
> >               hdmi_present = (sunxi_hdmi_hpd_detect(hpd_delay) == 1);
> >               if (hdmi_present && edid) {
> >                       printf("HDMI connected: ");
> > -                     if (sunxi_hdmi_edid_get_mode(&custom, true) == 0)
> > +                     if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, true) == 0)
> >                               mode = &custom;
> >                       else
> >                               hdmi_present = false;
> >               }
> >               /* Fall back to EDID in case HPD failed */
> >               if (edid && !hdmi_present) {
> > -                     if (sunxi_hdmi_edid_get_mode(&custom, false) == 0) {
> > +                     if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, false) == 0) {
> >                               mode = &custom;
> >                               hdmi_present = true;
> >                       }
> > @@ -1114,38 +1126,39 @@ void *video_hw_init(void)
> >               /* Shut down when display was not found */
> >               if ((hpd || edid) && !hdmi_present) {
> >                       sunxi_hdmi_shutdown();
> > -                     sunxi_display.monitor = sunxi_get_default_mon(false);
> > +                     sunxi_display->monitor = sunxi_get_default_mon(false);
> >               } /* else continue with hdmi/dvi without a cable connected */
> >       }
> >  #endif
> >
> > -     switch (sunxi_display.monitor) {
> > +     switch (sunxi_display->monitor) {
> >       case sunxi_monitor_none:
> > -             return NULL;
> > +             printf("Unknown monitor\n");
> > +             return -EINVAL;
> >       case sunxi_monitor_dvi:
> >       case sunxi_monitor_hdmi:
> >               if (!sunxi_has_hdmi()) {
> >                       printf("HDMI/DVI not supported on this board\n");
> > -                     sunxi_display.monitor = sunxi_monitor_none;
> > -                     return NULL;
> > +                     sunxi_display->monitor = sunxi_monitor_none;
> > +                     return -EINVAL;
> >               }
> >               break;
> >       case sunxi_monitor_lcd:
> >               if (!sunxi_has_lcd()) {
> >                       printf("LCD not supported on this board\n");
> > -                     sunxi_display.monitor = sunxi_monitor_none;
> > -                     return NULL;
> > +                     sunxi_display->monitor = sunxi_monitor_none;
> > +                     return -EINVAL;
> >               }
> > -             sunxi_display.depth = video_get_params(&custom, lcd_mode);
> > +             sunxi_display->depth = video_get_params(&custom, lcd_mode);
> >               mode = &custom;
> >               break;
> >       case sunxi_monitor_vga:
> >               if (!sunxi_has_vga()) {
> >                       printf("VGA not supported on this board\n");
> > -                     sunxi_display.monitor = sunxi_monitor_none;
> > -                     return NULL;
> > +                     sunxi_display->monitor = sunxi_monitor_none;
> > +                     return -EINVAL;
> >               }
> > -             sunxi_display.depth = 18;
> > +             sunxi_display->depth = 18;
> >               break;
> >       case sunxi_monitor_composite_pal:
> >       case sunxi_monitor_composite_ntsc:
> > @@ -1153,85 +1166,103 @@ void *video_hw_init(void)
> >       case sunxi_monitor_composite_pal_nc:
> >               if (!sunxi_has_composite()) {
> >                       printf("Composite video not supported on this board\n");
> > -                     sunxi_display.monitor = sunxi_monitor_none;
> > -                     return NULL;
> > +                     sunxi_display->monitor = sunxi_monitor_none;
> > +                     return -EINVAL;
> >               }
> > -             if (sunxi_display.monitor == sunxi_monitor_composite_pal ||
> > -                 sunxi_display.monitor == sunxi_monitor_composite_pal_nc)
> > +             if (sunxi_display->monitor == sunxi_monitor_composite_pal ||
> > +                 sunxi_display->monitor == sunxi_monitor_composite_pal_nc)
> >                       mode = &composite_video_modes[0];
> >               else
> >                       mode = &composite_video_modes[1];
> > -             sunxi_display.depth = 24;
> > +             sunxi_display->depth = 24;
> >               break;
> >       }
> >
> >       /* Yes these defaults are quite high, overscan on composite sucks... */
> >       if (overscan_x == -1)
> > -             overscan_x = sunxi_is_composite() ? 32 : 0;
> > +             overscan_x = sunxi_is_composite(sunxi_display->monitor) ? 32 : 0;
> >       if (overscan_y == -1)
> > -             overscan_y = sunxi_is_composite() ? 20 : 0;
> > +             overscan_y = sunxi_is_composite(sunxi_display->monitor) ? 20 : 0;
> >
> > -     sunxi_display.fb_size =
> > -             (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff;
> > +     sunxi_display->fb_size = plat->size;
> >       overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
> >       /* We want to keep the fb_base for simplefb page aligned, where as
> >        * the sunxi dma engines will happily accept an unaligned address. */
> >       if (overscan_offset)
> > -             sunxi_display.fb_size += 0x1000;
> > -
> > -     if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) {
> > -             printf("Error need %dkB for fb, but only %dkB is reserved\n",
> > -                    sunxi_display.fb_size >> 10,
> > -                    CONFIG_SUNXI_MAX_FB_SIZE >> 10);
> > -             return NULL;
> > -     }
> > +             sunxi_display->fb_size += 0x1000;
> >
> >       printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
> >              mode->xres, mode->yres,
> >              (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
> > -            sunxi_get_mon_desc(sunxi_display.monitor),
> > +            sunxi_get_mon_desc(sunxi_display->monitor),
> >              overscan_x, overscan_y);
> >
> > -     gd->fb_base = gd->bd->bi_dram[0].start +
> > -                   gd->bd->bi_dram[0].size - sunxi_display.fb_size;
> > +     sunxi_display->fb_addr = plat->base;
> >       sunxi_engines_init();
> >
> >  #ifdef CONFIG_EFI_LOADER
> > -     efi_add_memory_map(gd->fb_base, sunxi_display.fb_size,
> > +     efi_add_memory_map(sunxi_display->fb_addr, sunxi_display->fb_size,
> >                          EFI_RESERVED_MEMORY_TYPE);
> >  #endif
> >
> > -     fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE;
> > -     sunxi_display.fb_addr = gd->fb_base;
> > +     fb_dma_addr = sunxi_display->fb_addr - CONFIG_SYS_SDRAM_BASE;
> >       if (overscan_offset) {
> >               fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
> > -             sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff;
> > -             memset((void *)gd->fb_base, 0, sunxi_display.fb_size);
> > -             flush_cache(gd->fb_base, sunxi_display.fb_size);
> > +             sunxi_display->fb_addr += (overscan_offset + 0xfff) & ~0xfff;
> > +             memset((void *)sunxi_display->fb_addr, 0, sunxi_display->fb_size);
> > +             flush_cache(sunxi_display->fb_addr, sunxi_display->fb_size);
> >       }
> > -     sunxi_mode_set(mode, fb_dma_addr);
> > +     sunxi_mode_set(sunxi_display, mode, fb_dma_addr);
> >
> >       /*
> >        * These are the only members of this structure that are used. All the
> >        * others are driver specific. The pitch is stored in plnSizeX.
> >        */
> > -     graphic_device->frameAdrs = sunxi_display.fb_addr;
> > -     graphic_device->gdfIndex = GDF_32BIT_X888RGB;
> > -     graphic_device->gdfBytesPP = 4;
> > -     graphic_device->winSizeX = mode->xres - 2 * overscan_x;
> > -     graphic_device->winSizeY = mode->yres - 2 * overscan_y;
> > -     graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP;
> > -
> > -     return graphic_device;
> > +     uc_priv->bpix = VIDEO_BPP32;
> > +     uc_priv->xsize = mode->xres;
> > +     uc_priv->ysize = mode->yres;
> > +
> > +     video_set_flush_dcache(dev, 1);
>
> This is new. You should explain in a separate patch why the flush_cache
> above isn't enough.

This is new and but required when for DM_VIDEO conversion.
video-uclass has a common way to flush video via video_sync so the
driver has to trigger this flush and without this, the vidconsole
shows inconsistent characters.

Jagan.
Michael Nazzareno Trimarchi June 18, 2020, 5:13 p.m. UTC | #3
Hi

On Thu, Jun 18, 2020 at 6:07 PM Jagan Teki <jagan@amarulasolutions.com> wrote:
>
> On Thu, Jun 18, 2020 at 12:54 PM Maxime Ripard <maxime@cerno.tech> wrote:
> >
> > On Thu, Jun 18, 2020 at 01:54:37AM +0530, Jagan Teki wrote:
> > > DM_VIDEO migration deadline is already expired, but around
> > > 80 Allwinner boards are still using video in a legacy way.
> > >
> > > ===================== WARNING ======================
> > > This board does not use CONFIG_DM_VIDEO Please update
> > > the board to use CONFIG_DM_VIDEO before the v2019.07 release.
> > > Failure to update by the deadline may result in board removal.
> > > See doc/driver-model/migration.rst for more info.
> > > ====================================================
> > >
> > > So let's live these boards on the tree before the video maintainer
> > > removes it by converting in to DM_VIDEO.
> > >
> > > Tested in Bananapi M1+ Plus 1920x1200 HDMI out.
> > >
> > > Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
> > > ---
> > > Changes for v2:
> > > - add BMP support
> > >
> > >  arch/arm/mach-sunxi/Kconfig         |   4 +-
> > >  drivers/video/sunxi/sunxi_display.c | 264 ++++++++++++++++------------
> > >  include/configs/sunxi-common.h      |  17 --
> > >  scripts/config_whitelist.txt        |   1 -
> > >  4 files changed, 157 insertions(+), 129 deletions(-)
> > >
> > > diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> > > index be0822bfb7..7d2fb55ff0 100644
> > > --- a/arch/arm/mach-sunxi/Kconfig
> > > +++ b/arch/arm/mach-sunxi/Kconfig
> > > @@ -758,8 +758,10 @@ config VIDEO_SUNXI
> > >       depends on !MACH_SUN9I
> > >       depends on !MACH_SUN50I
> > >       depends on !MACH_SUN50I_H6
> > > -     select VIDEO
> > > +     select DM_VIDEO
> > > +     select DISPLAY
> > >       imply VIDEO_DT_SIMPLEFB
> > > +     imply CMD_BMP
> > >       default y
> > >       ---help---
> > >       Say Y here to add support for using a cfb console on the HDMI, LCD
> > > diff --git a/drivers/video/sunxi/sunxi_display.c b/drivers/video/sunxi/sunxi_display.c
> > > index f52aba4d21..fb51b0c5ba 100644
> > > --- a/drivers/video/sunxi/sunxi_display.c
> > > +++ b/drivers/video/sunxi/sunxi_display.c
> > > @@ -7,6 +7,8 @@
> > >   */
> > >
> > >  #include <common.h>
> > > +#include <display.h>
> > > +#include <dm.h>
> > >  #include <cpu_func.h>
> > >  #include <efi_loader.h>
> > >  #include <init.h>
> > > @@ -28,7 +30,9 @@
> > >  #include <fdt_support.h>
> > >  #include <i2c.h>
> > >  #include <malloc.h>
> > > +#include <video.h>
> > >  #include <video_fb.h>
> > > +#include <dm/uclass-internal.h>
> > >  #include "../videomodes.h"
> > >  #include "../anx9804.h"
> > >  #include "../hitachi_tx18d42vm_lcd.h"
> > > @@ -45,6 +49,13 @@
> > >
> > >  DECLARE_GLOBAL_DATA_PTR;
> > >
> > > +enum {
> > > +     /* Maximum LCD size we support */
> > > +     LCD_MAX_WIDTH           = 3840,
> > > +     LCD_MAX_HEIGHT          = 2160,
> > > +     LCD_MAX_LOG2_BPP        = VIDEO_BPP32,
> > > +};
> >
> > Why is that an enum? Those three values don't have any relationship with
> > each other.
>
> Not sure I understand your question. These are maximum resolution
> followed by maximum bpp supporting. based on these the fbbuffer
> allocation happens, please see sunxi_de_bind on this patch.
>
> >
> > >  enum sunxi_monitor {
> > >       sunxi_monitor_none,
> > >       sunxi_monitor_dvi,
> > > @@ -59,12 +70,11 @@ enum sunxi_monitor {
> > >  #define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc
> > >
> > >  struct sunxi_display {
> > > -     GraphicDevice graphic_device;
> > >       enum sunxi_monitor monitor;
> > >       unsigned int depth;
> > >       unsigned int fb_addr;
> > >       unsigned int fb_size;
> > > -} sunxi_display;
> > > +};
> >
> > This looks unrelated?
> >
> > >  const struct ctfb_res_modes composite_video_modes[2] = {
> > >       /*  x     y  hz  pixclk ps/kHz   le   ri  up  lo   hs vs  s  vmode */
> > > @@ -214,7 +224,8 @@ static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
> > >       return r;
> > >  }
> > >
> > > -static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode,
> > > +static int sunxi_hdmi_edid_get_mode(struct sunxi_display *sunxi_display,
> > > +                                 struct ctfb_res_modes *mode,
> > >                                   bool verbose_mode)
> > >  {
> > >       struct edid1_info edid1;
> > > @@ -291,14 +302,14 @@ static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode,
> > >       }
> > >
> > >       /* Check for basic audio support, if found enable hdmi output */
> > > -     sunxi_display.monitor = sunxi_monitor_dvi;
> > > +     sunxi_display->monitor = sunxi_monitor_dvi;
> > >       for (i = 0; i < ext_blocks; i++) {
> > >               if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
> > >                   cea681[i].revision < 2)
> > >                       continue;
> > >
> > >               if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
> > > -                     sunxi_display.monitor = sunxi_monitor_hdmi;
> > > +                     sunxi_display->monitor = sunxi_monitor_hdmi;
> > >       }
> > >
> > >       return 0;
> > > @@ -414,9 +425,9 @@ static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
> > >  static void sunxi_frontend_enable(void) {}
> > >  #endif
> > >
> > > -static bool sunxi_is_composite(void)
> > > +static bool sunxi_is_composite(enum sunxi_monitor monitor)
> > >  {
> > > -     switch (sunxi_display.monitor) {
> > > +     switch (monitor) {
> > >       case sunxi_monitor_none:
> > >       case sunxi_monitor_dvi:
> > >       case sunxi_monitor_hdmi:
> > > @@ -473,7 +484,8 @@ static const u32 sunxi_rgb2yuv_coef[12] = {
> > >  };
> > >
> > >  static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
> > > -                                 unsigned int address)
> > > +                                 unsigned int address,
> > > +                                 enum sunxi_monitor monitor)
> > >  {
> > >       struct sunxi_de_be_reg * const de_be =
> > >               (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
> > > @@ -502,7 +514,7 @@ static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
> > >  #endif
> > >                            SUNXI_DE_BE_MODE_INTERLACE_ENABLE);
> > >
> > > -     if (sunxi_is_composite()) {
> > > +     if (sunxi_is_composite(monitor)) {
> > >               writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE,
> > >                      &de_be->output_color_ctrl);
> > >               for (i = 0; i < 12; i++)
> > > @@ -616,7 +628,8 @@ static void sunxi_lcdc_backlight_enable(void)
> > >               gpio_direction_output(pin, PWM_ON);
> > >  }
> > >
> > > -static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
> > > +static void sunxi_lcdc_tcon0_mode_set(struct sunxi_display *sunxi_display,
> > > +                                   const struct ctfb_res_modes *mode,
> > >                                     bool for_ext_vga_dac)
> > >  {
> > >       struct sunxi_lcdc_reg * const lcdc =
> > > @@ -643,17 +656,18 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
> > >       }
> > >
> > >       lcdc_pll_set(ccm, 0, mode->pixclock_khz, &clk_div, &clk_double,
> > > -                  sunxi_is_composite());
> > > +                  sunxi_is_composite(sunxi_display->monitor));
> > >
> > >       video_ctfb_mode_to_display_timing(mode, &timing);
> > >       lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
> > > -                         sunxi_display.depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
> > > +                         sunxi_display->depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
> > >  }
> > >
> > >  #if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
> > >  static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
> > >                                     int *clk_div, int *clk_double,
> > > -                                   bool use_portd_hvsync)
> > > +                                   bool use_portd_hvsync,
> > > +                                   enum sunxi_monitor monitor)
> > >  {
> > >       struct sunxi_lcdc_reg * const lcdc =
> > >               (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
> > > @@ -663,7 +677,7 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
> > >
> > >       video_ctfb_mode_to_display_timing(mode, &timing);
> > >       lcdc_tcon1_mode_set(lcdc, &timing, use_portd_hvsync,
> > > -                         sunxi_is_composite());
> > > +                         sunxi_is_composite(monitor));
> > >
> > >       if (use_portd_hvsync) {
> > >               sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD_LCD0);
> > > @@ -671,7 +685,7 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
> > >       }
> > >
> > >       lcdc_pll_set(ccm, 1, mode->pixclock_khz, clk_div, clk_double,
> > > -                  sunxi_is_composite());
> > > +                  sunxi_is_composite(monitor));
> > >  }
> > >  #endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */
> > >
> > > @@ -725,7 +739,8 @@ static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
> > >  }
> > >
> > >  static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
> > > -                             int clk_div, int clk_double)
> > > +                             int clk_div, int clk_double,
> > > +                             enum sunxi_monitor monitor)
> > >  {
> > >       struct sunxi_hdmi_reg * const hdmi =
> > >               (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
> > > @@ -734,7 +749,7 @@ 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 (sunxi_display.monitor == sunxi_monitor_hdmi)
> > > +     if (monitor == sunxi_monitor_hdmi)
> > >               sunxi_hdmi_setup_info_frames(mode);
> > >
> > >       /* Set input sync enable */
> > > @@ -789,7 +804,7 @@ static void sunxi_hdmi_enable(void)
> > >
> > >  #if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
> > >
> > > -static void sunxi_tvencoder_mode_set(void)
> > > +static void sunxi_tvencoder_mode_set(enum sunxi_monitor monitor)
> > >  {
> > >       struct sunxi_ccm_reg * const ccm =
> > >               (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> > > @@ -801,7 +816,7 @@ static void sunxi_tvencoder_mode_set(void)
> > >       /* Clock on */
> > >       setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
> > >
> > > -     switch (sunxi_display.monitor) {
> > > +     switch (monitor) {
> > >       case sunxi_monitor_vga:
> > >               tvencoder_mode_set(tve, tve_mode_vga);
> > >               break;
> > > @@ -896,26 +911,28 @@ static void sunxi_engines_init(void)
> > >       sunxi_drc_init();
> > >  }
> > >
> > > -static void sunxi_mode_set(const struct ctfb_res_modes *mode,
> > > +static void sunxi_mode_set(struct sunxi_display *sunxi_display,
> > > +                        const struct ctfb_res_modes *mode,
> > >                          unsigned int address)
> > >  {
> > > +     enum sunxi_monitor monitor = sunxi_display->monitor;
> > >       int __maybe_unused clk_div, clk_double;
> > >       struct sunxi_lcdc_reg * const lcdc =
> > >               (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
> > >       struct sunxi_tve_reg * __maybe_unused const tve =
> > >               (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
> > >
> > > -     switch (sunxi_display.monitor) {
> > > +     switch (sunxi_display->monitor) {
> > >       case sunxi_monitor_none:
> > >               break;
> > >       case sunxi_monitor_dvi:
> > >       case sunxi_monitor_hdmi:
> > >  #ifdef CONFIG_VIDEO_HDMI
> > > -             sunxi_composer_mode_set(mode, address);
> > > -             sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
> > > -             sunxi_hdmi_mode_set(mode, clk_div, clk_double);
> > > +             sunxi_composer_mode_set(mode, address, monitor);
> > > +             sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0, monitor);
> > > +             sunxi_hdmi_mode_set(mode, clk_div, clk_double, monitor);
> > >               sunxi_composer_enable();
> > > -             lcdc_enable(lcdc, sunxi_display.depth);
> > > +             lcdc_enable(lcdc, sunxi_display->depth);
> > >               sunxi_hdmi_enable();
> > >  #endif
> > >               break;
> > > @@ -930,7 +947,7 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
> > >                       axp_set_eldo(3, 1800);
> > >                       anx9804_init(CONFIG_VIDEO_LCD_I2C_BUS, 4,
> > >                                    ANX9804_DATA_RATE_1620M,
> > > -                                  sunxi_display.depth);
> > > +                                  sunxi_display->depth);
> > >               }
> > >               if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
> > >                       mdelay(50); /* Wait for lcd controller power on */
> > > @@ -942,10 +959,10 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
> > >                       i2c_reg_write(0x5c, 0x04, 0x42); /* Turn on the LCD */
> > >                       i2c_set_bus_num(orig_i2c_bus);
> > >               }
> > > -             sunxi_composer_mode_set(mode, address);
> > > -             sunxi_lcdc_tcon0_mode_set(mode, false);
> > > +             sunxi_composer_mode_set(mode, address, monitor);
> > > +             sunxi_lcdc_tcon0_mode_set(sunxi_display, mode, false);
> > >               sunxi_composer_enable();
> > > -             lcdc_enable(lcdc, sunxi_display.depth);
> > > +             lcdc_enable(lcdc, sunxi_display->depth);
> > >  #ifdef CONFIG_VIDEO_LCD_SSD2828
> > >               sunxi_ssd2828_init(mode);
> > >  #endif
> > > @@ -953,17 +970,17 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
> > >               break;
> > >       case sunxi_monitor_vga:
> > >  #ifdef CONFIG_VIDEO_VGA
> > > -             sunxi_composer_mode_set(mode, address);
> > > -             sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
> > > -             sunxi_tvencoder_mode_set();
> > > +             sunxi_composer_mode_set(mode, address, monitor);
> > > +             sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1, monitor);
> > > +             sunxi_tvencoder_mode_set(monitor);
> > >               sunxi_composer_enable();
> > > -             lcdc_enable(lcdc, sunxi_display.depth);
> > > +             lcdc_enable(lcdc, sunxi_display->depth);
> > >               tvencoder_enable(tve);
> > >  #elif defined CONFIG_VIDEO_VGA_VIA_LCD
> > > -             sunxi_composer_mode_set(mode, address);
> > > -             sunxi_lcdc_tcon0_mode_set(mode, true);
> > > +             sunxi_composer_mode_set(mode, address, monitor);
> > > +             sunxi_lcdc_tcon0_mode_set(sunxi_display, mode, true);
> > >               sunxi_composer_enable();
> > > -             lcdc_enable(lcdc, sunxi_display.depth);
> > > +             lcdc_enable(lcdc, sunxi_display->depth);
> > >               sunxi_vga_external_dac_enable();
> > >  #endif
> > >               break;
> > > @@ -972,11 +989,11 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
> > >       case sunxi_monitor_composite_pal_m:
> > >       case sunxi_monitor_composite_pal_nc:
> > >  #ifdef CONFIG_VIDEO_COMPOSITE
> > > -             sunxi_composer_mode_set(mode, address);
> > > -             sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
> > > -             sunxi_tvencoder_mode_set();
> > > +             sunxi_composer_mode_set(mode, address, monitor);
> > > +             sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0, monitor);
> > > +             sunxi_tvencoder_mode_set(monitor);
> > >               sunxi_composer_enable();
> > > -             lcdc_enable(lcdc, sunxi_display.depth);
> > > +             lcdc_enable(lcdc, sunxi_display->depth);
> > >               tvencoder_enable(tve);
> > >  #endif
> > >               break;
> > > @@ -999,11 +1016,6 @@ static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
> > >       }
> > >  }
> > >
> > > -ulong board_get_usable_ram_top(ulong total_size)
> > > -{
> > > -     return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE;
> > > -}
> > > -
> >
> > I couldn't find how you're allocating and reserving the video buffer now
> > for simplefb to use later on.
>
> Based on maximum supporting resolutions and bpp as explained above and
> similar like what sunxi_de2 and other platforms like rockchip. via
> plat->size.


DM_VIDEO, take from the relocation - VIDEO_SIZE if I remember based on MAX video
size. Then there is always a logic to inject in the simple_framebuffer
dts the information for
reserved memory. The platform always defines MAX_SIZE of the video. so
HDMI resolutions
that does not fit the size are not handled. Rockchip is not completed
on this implementation, I have
sent a patch some time ago but I was struggling to have a working
persistent framebuffer

Michael

>
> >
> > >  static bool sunxi_has_hdmi(void)
> > >  {
> > >  #ifdef CONFIG_VIDEO_HDMI
> > > @@ -1052,9 +1064,11 @@ static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
> > >               return sunxi_monitor_none;
> > >  }
> > >
> > > -void *video_hw_init(void)
> > > +static int sunxi_de_probe(struct udevice *dev)
> > >  {
> > > -     static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
> > > +     struct video_priv *uc_priv = dev_get_uclass_priv(dev);
> > > +     struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
> > > +     struct sunxi_display *sunxi_display = dev_get_priv(dev);
> > >       const struct ctfb_res_modes *mode;
> > >       struct ctfb_res_modes custom;
> > >       const char *options;
> > > @@ -1067,10 +1081,8 @@ void *video_hw_init(void)
> > >       char mon[16];
> > >       char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
> > >
> > > -     memset(&sunxi_display, 0, sizeof(struct sunxi_display));
> > > -
> > >       video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
> > > -                              &sunxi_display.depth, &options);
> > > +                              &sunxi_display->depth, &options);
> > >  #ifdef CONFIG_VIDEO_HDMI
> > >       hpd = video_get_option_int(options, "hpd", 1);
> > >       hpd_delay = video_get_option_int(options, "hpd_delay", 500);
> > > @@ -1078,35 +1090,35 @@ void *video_hw_init(void)
> > >  #endif
> > >       overscan_x = video_get_option_int(options, "overscan_x", -1);
> > >       overscan_y = video_get_option_int(options, "overscan_y", -1);
> > > -     sunxi_display.monitor = sunxi_get_default_mon(true);
> > > +     sunxi_display->monitor = sunxi_get_default_mon(true);
> > >       video_get_option_string(options, "monitor", mon, sizeof(mon),
> > > -                             sunxi_get_mon_desc(sunxi_display.monitor));
> > > +                             sunxi_get_mon_desc(sunxi_display->monitor));
> > >       for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
> > >               if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
> > > -                     sunxi_display.monitor = i;
> > > +                     sunxi_display->monitor = i;
> > >                       break;
> > >               }
> > >       }
> > >       if (i > SUNXI_MONITOR_LAST)
> > >               printf("Unknown monitor: '%s', falling back to '%s'\n",
> > > -                    mon, sunxi_get_mon_desc(sunxi_display.monitor));
> > > +                    mon, sunxi_get_mon_desc(sunxi_display->monitor));
> > >
> > >  #ifdef CONFIG_VIDEO_HDMI
> > >       /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
> > > -     if (sunxi_display.monitor == sunxi_monitor_dvi ||
> > > -         sunxi_display.monitor == sunxi_monitor_hdmi) {
> > > +     if (sunxi_display->monitor == sunxi_monitor_dvi ||
> > > +         sunxi_display->monitor == sunxi_monitor_hdmi) {
> > >               /* Always call hdp_detect, as it also enables clocks, etc. */
> > >               hdmi_present = (sunxi_hdmi_hpd_detect(hpd_delay) == 1);
> > >               if (hdmi_present && edid) {
> > >                       printf("HDMI connected: ");
> > > -                     if (sunxi_hdmi_edid_get_mode(&custom, true) == 0)
> > > +                     if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, true) == 0)
> > >                               mode = &custom;
> > >                       else
> > >                               hdmi_present = false;
> > >               }
> > >               /* Fall back to EDID in case HPD failed */
> > >               if (edid && !hdmi_present) {
> > > -                     if (sunxi_hdmi_edid_get_mode(&custom, false) == 0) {
> > > +                     if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, false) == 0) {
> > >                               mode = &custom;
> > >                               hdmi_present = true;
> > >                       }
> > > @@ -1114,38 +1126,39 @@ void *video_hw_init(void)
> > >               /* Shut down when display was not found */
> > >               if ((hpd || edid) && !hdmi_present) {
> > >                       sunxi_hdmi_shutdown();
> > > -                     sunxi_display.monitor = sunxi_get_default_mon(false);
> > > +                     sunxi_display->monitor = sunxi_get_default_mon(false);
> > >               } /* else continue with hdmi/dvi without a cable connected */
> > >       }
> > >  #endif
> > >
> > > -     switch (sunxi_display.monitor) {
> > > +     switch (sunxi_display->monitor) {
> > >       case sunxi_monitor_none:
> > > -             return NULL;
> > > +             printf("Unknown monitor\n");
> > > +             return -EINVAL;
> > >       case sunxi_monitor_dvi:
> > >       case sunxi_monitor_hdmi:
> > >               if (!sunxi_has_hdmi()) {
> > >                       printf("HDMI/DVI not supported on this board\n");
> > > -                     sunxi_display.monitor = sunxi_monitor_none;
> > > -                     return NULL;
> > > +                     sunxi_display->monitor = sunxi_monitor_none;
> > > +                     return -EINVAL;
> > >               }
> > >               break;
> > >       case sunxi_monitor_lcd:
> > >               if (!sunxi_has_lcd()) {
> > >                       printf("LCD not supported on this board\n");
> > > -                     sunxi_display.monitor = sunxi_monitor_none;
> > > -                     return NULL;
> > > +                     sunxi_display->monitor = sunxi_monitor_none;
> > > +                     return -EINVAL;
> > >               }
> > > -             sunxi_display.depth = video_get_params(&custom, lcd_mode);
> > > +             sunxi_display->depth = video_get_params(&custom, lcd_mode);
> > >               mode = &custom;
> > >               break;
> > >       case sunxi_monitor_vga:
> > >               if (!sunxi_has_vga()) {
> > >                       printf("VGA not supported on this board\n");
> > > -                     sunxi_display.monitor = sunxi_monitor_none;
> > > -                     return NULL;
> > > +                     sunxi_display->monitor = sunxi_monitor_none;
> > > +                     return -EINVAL;
> > >               }
> > > -             sunxi_display.depth = 18;
> > > +             sunxi_display->depth = 18;
> > >               break;
> > >       case sunxi_monitor_composite_pal:
> > >       case sunxi_monitor_composite_ntsc:
> > > @@ -1153,85 +1166,103 @@ void *video_hw_init(void)
> > >       case sunxi_monitor_composite_pal_nc:
> > >               if (!sunxi_has_composite()) {
> > >                       printf("Composite video not supported on this board\n");
> > > -                     sunxi_display.monitor = sunxi_monitor_none;
> > > -                     return NULL;
> > > +                     sunxi_display->monitor = sunxi_monitor_none;
> > > +                     return -EINVAL;
> > >               }
> > > -             if (sunxi_display.monitor == sunxi_monitor_composite_pal ||
> > > -                 sunxi_display.monitor == sunxi_monitor_composite_pal_nc)
> > > +             if (sunxi_display->monitor == sunxi_monitor_composite_pal ||
> > > +                 sunxi_display->monitor == sunxi_monitor_composite_pal_nc)
> > >                       mode = &composite_video_modes[0];
> > >               else
> > >                       mode = &composite_video_modes[1];
> > > -             sunxi_display.depth = 24;
> > > +             sunxi_display->depth = 24;
> > >               break;
> > >       }
> > >
> > >       /* Yes these defaults are quite high, overscan on composite sucks... */
> > >       if (overscan_x == -1)
> > > -             overscan_x = sunxi_is_composite() ? 32 : 0;
> > > +             overscan_x = sunxi_is_composite(sunxi_display->monitor) ? 32 : 0;
> > >       if (overscan_y == -1)
> > > -             overscan_y = sunxi_is_composite() ? 20 : 0;
> > > +             overscan_y = sunxi_is_composite(sunxi_display->monitor) ? 20 : 0;
> > >
> > > -     sunxi_display.fb_size =
> > > -             (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff;
> > > +     sunxi_display->fb_size = plat->size;
> > >       overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
> > >       /* We want to keep the fb_base for simplefb page aligned, where as
> > >        * the sunxi dma engines will happily accept an unaligned address. */
> > >       if (overscan_offset)
> > > -             sunxi_display.fb_size += 0x1000;
> > > -
> > > -     if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) {
> > > -             printf("Error need %dkB for fb, but only %dkB is reserved\n",
> > > -                    sunxi_display.fb_size >> 10,
> > > -                    CONFIG_SUNXI_MAX_FB_SIZE >> 10);
> > > -             return NULL;
> > > -     }
> > > +             sunxi_display->fb_size += 0x1000;
> > >
> > >       printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
> > >              mode->xres, mode->yres,
> > >              (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
> > > -            sunxi_get_mon_desc(sunxi_display.monitor),
> > > +            sunxi_get_mon_desc(sunxi_display->monitor),
> > >              overscan_x, overscan_y);
> > >
> > > -     gd->fb_base = gd->bd->bi_dram[0].start +
> > > -                   gd->bd->bi_dram[0].size - sunxi_display.fb_size;
> > > +     sunxi_display->fb_addr = plat->base;
> > >       sunxi_engines_init();
> > >
> > >  #ifdef CONFIG_EFI_LOADER
> > > -     efi_add_memory_map(gd->fb_base, sunxi_display.fb_size,
> > > +     efi_add_memory_map(sunxi_display->fb_addr, sunxi_display->fb_size,
> > >                          EFI_RESERVED_MEMORY_TYPE);
> > >  #endif
> > >
> > > -     fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE;
> > > -     sunxi_display.fb_addr = gd->fb_base;
> > > +     fb_dma_addr = sunxi_display->fb_addr - CONFIG_SYS_SDRAM_BASE;
> > >       if (overscan_offset) {
> > >               fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
> > > -             sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff;
> > > -             memset((void *)gd->fb_base, 0, sunxi_display.fb_size);
> > > -             flush_cache(gd->fb_base, sunxi_display.fb_size);
> > > +             sunxi_display->fb_addr += (overscan_offset + 0xfff) & ~0xfff;
> > > +             memset((void *)sunxi_display->fb_addr, 0, sunxi_display->fb_size);
> > > +             flush_cache(sunxi_display->fb_addr, sunxi_display->fb_size);
> > >       }
> > > -     sunxi_mode_set(mode, fb_dma_addr);
> > > +     sunxi_mode_set(sunxi_display, mode, fb_dma_addr);
> > >
> > >       /*
> > >        * These are the only members of this structure that are used. All the
> > >        * others are driver specific. The pitch is stored in plnSizeX.
> > >        */
> > > -     graphic_device->frameAdrs = sunxi_display.fb_addr;
> > > -     graphic_device->gdfIndex = GDF_32BIT_X888RGB;
> > > -     graphic_device->gdfBytesPP = 4;
> > > -     graphic_device->winSizeX = mode->xres - 2 * overscan_x;
> > > -     graphic_device->winSizeY = mode->yres - 2 * overscan_y;
> > > -     graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP;
> > > -
> > > -     return graphic_device;
> > > +     uc_priv->bpix = VIDEO_BPP32;
> > > +     uc_priv->xsize = mode->xres;
> > > +     uc_priv->ysize = mode->yres;
> > > +
> > > +     video_set_flush_dcache(dev, 1);
> >
> > This is new. You should explain in a separate patch why the flush_cache
> > above isn't enough.
>
> This is new and but required when for DM_VIDEO conversion.
> video-uclass has a common way to flush video via video_sync so the
> driver has to trigger this flush and without this, the vidconsole
> shows inconsistent characters.
>
> Jagan.
>
>
Maxime Ripard June 19, 2020, 7:30 a.m. UTC | #4
On Thu, Jun 18, 2020 at 09:37:07PM +0530, Jagan Teki wrote:
> On Thu, Jun 18, 2020 at 12:54 PM Maxime Ripard <maxime@cerno.tech> wrote:
> >
> > On Thu, Jun 18, 2020 at 01:54:37AM +0530, Jagan Teki wrote:
> > > DM_VIDEO migration deadline is already expired, but around
> > > 80 Allwinner boards are still using video in a legacy way.
> > >
> > > ===================== WARNING ======================
> > > This board does not use CONFIG_DM_VIDEO Please update
> > > the board to use CONFIG_DM_VIDEO before the v2019.07 release.
> > > Failure to update by the deadline may result in board removal.
> > > See doc/driver-model/migration.rst for more info.
> > > ====================================================
> > >
> > > So let's live these boards on the tree before the video maintainer
> > > removes it by converting in to DM_VIDEO.
> > >
> > > Tested in Bananapi M1+ Plus 1920x1200 HDMI out.
> > >
> > > Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
> > > ---
> > > Changes for v2:
> > > - add BMP support
> > >
> > >  arch/arm/mach-sunxi/Kconfig         |   4 +-
> > >  drivers/video/sunxi/sunxi_display.c | 264 ++++++++++++++++------------
> > >  include/configs/sunxi-common.h      |  17 --
> > >  scripts/config_whitelist.txt        |   1 -
> > >  4 files changed, 157 insertions(+), 129 deletions(-)
> > >
> > > diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> > > index be0822bfb7..7d2fb55ff0 100644
> > > --- a/arch/arm/mach-sunxi/Kconfig
> > > +++ b/arch/arm/mach-sunxi/Kconfig
> > > @@ -758,8 +758,10 @@ config VIDEO_SUNXI
> > >       depends on !MACH_SUN9I
> > >       depends on !MACH_SUN50I
> > >       depends on !MACH_SUN50I_H6
> > > -     select VIDEO
> > > +     select DM_VIDEO
> > > +     select DISPLAY
> > >       imply VIDEO_DT_SIMPLEFB
> > > +     imply CMD_BMP
> > >       default y
> > >       ---help---
> > >       Say Y here to add support for using a cfb console on the HDMI, LCD
> > > diff --git a/drivers/video/sunxi/sunxi_display.c b/drivers/video/sunxi/sunxi_display.c
> > > index f52aba4d21..fb51b0c5ba 100644
> > > --- a/drivers/video/sunxi/sunxi_display.c
> > > +++ b/drivers/video/sunxi/sunxi_display.c
> > > @@ -7,6 +7,8 @@
> > >   */
> > >
> > >  #include <common.h>
> > > +#include <display.h>
> > > +#include <dm.h>
> > >  #include <cpu_func.h>
> > >  #include <efi_loader.h>
> > >  #include <init.h>
> > > @@ -28,7 +30,9 @@
> > >  #include <fdt_support.h>
> > >  #include <i2c.h>
> > >  #include <malloc.h>
> > > +#include <video.h>
> > >  #include <video_fb.h>
> > > +#include <dm/uclass-internal.h>
> > >  #include "../videomodes.h"
> > >  #include "../anx9804.h"
> > >  #include "../hitachi_tx18d42vm_lcd.h"
> > > @@ -45,6 +49,13 @@
> > >
> > >  DECLARE_GLOBAL_DATA_PTR;
> > >
> > > +enum {
> > > +     /* Maximum LCD size we support */
> > > +     LCD_MAX_WIDTH           = 3840,
> > > +     LCD_MAX_HEIGHT          = 2160,
> > > +     LCD_MAX_LOG2_BPP        = VIDEO_BPP32,
> > > +};
> >
> > Why is that an enum? Those three values don't have any relationship with
> > each other.
> 
> Not sure I understand your question. These are maximum resolution
> followed by maximum bpp supporting. based on these the fbbuffer
> allocation happens, please see sunxi_de_bind on this patch.

An enum is used for variables that can hold multiple values. There's no
relationship between those values, you cannot use the BPP in place of
the width there for example, it doesn't make any sense. So you must not
use an enum.

> > > -ulong board_get_usable_ram_top(ulong total_size)
> > > -{
> > > -     return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE;
> > > -}
> > > -
> >
> > I couldn't find how you're allocating and reserving the video buffer now
> > for simplefb to use later on.
> 
> Based on maximum supporting resolutions and bpp as explained above and
> similar like what sunxi_de2 and other platforms like rockchip. via
> plat->size.

It really doesn't explain how you're allocating and reserving it for
simplefb though, merely how much memory you're allocating.

Let me rephrase this. simplefb will run in Linux based on an address
that is passed from the bootloader to Linux through the device tree. How
do you make sure that Linux will not reuse that buffer for something
else once it's been booted?

The previous code was doing that by removing enough space from the total
memory available, so Linux could run only on the total amount of RAM
minus the size of the framebuffer. You removed that, so how does it work
now?

And if you're changing that somehow, at the very least, it should be
either in a separate patch or mentionned in the commit log.

> >
> > >  static bool sunxi_has_hdmi(void)
> > >  {
> > >  #ifdef CONFIG_VIDEO_HDMI
> > > @@ -1052,9 +1064,11 @@ static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
> > >               return sunxi_monitor_none;
> > >  }
> > >
> > > -void *video_hw_init(void)
> > > +static int sunxi_de_probe(struct udevice *dev)
> > >  {
> > > -     static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
> > > +     struct video_priv *uc_priv = dev_get_uclass_priv(dev);
> > > +     struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
> > > +     struct sunxi_display *sunxi_display = dev_get_priv(dev);
> > >       const struct ctfb_res_modes *mode;
> > >       struct ctfb_res_modes custom;
> > >       const char *options;
> > > @@ -1067,10 +1081,8 @@ void *video_hw_init(void)
> > >       char mon[16];
> > >       char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
> > >
> > > -     memset(&sunxi_display, 0, sizeof(struct sunxi_display));
> > > -
> > >       video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
> > > -                              &sunxi_display.depth, &options);
> > > +                              &sunxi_display->depth, &options);
> > >  #ifdef CONFIG_VIDEO_HDMI
> > >       hpd = video_get_option_int(options, "hpd", 1);
> > >       hpd_delay = video_get_option_int(options, "hpd_delay", 500);
> > > @@ -1078,35 +1090,35 @@ void *video_hw_init(void)
> > >  #endif
> > >       overscan_x = video_get_option_int(options, "overscan_x", -1);
> > >       overscan_y = video_get_option_int(options, "overscan_y", -1);
> > > -     sunxi_display.monitor = sunxi_get_default_mon(true);
> > > +     sunxi_display->monitor = sunxi_get_default_mon(true);
> > >       video_get_option_string(options, "monitor", mon, sizeof(mon),
> > > -                             sunxi_get_mon_desc(sunxi_display.monitor));
> > > +                             sunxi_get_mon_desc(sunxi_display->monitor));
> > >       for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
> > >               if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
> > > -                     sunxi_display.monitor = i;
> > > +                     sunxi_display->monitor = i;
> > >                       break;
> > >               }
> > >       }
> > >       if (i > SUNXI_MONITOR_LAST)
> > >               printf("Unknown monitor: '%s', falling back to '%s'\n",
> > > -                    mon, sunxi_get_mon_desc(sunxi_display.monitor));
> > > +                    mon, sunxi_get_mon_desc(sunxi_display->monitor));
> > >
> > >  #ifdef CONFIG_VIDEO_HDMI
> > >       /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
> > > -     if (sunxi_display.monitor == sunxi_monitor_dvi ||
> > > -         sunxi_display.monitor == sunxi_monitor_hdmi) {
> > > +     if (sunxi_display->monitor == sunxi_monitor_dvi ||
> > > +         sunxi_display->monitor == sunxi_monitor_hdmi) {
> > >               /* Always call hdp_detect, as it also enables clocks, etc. */
> > >               hdmi_present = (sunxi_hdmi_hpd_detect(hpd_delay) == 1);
> > >               if (hdmi_present && edid) {
> > >                       printf("HDMI connected: ");
> > > -                     if (sunxi_hdmi_edid_get_mode(&custom, true) == 0)
> > > +                     if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, true) == 0)
> > >                               mode = &custom;
> > >                       else
> > >                               hdmi_present = false;
> > >               }
> > >               /* Fall back to EDID in case HPD failed */
> > >               if (edid && !hdmi_present) {
> > > -                     if (sunxi_hdmi_edid_get_mode(&custom, false) == 0) {
> > > +                     if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, false) == 0) {
> > >                               mode = &custom;
> > >                               hdmi_present = true;
> > >                       }
> > > @@ -1114,38 +1126,39 @@ void *video_hw_init(void)
> > >               /* Shut down when display was not found */
> > >               if ((hpd || edid) && !hdmi_present) {
> > >                       sunxi_hdmi_shutdown();
> > > -                     sunxi_display.monitor = sunxi_get_default_mon(false);
> > > +                     sunxi_display->monitor = sunxi_get_default_mon(false);
> > >               } /* else continue with hdmi/dvi without a cable connected */
> > >       }
> > >  #endif
> > >
> > > -     switch (sunxi_display.monitor) {
> > > +     switch (sunxi_display->monitor) {
> > >       case sunxi_monitor_none:
> > > -             return NULL;
> > > +             printf("Unknown monitor\n");
> > > +             return -EINVAL;
> > >       case sunxi_monitor_dvi:
> > >       case sunxi_monitor_hdmi:
> > >               if (!sunxi_has_hdmi()) {
> > >                       printf("HDMI/DVI not supported on this board\n");
> > > -                     sunxi_display.monitor = sunxi_monitor_none;
> > > -                     return NULL;
> > > +                     sunxi_display->monitor = sunxi_monitor_none;
> > > +                     return -EINVAL;
> > >               }
> > >               break;
> > >       case sunxi_monitor_lcd:
> > >               if (!sunxi_has_lcd()) {
> > >                       printf("LCD not supported on this board\n");
> > > -                     sunxi_display.monitor = sunxi_monitor_none;
> > > -                     return NULL;
> > > +                     sunxi_display->monitor = sunxi_monitor_none;
> > > +                     return -EINVAL;
> > >               }
> > > -             sunxi_display.depth = video_get_params(&custom, lcd_mode);
> > > +             sunxi_display->depth = video_get_params(&custom, lcd_mode);
> > >               mode = &custom;
> > >               break;
> > >       case sunxi_monitor_vga:
> > >               if (!sunxi_has_vga()) {
> > >                       printf("VGA not supported on this board\n");
> > > -                     sunxi_display.monitor = sunxi_monitor_none;
> > > -                     return NULL;
> > > +                     sunxi_display->monitor = sunxi_monitor_none;
> > > +                     return -EINVAL;
> > >               }
> > > -             sunxi_display.depth = 18;
> > > +             sunxi_display->depth = 18;
> > >               break;
> > >       case sunxi_monitor_composite_pal:
> > >       case sunxi_monitor_composite_ntsc:
> > > @@ -1153,85 +1166,103 @@ void *video_hw_init(void)
> > >       case sunxi_monitor_composite_pal_nc:
> > >               if (!sunxi_has_composite()) {
> > >                       printf("Composite video not supported on this board\n");
> > > -                     sunxi_display.monitor = sunxi_monitor_none;
> > > -                     return NULL;
> > > +                     sunxi_display->monitor = sunxi_monitor_none;
> > > +                     return -EINVAL;
> > >               }
> > > -             if (sunxi_display.monitor == sunxi_monitor_composite_pal ||
> > > -                 sunxi_display.monitor == sunxi_monitor_composite_pal_nc)
> > > +             if (sunxi_display->monitor == sunxi_monitor_composite_pal ||
> > > +                 sunxi_display->monitor == sunxi_monitor_composite_pal_nc)
> > >                       mode = &composite_video_modes[0];
> > >               else
> > >                       mode = &composite_video_modes[1];
> > > -             sunxi_display.depth = 24;
> > > +             sunxi_display->depth = 24;
> > >               break;
> > >       }
> > >
> > >       /* Yes these defaults are quite high, overscan on composite sucks... */
> > >       if (overscan_x == -1)
> > > -             overscan_x = sunxi_is_composite() ? 32 : 0;
> > > +             overscan_x = sunxi_is_composite(sunxi_display->monitor) ? 32 : 0;
> > >       if (overscan_y == -1)
> > > -             overscan_y = sunxi_is_composite() ? 20 : 0;
> > > +             overscan_y = sunxi_is_composite(sunxi_display->monitor) ? 20 : 0;
> > >
> > > -     sunxi_display.fb_size =
> > > -             (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff;
> > > +     sunxi_display->fb_size = plat->size;
> > >       overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
> > >       /* We want to keep the fb_base for simplefb page aligned, where as
> > >        * the sunxi dma engines will happily accept an unaligned address. */
> > >       if (overscan_offset)
> > > -             sunxi_display.fb_size += 0x1000;
> > > -
> > > -     if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) {
> > > -             printf("Error need %dkB for fb, but only %dkB is reserved\n",
> > > -                    sunxi_display.fb_size >> 10,
> > > -                    CONFIG_SUNXI_MAX_FB_SIZE >> 10);
> > > -             return NULL;
> > > -     }
> > > +             sunxi_display->fb_size += 0x1000;
> > >
> > >       printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
> > >              mode->xres, mode->yres,
> > >              (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
> > > -            sunxi_get_mon_desc(sunxi_display.monitor),
> > > +            sunxi_get_mon_desc(sunxi_display->monitor),
> > >              overscan_x, overscan_y);
> > >
> > > -     gd->fb_base = gd->bd->bi_dram[0].start +
> > > -                   gd->bd->bi_dram[0].size - sunxi_display.fb_size;
> > > +     sunxi_display->fb_addr = plat->base;
> > >       sunxi_engines_init();
> > >
> > >  #ifdef CONFIG_EFI_LOADER
> > > -     efi_add_memory_map(gd->fb_base, sunxi_display.fb_size,
> > > +     efi_add_memory_map(sunxi_display->fb_addr, sunxi_display->fb_size,
> > >                          EFI_RESERVED_MEMORY_TYPE);
> > >  #endif
> > >
> > > -     fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE;
> > > -     sunxi_display.fb_addr = gd->fb_base;
> > > +     fb_dma_addr = sunxi_display->fb_addr - CONFIG_SYS_SDRAM_BASE;
> > >       if (overscan_offset) {
> > >               fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
> > > -             sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff;
> > > -             memset((void *)gd->fb_base, 0, sunxi_display.fb_size);
> > > -             flush_cache(gd->fb_base, sunxi_display.fb_size);
> > > +             sunxi_display->fb_addr += (overscan_offset + 0xfff) & ~0xfff;
> > > +             memset((void *)sunxi_display->fb_addr, 0, sunxi_display->fb_size);
> > > +             flush_cache(sunxi_display->fb_addr, sunxi_display->fb_size);
> > >       }
> > > -     sunxi_mode_set(mode, fb_dma_addr);
> > > +     sunxi_mode_set(sunxi_display, mode, fb_dma_addr);
> > >
> > >       /*
> > >        * These are the only members of this structure that are used. All the
> > >        * others are driver specific. The pitch is stored in plnSizeX.
> > >        */
> > > -     graphic_device->frameAdrs = sunxi_display.fb_addr;
> > > -     graphic_device->gdfIndex = GDF_32BIT_X888RGB;
> > > -     graphic_device->gdfBytesPP = 4;
> > > -     graphic_device->winSizeX = mode->xres - 2 * overscan_x;
> > > -     graphic_device->winSizeY = mode->yres - 2 * overscan_y;
> > > -     graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP;
> > > -
> > > -     return graphic_device;
> > > +     uc_priv->bpix = VIDEO_BPP32;
> > > +     uc_priv->xsize = mode->xres;
> > > +     uc_priv->ysize = mode->yres;
> > > +
> > > +     video_set_flush_dcache(dev, 1);
> >
> > This is new. You should explain in a separate patch why the flush_cache
> > above isn't enough.
> 
> This is new and but required when for DM_VIDEO conversion.
> video-uclass has a common way to flush video via video_sync so the
> driver has to trigger this flush and without this, the vidconsole
> shows inconsistent characters.

I still believe this is redundant with the flush_cache above.

Maxime
Andre Przywara Feb. 4, 2021, 10:30 p.m. UTC | #5
On Fri, 19 Jun 2020 09:30:22 +0200
Maxime Ripard <maxime@cerno.tech> wrote:

Hi,

I have picked up this patch, rebased, updated and fixed some parts.

I will send a v3 ASAP, so we can continue the discussion on this.

I am replying here to some of the outstanding comments, since this has
the context:

> On Thu, Jun 18, 2020 at 09:37:07PM +0530, Jagan Teki wrote:
> > On Thu, Jun 18, 2020 at 12:54 PM Maxime Ripard <maxime@cerno.tech> wrote:  
> > >
> > > On Thu, Jun 18, 2020 at 01:54:37AM +0530, Jagan Teki wrote:  
> > > > DM_VIDEO migration deadline is already expired, but around
> > > > 80 Allwinner boards are still using video in a legacy way.
> > > >
> > > > ===================== WARNING ======================
> > > > This board does not use CONFIG_DM_VIDEO Please update
> > > > the board to use CONFIG_DM_VIDEO before the v2019.07 release.
> > > > Failure to update by the deadline may result in board removal.
> > > > See doc/driver-model/migration.rst for more info.
> > > > ====================================================
> > > >
> > > > So let's live these boards on the tree before the video maintainer
> > > > removes it by converting in to DM_VIDEO.
> > > >
> > > > Tested in Bananapi M1+ Plus 1920x1200 HDMI out.
> > > >
> > > > Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
> > > > ---
> > > > Changes for v2:
> > > > - add BMP support
> > > >
> > > >  arch/arm/mach-sunxi/Kconfig         |   4 +-
> > > >  drivers/video/sunxi/sunxi_display.c | 264 ++++++++++++++++------------
> > > >  include/configs/sunxi-common.h      |  17 --
> > > >  scripts/config_whitelist.txt        |   1 -
> > > >  4 files changed, 157 insertions(+), 129 deletions(-)
> > > >
> > > > diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> > > > index be0822bfb7..7d2fb55ff0 100644
> > > > --- a/arch/arm/mach-sunxi/Kconfig
> > > > +++ b/arch/arm/mach-sunxi/Kconfig
> > > > @@ -758,8 +758,10 @@ config VIDEO_SUNXI
> > > >       depends on !MACH_SUN9I
> > > >       depends on !MACH_SUN50I
> > > >       depends on !MACH_SUN50I_H6
> > > > -     select VIDEO
> > > > +     select DM_VIDEO
> > > > +     select DISPLAY
> > > >       imply VIDEO_DT_SIMPLEFB
> > > > +     imply CMD_BMP
> > > >       default y
> > > >       ---help---
> > > >       Say Y here to add support for using a cfb console on the HDMI, LCD
> > > > diff --git a/drivers/video/sunxi/sunxi_display.c b/drivers/video/sunxi/sunxi_display.c
> > > > index f52aba4d21..fb51b0c5ba 100644
> > > > --- a/drivers/video/sunxi/sunxi_display.c
> > > > +++ b/drivers/video/sunxi/sunxi_display.c
> > > > @@ -7,6 +7,8 @@
> > > >   */
> > > >
> > > >  #include <common.h>
> > > > +#include <display.h>
> > > > +#include <dm.h>
> > > >  #include <cpu_func.h>
> > > >  #include <efi_loader.h>
> > > >  #include <init.h>
> > > > @@ -28,7 +30,9 @@
> > > >  #include <fdt_support.h>
> > > >  #include <i2c.h>
> > > >  #include <malloc.h>
> > > > +#include <video.h>
> > > >  #include <video_fb.h>
> > > > +#include <dm/uclass-internal.h>
> > > >  #include "../videomodes.h"
> > > >  #include "../anx9804.h"
> > > >  #include "../hitachi_tx18d42vm_lcd.h"
> > > > @@ -45,6 +49,13 @@
> > > >
> > > >  DECLARE_GLOBAL_DATA_PTR;
> > > >
> > > > +enum {
> > > > +     /* Maximum LCD size we support */
> > > > +     LCD_MAX_WIDTH           = 3840,
> > > > +     LCD_MAX_HEIGHT          = 2160,
> > > > +     LCD_MAX_LOG2_BPP        = VIDEO_BPP32,
> > > > +};  
> > >
> > > Why is that an enum? Those three values don't have any relationship with
> > > each other.  
> > 
> > Not sure I understand your question. These are maximum resolution
> > followed by maximum bpp supporting. based on these the fbbuffer
> > allocation happens, please see sunxi_de_bind on this patch.  
> 
> An enum is used for variables that can hold multiple values. There's no
> relationship between those values, you cannot use the BPP in place of
> the width there for example, it doesn't make any sense. So you must not
> use an enum.

I agree that an enum does not make sense here. That seems to be
copied from sunxi_de2.c, which has the same code ;-)
My guess is this stems from some misunderstood "use enums instead of
#define!" recommendation. I replaced it with old-fashioned #defines.

> 
> > > > -ulong board_get_usable_ram_top(ulong total_size)
> > > > -{
> > > > -     return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE;
> > > > -}
> > > > -  
> > >
> > > I couldn't find how you're allocating and reserving the video buffer now
> > > for simplefb to use later on.  
> > 
> > Based on maximum supporting resolutions and bpp as explained above and
> > similar like what sunxi_de2 and other platforms like rockchip. via
> > plat->size.  
> 
> It really doesn't explain how you're allocating and reserving it for
> simplefb though, merely how much memory you're allocating.
> 
> Let me rephrase this. simplefb will run in Linux based on an address
> that is passed from the bootloader to Linux through the device tree. How
> do you make sure that Linux will not reuse that buffer for something
> else once it's been booted?
> 
> The previous code was doing that by removing enough space from the total
> memory available, so Linux could run only on the total amount of RAM
> minus the size of the framebuffer. You removed that, so how does it work
> now?

So the former code lowered U-Boot's perception of gd->ram_top, but
sunxi-de2 doesn't do this either. I don't think this on its own is a
real problem.
What both sunxi-de2 and this patch do is to patch the /memory node in
the DT to exclude the framebuffer region, at the same time when
patching in the framebuffer details into the chosen node (in the last
function of this patch, not in this email anymore).

There *is* a bug in this patch that creates a small overlap, which
makes Linux crash. That's easily fixed, though:

 	start = gd->bd->bi_dram[0].start;
-	size = gd->bd->bi_dram[0].size - sunxi_display->fb_size;
+	size = sunxi_display->fb_addr - start;
 	ret = fdt_fixup_memory_banks(blob, &start, &size, 1);

Maxime, does that answer your concerns? I tested this (fixed) patch with
simplefb, and it works: the /memory node ends 32MB before the end of
DRAM, and the framebuffer works in Linux.

> And if you're changing that somehow, at the very least, it should be
> either in a separate patch or mentionned in the commit log.
> 
> > >  
> > > >  static bool sunxi_has_hdmi(void)
> > > >  {
> > > >  #ifdef CONFIG_VIDEO_HDMI
> > > > @@ -1052,9 +1064,11 @@ static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
> > > >               return sunxi_monitor_none;
> > > >  }
> > > >
> > > > -void *video_hw_init(void)
> > > > +static int sunxi_de_probe(struct udevice *dev)
> > > >  {
> > > > -     static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
> > > > +     struct video_priv *uc_priv = dev_get_uclass_priv(dev);
> > > > +     struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
> > > > +     struct sunxi_display *sunxi_display = dev_get_priv(dev);
> > > >       const struct ctfb_res_modes *mode;
> > > >       struct ctfb_res_modes custom;
> > > >       const char *options;
> > > > @@ -1067,10 +1081,8 @@ void *video_hw_init(void)
> > > >       char mon[16];
> > > >       char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
> > > >
> > > > -     memset(&sunxi_display, 0, sizeof(struct sunxi_display));
> > > > -
> > > >       video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
> > > > -                              &sunxi_display.depth, &options);
> > > > +                              &sunxi_display->depth, &options);
> > > >  #ifdef CONFIG_VIDEO_HDMI
> > > >       hpd = video_get_option_int(options, "hpd", 1);
> > > >       hpd_delay = video_get_option_int(options, "hpd_delay", 500);
> > > > @@ -1078,35 +1090,35 @@ void *video_hw_init(void)
> > > >  #endif
> > > >       overscan_x = video_get_option_int(options, "overscan_x", -1);
> > > >       overscan_y = video_get_option_int(options, "overscan_y", -1);
> > > > -     sunxi_display.monitor = sunxi_get_default_mon(true);
> > > > +     sunxi_display->monitor = sunxi_get_default_mon(true);
> > > >       video_get_option_string(options, "monitor", mon, sizeof(mon),
> > > > -                             sunxi_get_mon_desc(sunxi_display.monitor));
> > > > +                             sunxi_get_mon_desc(sunxi_display->monitor));
> > > >       for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
> > > >               if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
> > > > -                     sunxi_display.monitor = i;
> > > > +                     sunxi_display->monitor = i;
> > > >                       break;
> > > >               }
> > > >       }
> > > >       if (i > SUNXI_MONITOR_LAST)
> > > >               printf("Unknown monitor: '%s', falling back to '%s'\n",
> > > > -                    mon, sunxi_get_mon_desc(sunxi_display.monitor));
> > > > +                    mon, sunxi_get_mon_desc(sunxi_display->monitor));
> > > >
> > > >  #ifdef CONFIG_VIDEO_HDMI
> > > >       /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
> > > > -     if (sunxi_display.monitor == sunxi_monitor_dvi ||
> > > > -         sunxi_display.monitor == sunxi_monitor_hdmi) {
> > > > +     if (sunxi_display->monitor == sunxi_monitor_dvi ||
> > > > +         sunxi_display->monitor == sunxi_monitor_hdmi) {
> > > >               /* Always call hdp_detect, as it also enables clocks, etc. */
> > > >               hdmi_present = (sunxi_hdmi_hpd_detect(hpd_delay) == 1);
> > > >               if (hdmi_present && edid) {
> > > >                       printf("HDMI connected: ");
> > > > -                     if (sunxi_hdmi_edid_get_mode(&custom, true) == 0)
> > > > +                     if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, true) == 0)
> > > >                               mode = &custom;
> > > >                       else
> > > >                               hdmi_present = false;
> > > >               }
> > > >               /* Fall back to EDID in case HPD failed */
> > > >               if (edid && !hdmi_present) {
> > > > -                     if (sunxi_hdmi_edid_get_mode(&custom, false) == 0) {
> > > > +                     if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, false) == 0) {
> > > >                               mode = &custom;
> > > >                               hdmi_present = true;
> > > >                       }
> > > > @@ -1114,38 +1126,39 @@ void *video_hw_init(void)
> > > >               /* Shut down when display was not found */
> > > >               if ((hpd || edid) && !hdmi_present) {
> > > >                       sunxi_hdmi_shutdown();
> > > > -                     sunxi_display.monitor = sunxi_get_default_mon(false);
> > > > +                     sunxi_display->monitor = sunxi_get_default_mon(false);
> > > >               } /* else continue with hdmi/dvi without a cable connected */
> > > >       }
> > > >  #endif
> > > >
> > > > -     switch (sunxi_display.monitor) {
> > > > +     switch (sunxi_display->monitor) {
> > > >       case sunxi_monitor_none:
> > > > -             return NULL;
> > > > +             printf("Unknown monitor\n");
> > > > +             return -EINVAL;
> > > >       case sunxi_monitor_dvi:
> > > >       case sunxi_monitor_hdmi:
> > > >               if (!sunxi_has_hdmi()) {
> > > >                       printf("HDMI/DVI not supported on this board\n");
> > > > -                     sunxi_display.monitor = sunxi_monitor_none;
> > > > -                     return NULL;
> > > > +                     sunxi_display->monitor = sunxi_monitor_none;
> > > > +                     return -EINVAL;
> > > >               }
> > > >               break;
> > > >       case sunxi_monitor_lcd:
> > > >               if (!sunxi_has_lcd()) {
> > > >                       printf("LCD not supported on this board\n");
> > > > -                     sunxi_display.monitor = sunxi_monitor_none;
> > > > -                     return NULL;
> > > > +                     sunxi_display->monitor = sunxi_monitor_none;
> > > > +                     return -EINVAL;
> > > >               }
> > > > -             sunxi_display.depth = video_get_params(&custom, lcd_mode);
> > > > +             sunxi_display->depth = video_get_params(&custom, lcd_mode);
> > > >               mode = &custom;
> > > >               break;
> > > >       case sunxi_monitor_vga:
> > > >               if (!sunxi_has_vga()) {
> > > >                       printf("VGA not supported on this board\n");
> > > > -                     sunxi_display.monitor = sunxi_monitor_none;
> > > > -                     return NULL;
> > > > +                     sunxi_display->monitor = sunxi_monitor_none;
> > > > +                     return -EINVAL;
> > > >               }
> > > > -             sunxi_display.depth = 18;
> > > > +             sunxi_display->depth = 18;
> > > >               break;
> > > >       case sunxi_monitor_composite_pal:
> > > >       case sunxi_monitor_composite_ntsc:
> > > > @@ -1153,85 +1166,103 @@ void *video_hw_init(void)
> > > >       case sunxi_monitor_composite_pal_nc:
> > > >               if (!sunxi_has_composite()) {
> > > >                       printf("Composite video not supported on this board\n");
> > > > -                     sunxi_display.monitor = sunxi_monitor_none;
> > > > -                     return NULL;
> > > > +                     sunxi_display->monitor = sunxi_monitor_none;
> > > > +                     return -EINVAL;
> > > >               }
> > > > -             if (sunxi_display.monitor == sunxi_monitor_composite_pal ||
> > > > -                 sunxi_display.monitor == sunxi_monitor_composite_pal_nc)
> > > > +             if (sunxi_display->monitor == sunxi_monitor_composite_pal ||
> > > > +                 sunxi_display->monitor == sunxi_monitor_composite_pal_nc)
> > > >                       mode = &composite_video_modes[0];
> > > >               else
> > > >                       mode = &composite_video_modes[1];
> > > > -             sunxi_display.depth = 24;
> > > > +             sunxi_display->depth = 24;
> > > >               break;
> > > >       }
> > > >
> > > >       /* Yes these defaults are quite high, overscan on composite sucks... */
> > > >       if (overscan_x == -1)
> > > > -             overscan_x = sunxi_is_composite() ? 32 : 0;
> > > > +             overscan_x = sunxi_is_composite(sunxi_display->monitor) ? 32 : 0;
> > > >       if (overscan_y == -1)
> > > > -             overscan_y = sunxi_is_composite() ? 20 : 0;
> > > > +             overscan_y = sunxi_is_composite(sunxi_display->monitor) ? 20 : 0;
> > > >
> > > > -     sunxi_display.fb_size =
> > > > -             (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff;
> > > > +     sunxi_display->fb_size = plat->size;
> > > >       overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
> > > >       /* We want to keep the fb_base for simplefb page aligned, where as
> > > >        * the sunxi dma engines will happily accept an unaligned address. */
> > > >       if (overscan_offset)
> > > > -             sunxi_display.fb_size += 0x1000;
> > > > -
> > > > -     if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) {
> > > > -             printf("Error need %dkB for fb, but only %dkB is reserved\n",
> > > > -                    sunxi_display.fb_size >> 10,
> > > > -                    CONFIG_SUNXI_MAX_FB_SIZE >> 10);
> > > > -             return NULL;
> > > > -     }
> > > > +             sunxi_display->fb_size += 0x1000;
> > > >
> > > >       printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
> > > >              mode->xres, mode->yres,
> > > >              (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
> > > > -            sunxi_get_mon_desc(sunxi_display.monitor),
> > > > +            sunxi_get_mon_desc(sunxi_display->monitor),
> > > >              overscan_x, overscan_y);
> > > >
> > > > -     gd->fb_base = gd->bd->bi_dram[0].start +
> > > > -                   gd->bd->bi_dram[0].size - sunxi_display.fb_size;
> > > > +     sunxi_display->fb_addr = plat->base;
> > > >       sunxi_engines_init();
> > > >
> > > >  #ifdef CONFIG_EFI_LOADER
> > > > -     efi_add_memory_map(gd->fb_base, sunxi_display.fb_size,
> > > > +     efi_add_memory_map(sunxi_display->fb_addr, sunxi_display->fb_size,
> > > >                          EFI_RESERVED_MEMORY_TYPE);
> > > >  #endif
> > > >
> > > > -     fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE;
> > > > -     sunxi_display.fb_addr = gd->fb_base;
> > > > +     fb_dma_addr = sunxi_display->fb_addr - CONFIG_SYS_SDRAM_BASE;
> > > >       if (overscan_offset) {
> > > >               fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
> > > > -             sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff;
> > > > -             memset((void *)gd->fb_base, 0, sunxi_display.fb_size);
> > > > -             flush_cache(gd->fb_base, sunxi_display.fb_size);
> > > > +             sunxi_display->fb_addr += (overscan_offset + 0xfff) & ~0xfff;
> > > > +             memset((void *)sunxi_display->fb_addr, 0, sunxi_display->fb_size);
> > > > +             flush_cache(sunxi_display->fb_addr, sunxi_display->fb_size);
> > > >       }
> > > > -     sunxi_mode_set(mode, fb_dma_addr);
> > > > +     sunxi_mode_set(sunxi_display, mode, fb_dma_addr);
> > > >
> > > >       /*
> > > >        * These are the only members of this structure that are used. All the
> > > >        * others are driver specific. The pitch is stored in plnSizeX.
> > > >        */
> > > > -     graphic_device->frameAdrs = sunxi_display.fb_addr;
> > > > -     graphic_device->gdfIndex = GDF_32BIT_X888RGB;
> > > > -     graphic_device->gdfBytesPP = 4;
> > > > -     graphic_device->winSizeX = mode->xres - 2 * overscan_x;
> > > > -     graphic_device->winSizeY = mode->yres - 2 * overscan_y;
> > > > -     graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP;
> > > > -
> > > > -     return graphic_device;
> > > > +     uc_priv->bpix = VIDEO_BPP32;
> > > > +     uc_priv->xsize = mode->xres;
> > > > +     uc_priv->ysize = mode->yres;
> > > > +
> > > > +     video_set_flush_dcache(dev, 1);  
> > >
> > > This is new. You should explain in a separate patch why the flush_cache
> > > above isn't enough.  
> > 
> > This is new and but required when for DM_VIDEO conversion.
> > video-uclass has a common way to flush video via video_sync so the
> > driver has to trigger this flush and without this, the vidconsole
> > shows inconsistent characters.  
> 
> I still believe this is redundant with the flush_cache above.

So this video_set_flush_dcache() does not *do* an actual cache_clean, it
merely enables regular cache cleans in video-uclass.c:video_sync().
This replaces the automatic determination of
CONFIG_VIDEO's cfb_do_flush_cache in cfb_console.c, with an explicit
request.
The initial cache clean of the framebuffer above is independent of this.
sunxi_de2 does this video_set_flush_dcache() call as well.

Cheers,
Andre
diff mbox series

Patch

diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index be0822bfb7..7d2fb55ff0 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -758,8 +758,10 @@  config VIDEO_SUNXI
 	depends on !MACH_SUN9I
 	depends on !MACH_SUN50I
 	depends on !MACH_SUN50I_H6
-	select VIDEO
+	select DM_VIDEO
+	select DISPLAY
 	imply VIDEO_DT_SIMPLEFB
+	imply CMD_BMP
 	default y
 	---help---
 	Say Y here to add support for using a cfb console on the HDMI, LCD
diff --git a/drivers/video/sunxi/sunxi_display.c b/drivers/video/sunxi/sunxi_display.c
index f52aba4d21..fb51b0c5ba 100644
--- a/drivers/video/sunxi/sunxi_display.c
+++ b/drivers/video/sunxi/sunxi_display.c
@@ -7,6 +7,8 @@ 
  */
 
 #include <common.h>
+#include <display.h>
+#include <dm.h>
 #include <cpu_func.h>
 #include <efi_loader.h>
 #include <init.h>
@@ -28,7 +30,9 @@ 
 #include <fdt_support.h>
 #include <i2c.h>
 #include <malloc.h>
+#include <video.h>
 #include <video_fb.h>
+#include <dm/uclass-internal.h>
 #include "../videomodes.h"
 #include "../anx9804.h"
 #include "../hitachi_tx18d42vm_lcd.h"
@@ -45,6 +49,13 @@ 
 
 DECLARE_GLOBAL_DATA_PTR;
 
+enum {
+	/* Maximum LCD size we support */
+	LCD_MAX_WIDTH		= 3840,
+	LCD_MAX_HEIGHT		= 2160,
+	LCD_MAX_LOG2_BPP	= VIDEO_BPP32,
+};
+
 enum sunxi_monitor {
 	sunxi_monitor_none,
 	sunxi_monitor_dvi,
@@ -59,12 +70,11 @@  enum sunxi_monitor {
 #define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc
 
 struct sunxi_display {
-	GraphicDevice graphic_device;
 	enum sunxi_monitor monitor;
 	unsigned int depth;
 	unsigned int fb_addr;
 	unsigned int fb_size;
-} sunxi_display;
+};
 
 const struct ctfb_res_modes composite_video_modes[2] = {
 	/*  x     y  hz  pixclk ps/kHz   le   ri  up  lo   hs vs  s  vmode */
@@ -214,7 +224,8 @@  static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
 	return r;
 }
 
-static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode,
+static int sunxi_hdmi_edid_get_mode(struct sunxi_display *sunxi_display,
+				    struct ctfb_res_modes *mode,
 				    bool verbose_mode)
 {
 	struct edid1_info edid1;
@@ -291,14 +302,14 @@  static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode,
 	}
 
 	/* Check for basic audio support, if found enable hdmi output */
-	sunxi_display.monitor = sunxi_monitor_dvi;
+	sunxi_display->monitor = sunxi_monitor_dvi;
 	for (i = 0; i < ext_blocks; i++) {
 		if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
 		    cea681[i].revision < 2)
 			continue;
 
 		if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
-			sunxi_display.monitor = sunxi_monitor_hdmi;
+			sunxi_display->monitor = sunxi_monitor_hdmi;
 	}
 
 	return 0;
@@ -414,9 +425,9 @@  static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
 static void sunxi_frontend_enable(void) {}
 #endif
 
-static bool sunxi_is_composite(void)
+static bool sunxi_is_composite(enum sunxi_monitor monitor)
 {
-	switch (sunxi_display.monitor) {
+	switch (monitor) {
 	case sunxi_monitor_none:
 	case sunxi_monitor_dvi:
 	case sunxi_monitor_hdmi:
@@ -473,7 +484,8 @@  static const u32 sunxi_rgb2yuv_coef[12] = {
 };
 
 static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
-				    unsigned int address)
+				    unsigned int address,
+				    enum sunxi_monitor monitor)
 {
 	struct sunxi_de_be_reg * const de_be =
 		(struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
@@ -502,7 +514,7 @@  static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
 #endif
 			     SUNXI_DE_BE_MODE_INTERLACE_ENABLE);
 
-	if (sunxi_is_composite()) {
+	if (sunxi_is_composite(monitor)) {
 		writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE,
 		       &de_be->output_color_ctrl);
 		for (i = 0; i < 12; i++)
@@ -616,7 +628,8 @@  static void sunxi_lcdc_backlight_enable(void)
 		gpio_direction_output(pin, PWM_ON);
 }
 
-static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
+static void sunxi_lcdc_tcon0_mode_set(struct sunxi_display *sunxi_display,
+				      const struct ctfb_res_modes *mode,
 				      bool for_ext_vga_dac)
 {
 	struct sunxi_lcdc_reg * const lcdc =
@@ -643,17 +656,18 @@  static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
 	}
 
 	lcdc_pll_set(ccm, 0, mode->pixclock_khz, &clk_div, &clk_double,
-		     sunxi_is_composite());
+		     sunxi_is_composite(sunxi_display->monitor));
 
 	video_ctfb_mode_to_display_timing(mode, &timing);
 	lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
-			    sunxi_display.depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
+			    sunxi_display->depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
 }
 
 #if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
 static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
 				      int *clk_div, int *clk_double,
-				      bool use_portd_hvsync)
+				      bool use_portd_hvsync,
+				      enum sunxi_monitor monitor)
 {
 	struct sunxi_lcdc_reg * const lcdc =
 		(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
@@ -663,7 +677,7 @@  static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
 
 	video_ctfb_mode_to_display_timing(mode, &timing);
 	lcdc_tcon1_mode_set(lcdc, &timing, use_portd_hvsync,
-			    sunxi_is_composite());
+			    sunxi_is_composite(monitor));
 
 	if (use_portd_hvsync) {
 		sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD_LCD0);
@@ -671,7 +685,7 @@  static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
 	}
 
 	lcdc_pll_set(ccm, 1, mode->pixclock_khz, clk_div, clk_double,
-		     sunxi_is_composite());
+		     sunxi_is_composite(monitor));
 }
 #endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */
 
@@ -725,7 +739,8 @@  static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
 }
 
 static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
-				int clk_div, int clk_double)
+				int clk_div, int clk_double,
+				enum sunxi_monitor monitor)
 {
 	struct sunxi_hdmi_reg * const hdmi =
 		(struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
@@ -734,7 +749,7 @@  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 (sunxi_display.monitor == sunxi_monitor_hdmi)
+	if (monitor == sunxi_monitor_hdmi)
 		sunxi_hdmi_setup_info_frames(mode);
 
 	/* Set input sync enable */
@@ -789,7 +804,7 @@  static void sunxi_hdmi_enable(void)
 
 #if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
 
-static void sunxi_tvencoder_mode_set(void)
+static void sunxi_tvencoder_mode_set(enum sunxi_monitor monitor)
 {
 	struct sunxi_ccm_reg * const ccm =
 		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
@@ -801,7 +816,7 @@  static void sunxi_tvencoder_mode_set(void)
 	/* Clock on */
 	setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
 
-	switch (sunxi_display.monitor) {
+	switch (monitor) {
 	case sunxi_monitor_vga:
 		tvencoder_mode_set(tve, tve_mode_vga);
 		break;
@@ -896,26 +911,28 @@  static void sunxi_engines_init(void)
 	sunxi_drc_init();
 }
 
-static void sunxi_mode_set(const struct ctfb_res_modes *mode,
+static void sunxi_mode_set(struct sunxi_display *sunxi_display,
+			   const struct ctfb_res_modes *mode,
 			   unsigned int address)
 {
+	enum sunxi_monitor monitor = sunxi_display->monitor;
 	int __maybe_unused clk_div, clk_double;
 	struct sunxi_lcdc_reg * const lcdc =
 		(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
 	struct sunxi_tve_reg * __maybe_unused const tve =
 		(struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
 
-	switch (sunxi_display.monitor) {
+	switch (sunxi_display->monitor) {
 	case sunxi_monitor_none:
 		break;
 	case sunxi_monitor_dvi:
 	case sunxi_monitor_hdmi:
 #ifdef CONFIG_VIDEO_HDMI
-		sunxi_composer_mode_set(mode, address);
-		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
-		sunxi_hdmi_mode_set(mode, clk_div, clk_double);
+		sunxi_composer_mode_set(mode, address, monitor);
+		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0, monitor);
+		sunxi_hdmi_mode_set(mode, clk_div, clk_double, monitor);
 		sunxi_composer_enable();
-		lcdc_enable(lcdc, sunxi_display.depth);
+		lcdc_enable(lcdc, sunxi_display->depth);
 		sunxi_hdmi_enable();
 #endif
 		break;
@@ -930,7 +947,7 @@  static void sunxi_mode_set(const struct ctfb_res_modes *mode,
 			axp_set_eldo(3, 1800);
 			anx9804_init(CONFIG_VIDEO_LCD_I2C_BUS, 4,
 				     ANX9804_DATA_RATE_1620M,
-				     sunxi_display.depth);
+				     sunxi_display->depth);
 		}
 		if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
 			mdelay(50); /* Wait for lcd controller power on */
@@ -942,10 +959,10 @@  static void sunxi_mode_set(const struct ctfb_res_modes *mode,
 			i2c_reg_write(0x5c, 0x04, 0x42); /* Turn on the LCD */
 			i2c_set_bus_num(orig_i2c_bus);
 		}
-		sunxi_composer_mode_set(mode, address);
-		sunxi_lcdc_tcon0_mode_set(mode, false);
+		sunxi_composer_mode_set(mode, address, monitor);
+		sunxi_lcdc_tcon0_mode_set(sunxi_display, mode, false);
 		sunxi_composer_enable();
-		lcdc_enable(lcdc, sunxi_display.depth);
+		lcdc_enable(lcdc, sunxi_display->depth);
 #ifdef CONFIG_VIDEO_LCD_SSD2828
 		sunxi_ssd2828_init(mode);
 #endif
@@ -953,17 +970,17 @@  static void sunxi_mode_set(const struct ctfb_res_modes *mode,
 		break;
 	case sunxi_monitor_vga:
 #ifdef CONFIG_VIDEO_VGA
-		sunxi_composer_mode_set(mode, address);
-		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
-		sunxi_tvencoder_mode_set();
+		sunxi_composer_mode_set(mode, address, monitor);
+		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1, monitor);
+		sunxi_tvencoder_mode_set(monitor);
 		sunxi_composer_enable();
-		lcdc_enable(lcdc, sunxi_display.depth);
+		lcdc_enable(lcdc, sunxi_display->depth);
 		tvencoder_enable(tve);
 #elif defined CONFIG_VIDEO_VGA_VIA_LCD
-		sunxi_composer_mode_set(mode, address);
-		sunxi_lcdc_tcon0_mode_set(mode, true);
+		sunxi_composer_mode_set(mode, address, monitor);
+		sunxi_lcdc_tcon0_mode_set(sunxi_display, mode, true);
 		sunxi_composer_enable();
-		lcdc_enable(lcdc, sunxi_display.depth);
+		lcdc_enable(lcdc, sunxi_display->depth);
 		sunxi_vga_external_dac_enable();
 #endif
 		break;
@@ -972,11 +989,11 @@  static void sunxi_mode_set(const struct ctfb_res_modes *mode,
 	case sunxi_monitor_composite_pal_m:
 	case sunxi_monitor_composite_pal_nc:
 #ifdef CONFIG_VIDEO_COMPOSITE
-		sunxi_composer_mode_set(mode, address);
-		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
-		sunxi_tvencoder_mode_set();
+		sunxi_composer_mode_set(mode, address, monitor);
+		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0, monitor);
+		sunxi_tvencoder_mode_set(monitor);
 		sunxi_composer_enable();
-		lcdc_enable(lcdc, sunxi_display.depth);
+		lcdc_enable(lcdc, sunxi_display->depth);
 		tvencoder_enable(tve);
 #endif
 		break;
@@ -999,11 +1016,6 @@  static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
 	}
 }
 
-ulong board_get_usable_ram_top(ulong total_size)
-{
-	return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE;
-}
-
 static bool sunxi_has_hdmi(void)
 {
 #ifdef CONFIG_VIDEO_HDMI
@@ -1052,9 +1064,11 @@  static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
 		return sunxi_monitor_none;
 }
 
-void *video_hw_init(void)
+static int sunxi_de_probe(struct udevice *dev)
 {
-	static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
+	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+	struct sunxi_display *sunxi_display = dev_get_priv(dev);
 	const struct ctfb_res_modes *mode;
 	struct ctfb_res_modes custom;
 	const char *options;
@@ -1067,10 +1081,8 @@  void *video_hw_init(void)
 	char mon[16];
 	char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
 
-	memset(&sunxi_display, 0, sizeof(struct sunxi_display));
-
 	video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
-				 &sunxi_display.depth, &options);
+				 &sunxi_display->depth, &options);
 #ifdef CONFIG_VIDEO_HDMI
 	hpd = video_get_option_int(options, "hpd", 1);
 	hpd_delay = video_get_option_int(options, "hpd_delay", 500);
@@ -1078,35 +1090,35 @@  void *video_hw_init(void)
 #endif
 	overscan_x = video_get_option_int(options, "overscan_x", -1);
 	overscan_y = video_get_option_int(options, "overscan_y", -1);
-	sunxi_display.monitor = sunxi_get_default_mon(true);
+	sunxi_display->monitor = sunxi_get_default_mon(true);
 	video_get_option_string(options, "monitor", mon, sizeof(mon),
-				sunxi_get_mon_desc(sunxi_display.monitor));
+				sunxi_get_mon_desc(sunxi_display->monitor));
 	for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
 		if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
-			sunxi_display.monitor = i;
+			sunxi_display->monitor = i;
 			break;
 		}
 	}
 	if (i > SUNXI_MONITOR_LAST)
 		printf("Unknown monitor: '%s', falling back to '%s'\n",
-		       mon, sunxi_get_mon_desc(sunxi_display.monitor));
+		       mon, sunxi_get_mon_desc(sunxi_display->monitor));
 
 #ifdef CONFIG_VIDEO_HDMI
 	/* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
-	if (sunxi_display.monitor == sunxi_monitor_dvi ||
-	    sunxi_display.monitor == sunxi_monitor_hdmi) {
+	if (sunxi_display->monitor == sunxi_monitor_dvi ||
+	    sunxi_display->monitor == sunxi_monitor_hdmi) {
 		/* Always call hdp_detect, as it also enables clocks, etc. */
 		hdmi_present = (sunxi_hdmi_hpd_detect(hpd_delay) == 1);
 		if (hdmi_present && edid) {
 			printf("HDMI connected: ");
-			if (sunxi_hdmi_edid_get_mode(&custom, true) == 0)
+			if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, true) == 0)
 				mode = &custom;
 			else
 				hdmi_present = false;
 		}
 		/* Fall back to EDID in case HPD failed */
 		if (edid && !hdmi_present) {
-			if (sunxi_hdmi_edid_get_mode(&custom, false) == 0) {
+			if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, false) == 0) {
 				mode = &custom;
 				hdmi_present = true;
 			}
@@ -1114,38 +1126,39 @@  void *video_hw_init(void)
 		/* Shut down when display was not found */
 		if ((hpd || edid) && !hdmi_present) {
 			sunxi_hdmi_shutdown();
-			sunxi_display.monitor = sunxi_get_default_mon(false);
+			sunxi_display->monitor = sunxi_get_default_mon(false);
 		} /* else continue with hdmi/dvi without a cable connected */
 	}
 #endif
 
-	switch (sunxi_display.monitor) {
+	switch (sunxi_display->monitor) {
 	case sunxi_monitor_none:
-		return NULL;
+		printf("Unknown monitor\n");
+		return -EINVAL;
 	case sunxi_monitor_dvi:
 	case sunxi_monitor_hdmi:
 		if (!sunxi_has_hdmi()) {
 			printf("HDMI/DVI not supported on this board\n");
-			sunxi_display.monitor = sunxi_monitor_none;
-			return NULL;
+			sunxi_display->monitor = sunxi_monitor_none;
+			return -EINVAL;
 		}
 		break;
 	case sunxi_monitor_lcd:
 		if (!sunxi_has_lcd()) {
 			printf("LCD not supported on this board\n");
-			sunxi_display.monitor = sunxi_monitor_none;
-			return NULL;
+			sunxi_display->monitor = sunxi_monitor_none;
+			return -EINVAL;
 		}
-		sunxi_display.depth = video_get_params(&custom, lcd_mode);
+		sunxi_display->depth = video_get_params(&custom, lcd_mode);
 		mode = &custom;
 		break;
 	case sunxi_monitor_vga:
 		if (!sunxi_has_vga()) {
 			printf("VGA not supported on this board\n");
-			sunxi_display.monitor = sunxi_monitor_none;
-			return NULL;
+			sunxi_display->monitor = sunxi_monitor_none;
+			return -EINVAL;
 		}
-		sunxi_display.depth = 18;
+		sunxi_display->depth = 18;
 		break;
 	case sunxi_monitor_composite_pal:
 	case sunxi_monitor_composite_ntsc:
@@ -1153,85 +1166,103 @@  void *video_hw_init(void)
 	case sunxi_monitor_composite_pal_nc:
 		if (!sunxi_has_composite()) {
 			printf("Composite video not supported on this board\n");
-			sunxi_display.monitor = sunxi_monitor_none;
-			return NULL;
+			sunxi_display->monitor = sunxi_monitor_none;
+			return -EINVAL;
 		}
-		if (sunxi_display.monitor == sunxi_monitor_composite_pal ||
-		    sunxi_display.monitor == sunxi_monitor_composite_pal_nc)
+		if (sunxi_display->monitor == sunxi_monitor_composite_pal ||
+		    sunxi_display->monitor == sunxi_monitor_composite_pal_nc)
 			mode = &composite_video_modes[0];
 		else
 			mode = &composite_video_modes[1];
-		sunxi_display.depth = 24;
+		sunxi_display->depth = 24;
 		break;
 	}
 
 	/* Yes these defaults are quite high, overscan on composite sucks... */
 	if (overscan_x == -1)
-		overscan_x = sunxi_is_composite() ? 32 : 0;
+		overscan_x = sunxi_is_composite(sunxi_display->monitor) ? 32 : 0;
 	if (overscan_y == -1)
-		overscan_y = sunxi_is_composite() ? 20 : 0;
+		overscan_y = sunxi_is_composite(sunxi_display->monitor) ? 20 : 0;
 
-	sunxi_display.fb_size =
-		(mode->xres * mode->yres * 4 + 0xfff) & ~0xfff;
+	sunxi_display->fb_size = plat->size;
 	overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
 	/* We want to keep the fb_base for simplefb page aligned, where as
 	 * the sunxi dma engines will happily accept an unaligned address. */
 	if (overscan_offset)
-		sunxi_display.fb_size += 0x1000;
-
-	if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) {
-		printf("Error need %dkB for fb, but only %dkB is reserved\n",
-		       sunxi_display.fb_size >> 10,
-		       CONFIG_SUNXI_MAX_FB_SIZE >> 10);
-		return NULL;
-	}
+		sunxi_display->fb_size += 0x1000;
 
 	printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
 	       mode->xres, mode->yres,
 	       (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
-	       sunxi_get_mon_desc(sunxi_display.monitor),
+	       sunxi_get_mon_desc(sunxi_display->monitor),
 	       overscan_x, overscan_y);
 
-	gd->fb_base = gd->bd->bi_dram[0].start +
-		      gd->bd->bi_dram[0].size - sunxi_display.fb_size;
+	sunxi_display->fb_addr = plat->base;
 	sunxi_engines_init();
 
 #ifdef CONFIG_EFI_LOADER
-	efi_add_memory_map(gd->fb_base, sunxi_display.fb_size,
+	efi_add_memory_map(sunxi_display->fb_addr, sunxi_display->fb_size,
 			   EFI_RESERVED_MEMORY_TYPE);
 #endif
 
-	fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE;
-	sunxi_display.fb_addr = gd->fb_base;
+	fb_dma_addr = sunxi_display->fb_addr - CONFIG_SYS_SDRAM_BASE;
 	if (overscan_offset) {
 		fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
-		sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff;
-		memset((void *)gd->fb_base, 0, sunxi_display.fb_size);
-		flush_cache(gd->fb_base, sunxi_display.fb_size);
+		sunxi_display->fb_addr += (overscan_offset + 0xfff) & ~0xfff;
+		memset((void *)sunxi_display->fb_addr, 0, sunxi_display->fb_size);
+		flush_cache(sunxi_display->fb_addr, sunxi_display->fb_size);
 	}
-	sunxi_mode_set(mode, fb_dma_addr);
+	sunxi_mode_set(sunxi_display, mode, fb_dma_addr);
 
 	/*
 	 * These are the only members of this structure that are used. All the
 	 * others are driver specific. The pitch is stored in plnSizeX.
 	 */
-	graphic_device->frameAdrs = sunxi_display.fb_addr;
-	graphic_device->gdfIndex = GDF_32BIT_X888RGB;
-	graphic_device->gdfBytesPP = 4;
-	graphic_device->winSizeX = mode->xres - 2 * overscan_x;
-	graphic_device->winSizeY = mode->yres - 2 * overscan_y;
-	graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP;
-
-	return graphic_device;
+	uc_priv->bpix = VIDEO_BPP32;
+	uc_priv->xsize = mode->xres;
+	uc_priv->ysize = mode->yres;
+
+	video_set_flush_dcache(dev, 1);
+
+	return 0;
+}
+
+static int sunxi_de_bind(struct udevice *dev)
+{
+	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+
+	plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
+		     (1 << LCD_MAX_LOG2_BPP) / 8;
+
+	return 0;
 }
 
+static const struct video_ops sunxi_de_ops = {
+};
+
+U_BOOT_DRIVER(sunxi_de) = {
+	.name	= "sunxi_de",
+	.id	= UCLASS_VIDEO,
+	.ops	= &sunxi_de_ops,
+	.bind	= sunxi_de_bind,
+	.probe	= sunxi_de_probe,
+	.priv_auto_alloc_size = sizeof(struct sunxi_display),
+	.flags	= DM_FLAG_PRE_RELOC,
+};
+
+U_BOOT_DEVICE(sunxi_de) = {
+	.name = "sunxi_de"
+};
+
 /*
  * Simplefb support.
  */
 #if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB)
 int sunxi_simplefb_setup(void *blob)
 {
-	static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
+	struct sunxi_display *sunxi_display;
+	struct video_priv *uc_priv;
+	struct udevice *de;
 	int offset, ret;
 	u64 start, size;
 	const char *pipeline = NULL;
@@ -1242,7 +1273,19 @@  int sunxi_simplefb_setup(void *blob)
 #define PIPELINE_PREFIX
 #endif
 
-	switch (sunxi_display.monitor) {
+	ret = uclass_find_device_by_name(UCLASS_VIDEO, "sunxi_de", &de);
+	if (ret) {
+		printf("DE not present\n");
+		return 0;
+	} else if (!device_active(de)) {
+		printf("DE is present but not probed\n");
+		return 0;
+	}
+
+	uc_priv = dev_get_uclass_priv(de);
+	sunxi_display = dev_get_priv(de);
+
+	switch (sunxi_display->monitor) {
 	case sunxi_monitor_none:
 		return 0;
 	case sunxi_monitor_dvi:
@@ -1280,16 +1323,17 @@  int sunxi_simplefb_setup(void *blob)
 	 * linux/arch/arm/mm/ioremap.c around line 301.
 	 */
 	start = gd->bd->bi_dram[0].start;
-	size = gd->bd->bi_dram[0].size - sunxi_display.fb_size;
+	size = gd->bd->bi_dram[0].size - sunxi_display->fb_size;
 	ret = fdt_fixup_memory_banks(blob, &start, &size, 1);
 	if (ret) {
 		eprintf("Cannot setup simplefb: Error reserving memory\n");
 		return ret;
 	}
 
-	ret = fdt_setup_simplefb_node(blob, offset, sunxi_display.fb_addr,
-			graphic_device->winSizeX, graphic_device->winSizeY,
-			graphic_device->plnSizeX, "x8r8g8b8");
+	ret = fdt_setup_simplefb_node(blob, offset, sunxi_display->fb_addr,
+				      uc_priv->xsize, uc_priv->ysize,
+				      VNBYTES(uc_priv->bpix) * uc_priv->xsize,
+				      "x8r8g8b8");
 	if (ret)
 		eprintf("Cannot setup simplefb: Error setting properties\n");
 
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h
index 308d7a42aa..affedda46c 100644
--- a/include/configs/sunxi-common.h
+++ b/include/configs/sunxi-common.h
@@ -253,23 +253,6 @@  extern int soft_i2c_gpio_scl;
 #endif
 #endif /* ifdef CONFIG_REQUIRE_SERIAL_CONSOLE */
 
-#ifdef CONFIG_VIDEO_SUNXI
-/*
- * The amount of RAM to keep free at the top of RAM when relocating u-boot,
- * to use as framebuffer. This must be a multiple of 4096.
- */
-#define CONFIG_SUNXI_MAX_FB_SIZE (16 << 20)
-
-#define CONFIG_VIDEO_LOGO
-#define CONFIG_VIDEO_STD_TIMINGS
-#define CONFIG_I2C_EDID
-#define VIDEO_LINE_LEN (pGD->plnSizeX)
-
-/* allow both serial and cfb console. */
-/* stop x86 thinking in cfbconsole from trying to init a pc keyboard */
-
-#endif /* CONFIG_VIDEO_SUNXI */
-
 #if CONFIG_IS_ENABLED(CMD_BMP)
 # define CONFIG_VIDEO_BMP_RLE8
 # define CONFIG_SPLASH_SCREEN
diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt
index f6bf6f2474..30a101638e 100644
--- a/scripts/config_whitelist.txt
+++ b/scripts/config_whitelist.txt
@@ -1724,7 +1724,6 @@  CONFIG_STV0991
 CONFIG_STV0991_HZ
 CONFIG_STV0991_HZ_CLOCK
 CONFIG_ST_SMI
-CONFIG_SUNXI_MAX_FB_SIZE
 CONFIG_SXNI855T
 CONFIG_SYSFLAGS_ADDR
 CONFIG_SYSFS