On Mon, May 15, 2023 at 10:49:43AM +0800, Kever Yang wrote: > Hi Chris, > > Please split this patch into a series with:
Thanks, sorry. I probably should have done it this way to begin with. > > - driver update > > - dts update > > - config update. > > > Thanks, > > - Kever > > On 2023/5/13 00:34, Chris Morgan wrote: > > From: Chris Morgan <macromor...@hotmail.com> > > > > Add support for panel auto detection for the Anbernic RGxx3 series. > > This requires us to probe the DSI and DSI-DPHY controllers so that > > we may send a MIPI_DCS_GET_DISPLAY_ID command to the panel to find > > out which panel we are running. This requires creating a kind of > > "skeleton" driver for the panel so we have just enough information > > about it to issue the necessary command. > > > > Once the panel type is determined the panel type is saved as an > > environment variable, and additionally the panel compatible string > > is automatically fixed-up in the devicetree if it is found to be > > incompatible. There is a table of panel IDs and compatible strings, > > support for a new panel only requires knowing the ID and the string. > > > > Additionally, the device auto detection was changed so that it > > mimicks the panel auto detection, requiring only defining a few > > new values to add support for a new compatible board. This is done > > while also adding a new board, the RG353PS. > > > > Tested the auto detection on an RG353P, RG353PS (2nd revision panel), > > RG353V, RG353V (2nd revision panel), RG353M, and RG503. As long as > > the correct devicetrees were located in ${boot}/rockchip/. U-Boot > > was able to automatically detect the correct board and panel and > > boot each device correctly. > > > > Signed-off-by: Chris Morgan <macromor...@hotmail.com> > > --- > > .../arm/dts/rk3566-anbernic-rgxx3-u-boot.dtsi | 10 + > > board/anbernic/rgxx3_rk3566/rgxx3-rk3566.c | 322 +++++++++++++++--- > > configs/anbernic-rgxx3_defconfig | 15 + > > 3 files changed, 307 insertions(+), 40 deletions(-) > > > > diff --git a/arch/arm/dts/rk3566-anbernic-rgxx3-u-boot.dtsi > > b/arch/arm/dts/rk3566-anbernic-rgxx3-u-boot.dtsi > > index a18e5d1cf7..f986e1941e 100644 > > --- a/arch/arm/dts/rk3566-anbernic-rgxx3-u-boot.dtsi > > +++ b/arch/arm/dts/rk3566-anbernic-rgxx3-u-boot.dtsi > > @@ -46,7 +46,17 @@ > > <&pmucru CLK_RTC32K_FRAC>; > > }; > > +&dsi_dphy0 { > > + status = "okay"; > > +}; > > + > > +&dsi0 { > > + status = "okay"; > > +}; > > + > > &i2c2 { > > + pinctrl-0 = <&i2c2m1_xfer>; > > + pinctrl-names = "default"; > > status = "okay"; > > }; > > diff --git a/board/anbernic/rgxx3_rk3566/rgxx3-rk3566.c > > b/board/anbernic/rgxx3_rk3566/rgxx3-rk3566.c > > index decc46db78..027f4872bb 100644 > > --- a/board/anbernic/rgxx3_rk3566/rgxx3-rk3566.c > > +++ b/board/anbernic/rgxx3_rk3566/rgxx3-rk3566.c > > @@ -6,25 +6,37 @@ > > #include <abuf.h> > > #include <adc.h> > > #include <asm/io.h> > > +#include <display.h> > > #include <dm.h> > > +#include <dm/lists.h> > > +#include <env.h> > > +#include <fdt_support.h> > > #include <linux/delay.h> > > +#include <mipi_dsi.h> > > +#include <mmc.h> > > +#include <panel.h> > > #include <pwm.h> > > #include <rng.h> > > #include <stdlib.h> > > -#include <mmc.h> > > -#include <env.h> > > +#include <video_bridge.h> > > #define GPIO0_BASE 0xfdd60000 > > +#define GPIO4_BASE 0xfe770000 > > +#define GPIO_SWPORT_DR_L 0x0000 > > #define GPIO_SWPORT_DR_H 0x0004 > > +#define GPIO_SWPORT_DDR_L 0x0008 > > #define GPIO_SWPORT_DDR_H 0x000c > > -#define GPIO_A5 BIT(5) > > -#define GPIO_A6 BIT(6) > > +#define GPIO_A0 BIT(0) > > +#define GPIO_C5 BIT(5) > > +#define GPIO_C6 BIT(6) > > +#define GPIO_C7 BIT(7) > > #define GPIO_WRITEMASK(bits) ((bits) << 16) > > #define DTB_DIR "rockchip/" > > struct rg3xx_model { > > + const u16 adc_value; > > const char *board; > > const char *board_name; > > const char *fdtfile; > > @@ -34,49 +46,74 @@ enum rgxx3_device_id { > > RG353M, > > RG353P, > > RG353V, > > - RG353VS, > > RG503, > > + /* Devices with duplicate ADC value */ > > + RG353PS, > > + RG353VS, > > }; > > static const struct rg3xx_model rg3xx_model_details[] = { > > [RG353M] = { > > + 517, /* Observed average from device */ > > "rk3566-anbernic-rg353m", > > "RG353M", > > - DTB_DIR "rk3566-anbernic-rg353m.dtb", > > + DTB_DIR "rk3566-anbernic-rg353p.dtb", /* Identical devices */ > > }, > > [RG353P] = { > > + 860, /* Documented value of 860 */ > > "rk3566-anbernic-rg353p", > > "RG353P", > > DTB_DIR "rk3566-anbernic-rg353p.dtb", > > }, > > [RG353V] = { > > + 695, /* Observed average from device */ > > "rk3566-anbernic-rg353v", > > "RG353V", > > DTB_DIR "rk3566-anbernic-rg353v.dtb", > > }, > > - [RG353VS] = { > > - "rk3566-anbernic-rg353vs", > > - "RG353VS", > > - DTB_DIR "rk3566-anbernic-rg353vs.dtb", > > - }, > > [RG503] = { > > + 1023, /* Observed average from device */ > > "rk3566-anbernic-rg503", > > "RG503", > > DTB_DIR "rk3566-anbernic-rg503.dtb", > > }, > > + /* Devices with duplicate ADC value */ > > + [RG353PS] = { > > + 860, /* Gathered from second hand information */ > > + "rk3566-anbernic-rg353ps", > > + "RG353PS", > > + DTB_DIR "rk3566-anbernic-rg353ps.dtb", > > + }, > > + [RG353VS] = { > > + 695, /* Gathered from second hand information */ > > + "rk3566-anbernic-rg353vs", > > + "RG353VS", > > + DTB_DIR "rk3566-anbernic-rg353vs.dtb", > > + }, > > +}; > > + > > +struct rg353_panel { > > + const u16 id; > > + const char *panel_compat; > > +}; > > + > > +static const struct rg353_panel rg353_panel_details[] = { > > + { .id = 0x3052, .panel_compat = "newvision,nv3051d"}, > > + { .id = 0x3821, .panel_compat = "anbernic,rg353v-panel-v2"}, > > }; > > /* > > * Start LED very early so user knows device is on. Set color > > - * to amber. > > + * to red. > > */ > > void spl_board_init(void) > > { > > - /* Set GPIO0_A5 and GPIO0_A6 to output. */ > > - writel(GPIO_WRITEMASK(GPIO_A6 | GPIO_A5) | (GPIO_A6 | GPIO_A5), > > + /* Set GPIO0_C5, GPIO0_C6, and GPIO0_C7 to output. */ > > + writel(GPIO_WRITEMASK(GPIO_C7 | GPIO_C6 | GPIO_C5) | \ > > + (GPIO_C7 | GPIO_C6 | GPIO_C5), > > (GPIO0_BASE + GPIO_SWPORT_DDR_H)); > > - /* Set GPIO0_A5 to 0 and GPIO0_A6 to 1. */ > > - writel(GPIO_WRITEMASK(GPIO_A6 | GPIO_A5) | GPIO_A6, > > + /* Set GPIO0_C5 and GPIO_C6 to 0 and GPIO0_C7 to 1. */ > > + writel(GPIO_WRITEMASK(GPIO_C7 | GPIO_C6 | GPIO_C5) | GPIO_C7, > > (GPIO0_BASE + GPIO_SWPORT_DR_H)); > > } > > @@ -126,15 +163,159 @@ void __maybe_unused startup_buzz(void) > > pwm_set_enable(dev, 0, 0); > > } > > +/* > > + * Provide the bare minimum to identify the panel for the RG353 > > + * series. Since we don't have a working framebuffer device, no > > + * need to init the panel; just identify it and provide the > > + * clocks so we know what to set the different clock values to. > > + */ > > + > > +static const struct display_timing rg353_default_timing = { > > + .pixelclock.typ = 24150000, > > + .hactive.typ = 640, > > + .hfront_porch.typ = 40, > > + .hback_porch.typ = 80, > > + .hsync_len.typ = 2, > > + .vactive.typ = 480, > > + .vfront_porch.typ = 18, > > + .vback_porch.typ = 28, > > + .vsync_len.typ = 2, > > + .flags = DISPLAY_FLAGS_HSYNC_HIGH | > > + DISPLAY_FLAGS_VSYNC_HIGH, > > +}; > > + > > +static int anbernic_rg353_panel_get_timing(struct udevice *dev, > > + struct display_timing *timings) > > +{ > > + memcpy(timings, &rg353_default_timing, sizeof(*timings)); > > + > > + return 0; > > +} > > + > > +static int anbernic_rg353_panel_probe(struct udevice *dev) > > +{ > > + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); > > + > > + plat->lanes = 4; > > + plat->format = MIPI_DSI_FMT_RGB888; > > + plat->mode_flags = MIPI_DSI_MODE_VIDEO | > > + MIPI_DSI_MODE_VIDEO_BURST | > > + MIPI_DSI_MODE_EOT_PACKET | > > + MIPI_DSI_MODE_LPM; > > + > > + return 0; > > +} > > + > > +static const struct panel_ops anbernic_rg353_panel_ops = { > > + .get_display_timing = anbernic_rg353_panel_get_timing, > > +}; > > + > > +U_BOOT_DRIVER(anbernic_rg353_panel) = { > > + .name = "anbernic_rg353_panel", > > + .id = UCLASS_PANEL, > > + .ops = &anbernic_rg353_panel_ops, > > + .probe = anbernic_rg353_panel_probe, > > + .plat_auto = sizeof(struct mipi_dsi_panel_plat), > > +}; > > + > > +int rgxx3_detect_display(void) > > +{ > > + struct udevice *dev; > > + struct mipi_dsi_device *dsi; > > + struct mipi_dsi_panel_plat *mplat; > > + const struct rg353_panel *panel; > > + int ret = 0; > > + int i; > > + u8 panel_id[2]; > > + > > + /* > > + * Take panel out of reset status. > > + * Set GPIO4_A0 to output. > > + */ > > + writel(GPIO_WRITEMASK(GPIO_A0) | GPIO_A0, > > + (GPIO4_BASE + GPIO_SWPORT_DDR_L)); > > + /* Set GPIO4_A0 to 1. */ > > + writel(GPIO_WRITEMASK(GPIO_A0) | GPIO_A0, > > + (GPIO4_BASE + GPIO_SWPORT_DR_L)); > > + > > + /* Probe the DSI controller. */ > > + ret = uclass_get_device_by_name(UCLASS_VIDEO_BRIDGE, > > + "dsi@fe060000", &dev); > > + if (ret) { > > + printf("DSI host not probed: %d\n", ret); > > + return ret; > > + } > > + > > + /* Probe the DSI panel. */ > > + ret = device_bind_driver_to_node(dev, "anbernic_rg353_panel", > > + "anbernic_rg353_panel", > > + dev_ofnode(dev), NULL); > > + if (ret) { > > + printf("Failed to probe RG353 panel: %d\n", ret); > > + return ret; > > + } > > + > > + /* > > + * Attach the DSI controller which will also probe and attach > > + * the DSIDPHY. > > + */ > > + ret = video_bridge_attach(dev); > > + if (ret) { > > + printf("Failed to attach DSI controller: %d\n", ret); > > + return ret; > > + } > > + > > + /* > > + * Get the panel which should have already been probed by the > > + * video_bridge_attach() function. > > + */ > > + ret = uclass_first_device_err(UCLASS_PANEL, &dev); > > + if (ret) { > > + printf("Panel device error: %d\n", ret); > > + return ret; > > + } > > + > > + /* Now call the panel via DSI commands to get the panel ID. */ > > + mplat = dev_get_plat(dev); > > + dsi = mplat->device; > > + mipi_dsi_set_maximum_return_packet_size(dsi, sizeof(panel_id)); > > + ret = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_DISPLAY_ID, &panel_id, > > + sizeof(panel_id)); > > + if (ret < 0) { > > + printf("Unable to read panel ID: %d\n", ret); > > + return ret; > > + } > > + > > + /* Get the correct panel compatible from the table. */ > > + for (i = 0; i < ARRAY_SIZE(rg353_panel_details); i++) { > > + if (rg353_panel_details[i].id == ((panel_id[0] << 8) | > > + panel_id[1])) { > > + panel = &rg353_panel_details[i]; > > + break; > > + } > > + } > > + > > + if (!panel) { > > + printf("Unable to identify panel_id %x\n", > > + (panel_id[0] << 8) | panel_id[1]); > > + env_set("panel", "unknown"); > > + return -EINVAL; > > + } > > + > > + env_set("panel", panel->panel_compat); > > + > > + return 0; > > +} > > + > > /* Detect which Anbernic RGXX3 device we are using so as to load the > > * correct devicetree for Linux. Set an environment variable once > > * found. The detection depends on the value of ADC channel 1, the > > - * presence of an eMMC on mmc0, and querying the DSI panel (TODO). > > + * presence of an eMMC on mmc0, and querying the DSI panel. > > */ > > int rgxx3_detect_device(void) > > { > > u32 adc_info; > > - int ret; > > + int ret, i; > > int board_id = -ENXIO; > > struct mmc *mmc; > > @@ -144,30 +325,37 @@ int rgxx3_detect_device(void) > > return ret; > > } > > - /* Observed value 517. */ > > - if (adc_info > 505 && adc_info < 530) > > - board_id = RG353M; > > - /* Observed value 695. */ > > - if (adc_info > 680 && adc_info < 710) > > - board_id = RG353V; > > - /* Documented value 860. */ > > - if (adc_info > 850 && adc_info < 870) > > - board_id = RG353P; > > - /* Observed value 1023. */ > > - if (adc_info > 1010) > > - board_id = RG503; > > + /* > > + * Get the correct device from the table. The ADC value is > > + * determined by a resistor on ADC channel 0. The hardware > > + * design calls for no more than a 1% variance on the > > + * resistor, so assume a +- value of 15 should be enough. > > + */ > > + for (i = 0; i < ARRAY_SIZE(rg3xx_model_details); i++) { > > + u32 adc_min = rg3xx_model_details[i].adc_value - 15; > > + u32 adc_max = rg3xx_model_details[i].adc_value + 15; > > + > > + if (adc_min < adc_info && adc_max > adc_info) { > > + board_id = i; > > + break; > > + } > > + } > > /* > > - * Try to access the eMMC on an RG353V. If it's missing, it's > > - * an RG353VS. Note we could also check for a touchscreen at > > - * 0x1a on i2c2. > > + * Try to access the eMMC on an RG353V or RG353P. If it's > > + * missing, it's an RG353VS or RG353PS. Note we could also > > + * check for a touchscreen at 0x1a on i2c2. > > */ > > - if (board_id == RG353V) { > > + if (board_id == RG353V || board_id == RG353P) { > > mmc = find_mmc_device(0); > > if (mmc) { > > ret = mmc_init(mmc); > > - if (ret) > > - board_id = RG353VS; > > + if (ret) { > > + if (board_id == RG353V) > > + board_id = RG353VS; > > + else > > + board_id = RG353PS; > > + } > > } > > } > > @@ -179,6 +367,14 @@ int rgxx3_detect_device(void) > > rg3xx_model_details[board_id].board_name); > > env_set("fdtfile", rg3xx_model_details[board_id].fdtfile); > > + /* Detect the panel type for any device that isn't a 503. */ > > + if (board_id == RG503) > > + return 0; > > + > > + ret = rgxx3_detect_display(); > > + if (ret) > > + return ret; > > + > > return 0; > > } > > @@ -186,18 +382,64 @@ int rk_board_late_init(void) > > { > > int ret; > > - /* Turn off orange LED and turn on green LED. */ > > - writel(GPIO_WRITEMASK(GPIO_A6 | GPIO_A5) | GPIO_A5, > > - (GPIO0_BASE + GPIO_SWPORT_DR_H)); > > - > > ret = rgxx3_detect_device(); > > if (ret) { > > printf("Unable to detect device type: %d\n", ret); > > return ret; > > } > > + /* Turn off red LED and turn on orange LED. */ > > + writel(GPIO_WRITEMASK(GPIO_C7 | GPIO_C6 | GPIO_C5) | GPIO_C6, > > + (GPIO0_BASE + GPIO_SWPORT_DR_H)); > > + > > if (IS_ENABLED(CONFIG_DM_PWM)) > > startup_buzz(); > > return 0; > > } > > + > > +int ft_board_setup(void *blob, struct bd_info *bd) > > +{ > > + int node, ret; > > + char *env; > > + > > + /* No fixups necessary for the RG503 */ > > + env = env_get("board_name"); > > + if (env && (!strcmp(env, rg3xx_model_details[RG503].board_name))) > > + return 0; > > + > > + /* Change the model name of the RG353M */ > > + if (env && (!strcmp(env, rg3xx_model_details[RG353M].board_name))) > > + fdt_setprop(blob, 0, "model", > > + rg3xx_model_details[RG353M].board_name, > > + sizeof(rg3xx_model_details[RG353M].board_name)); > > + > > + /* > > + * Check if the environment variable doesn't equal the panel. > > + * If it doesn't, update the devicetree to the correct panel. > > + */ > > + node = fdt_path_offset(blob, "/dsi@fe060000/panel@0"); > > + if (!(node > 0)) { > > + printf("Can't find the DSI node\n"); > > + return -ENODEV; > > + } > > + > > + env = env_get("panel"); > > + if (!env) { > > + printf("Can't get panel env\n"); > > + return -ENODEV; > > + } > > + > > + ret = fdt_node_check_compatible(blob, node, env); > > + if (ret < 0) > > + return -ENODEV; > > + > > + /* Panels match, return 0. */ > > + if (!ret) > > + return 0; > > + > > + do_fixup_by_path_string(blob, "/dsi@fe060000/panel@0", > > + "compatible", env); > > + > > + return 0; > > +} > > diff --git a/configs/anbernic-rgxx3_defconfig > > b/configs/anbernic-rgxx3_defconfig > > index b17e917914..ed6643d9d4 100644 > > --- a/configs/anbernic-rgxx3_defconfig > > +++ b/configs/anbernic-rgxx3_defconfig > > @@ -12,6 +12,7 @@ CONFIG_DEFAULT_DEVICE_TREE="rk3566-anbernic-rgxx3" > > CONFIG_ROCKCHIP_RK3568=y > > CONFIG_SPL_ROCKCHIP_BACK_TO_BROM=y > > CONFIG_SPL_ROCKCHIP_COMMON_BOARD=y > > +CONFIG_ROCKCHIP_RK8XX_DISABLE_BOOT_ON_POWERON=y > > CONFIG_SPL_MMC=y > > CONFIG_SPL_SERIAL=y > > CONFIG_SPL_STACK_R_ADDR=0x600000 > > @@ -24,9 +25,13 @@ CONFIG_DEBUG_UART=y > > CONFIG_FIT=y > > CONFIG_FIT_VERBOSE=y > > CONFIG_SPL_LOAD_FIT=y > > +CONFIG_OF_BOARD_SETUP=y > > +CONFIG_OF_STDOUT_VIA_ALIAS=y > > CONFIG_DEFAULT_FDT_FILE="rockchip/rk3566-anbernic-rgxx3.dtb" > > +# CONFIG_CONSOLE_MUX is not set > > # CONFIG_DISPLAY_CPUINFO is not set > > CONFIG_DISPLAY_BOARDINFO_LATE=y > > +CONFIG_BOARD_RNG_SEED=y > > CONFIG_SPL_MAX_SIZE=0x20000 > > CONFIG_SPL_PAD_TO=0x7f8000 > > CONFIG_SPL_HAS_BSS_LINKER_SECTION=y > > @@ -41,6 +46,7 @@ CONFIG_CMD_PWM=y > > CONFIG_CMD_GPT=y > > CONFIG_CMD_MMC=y > > # CONFIG_CMD_SETEXPR is not set > > +# CONFIG_CMD_CLS is not set > > # CONFIG_SPL_DOS_PARTITION is not set > > CONFIG_SPL_OF_CONTROL=y > > CONFIG_OF_LIVE=y > > @@ -60,6 +66,7 @@ CONFIG_MMC_DW_ROCKCHIP=y > > CONFIG_MMC_SDHCI=y > > CONFIG_MMC_SDHCI_SDMA=y > > CONFIG_MMC_SDHCI_ROCKCHIP=y > > +CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY=y > > CONFIG_DM_PMIC=y > > CONFIG_DM_PMIC_FAN53555=y > > CONFIG_PMIC_RK8XX=y > > @@ -69,10 +76,18 @@ CONFIG_REGULATOR_RK8XX=y > > CONFIG_DM_REGULATOR_SCMI=y > > CONFIG_PWM_ROCKCHIP=y > > CONFIG_SPL_RAM=y > > +# CONFIG_RAM_ROCKCHIP_DEBUG is not set > > +CONFIG_DM_RNG=y > > +CONFIG_RNG_ROCKCHIP=y > > +# CONFIG_RNG_SMCCC_TRNG is not set > > CONFIG_BAUDRATE=1500000 > > CONFIG_DEBUG_UART_SHIFT=2 > > CONFIG_SYS_NS16550_MEM32=y > > CONFIG_SYSRESET=y > > +CONFIG_VIDEO=y > > +CONFIG_VIDEO_ROCKCHIP=y > > +CONFIG_DISPLAY_ROCKCHIP_DW_MIPI=y > > +CONFIG_VIDEO_BRIDGE=y > > CONFIG_REGEX=y > > CONFIG_ERRNO_STR=y > > # CONFIG_EFI_LOADER is not set