diff --git a/boards/nxp/imx93_evk/imx93_evk_mimx9352_m33_ddr.dts b/boards/nxp/imx93_evk/imx93_evk_mimx9352_m33_ddr.dts index 7c690afd3f384..69a111258ed78 100644 --- a/boards/nxp/imx93_evk/imx93_evk_mimx9352_m33_ddr.dts +++ b/boards/nxp/imx93_evk/imx93_evk_mimx9352_m33_ddr.dts @@ -1,5 +1,5 @@ /* - * Copyright 2024 NXP + * Copyright 2024-2025 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,6 +7,7 @@ /dts-v1/; #include "imx93_evk_mimx9352_m33.dts" +#include / { model = "NXP i.MX93 EVK board DDR variant"; @@ -18,6 +19,18 @@ ddr: memory@84000000 { device_type = "memory"; - reg = <0x84000000 DT_SIZE_M(4)>; + reg = <0x84000000 DT_SIZE_M(32)>; }; }; + +&lpi2c2 { + pinctrl-0 = <&i2c2_default>; + pinctrl-names = "default"; + status = "okay"; +}; + +zephyr_lcdif: &lcdif {}; + +display_i2c: &lpi2c2 {}; + +zephyr_mipi_dsi: &mipi_dsi {}; diff --git a/boards/shields/nxp_mx8_dsi_oled1a/Kconfig.defconfig b/boards/shields/nxp_mx8_dsi_oled1a/Kconfig.defconfig new file mode 100644 index 0000000000000..e277634cf673d --- /dev/null +++ b/boards/shields/nxp_mx8_dsi_oled1a/Kconfig.defconfig @@ -0,0 +1,39 @@ +# +# Copyright 2025 NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +if SHIELD_NXP_MX8_DSI_OLED1A + +config GPIO + default y + +config GPIO_ADP5585 + default y + +if DISPLAY + +if LVGL + +config LV_Z_VDB_SIZE + default 100 + +config LV_Z_DOUBLE_VDB + default y + +config LV_Z_BITS_PER_PIXEL + default 32 + +config LV_Z_FULL_REFRESH + default y + +choice LV_COLOR_DEPTH + default LV_COLOR_DEPTH_32 +endchoice + +endif # LVGL + +endif # DISPLAY + +endif # SHIELD_NXP_MX8_DSI_OLED1A diff --git a/boards/shields/nxp_mx8_dsi_oled1a/Kconfig.shield b/boards/shields/nxp_mx8_dsi_oled1a/Kconfig.shield new file mode 100644 index 0000000000000..45d0cd9fec3cb --- /dev/null +++ b/boards/shields/nxp_mx8_dsi_oled1a/Kconfig.shield @@ -0,0 +1,8 @@ +# +# Copyright 2025 NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +config SHIELD_NXP_MX8_DSI_OLED1A + def_bool $(shields_list_contains,nxp_mx8_dsi_oled1a) diff --git a/boards/shields/nxp_mx8_dsi_oled1a/doc/index.rst b/boards/shields/nxp_mx8_dsi_oled1a/doc/index.rst new file mode 100644 index 0000000000000..0098da1a42054 --- /dev/null +++ b/boards/shields/nxp_mx8_dsi_oled1a/doc/index.rst @@ -0,0 +1,49 @@ +.. _nxp_mx8_dsi_oled1a: + +NXP MX8 DSI OLED1A Panel +######################### + +Overview +******** + +The NXP MX8 DSI OLED1A shield is a high-resolution OLED display panel +designed for use with NXP i.MX8 series processors. This panel provides +excellent color reproduction and contrast ratio through OLED technology. +The display shield connects via MIPI DSI interface and offers superior visual +performance for embedded applications. + +More information about the panel can be found +at the `NXP MX8 DSI OLED1A Shield website`_. + +Current supported displays +========================== + ++--------------+------------------------------+ +| Display | Shield Designation | +| | | ++==============+==============================+ +| MX8 DSI | nxp_mx8_dsi_oled1a | +| OLED1A | | ++--------------+------------------------------+ + +Programming +*********** + +Correct shield designation (see the table above) for your display must +be entered when you invoke ``west build``. + +For example: + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/display/lvgl + :board: imx93_evk/mimx9352/m33/ddr + :shield: nxp_mx8_dsi_oled1a + :goals: build + +References +********** + +.. target-notes:: + +.. _NXP MX8 DSI OLED1A Shield website: + https://www.nxp.com/part/MX8-DSI-OLED1A diff --git a/boards/shields/nxp_mx8_dsi_oled1a/nxp_mx8_dsi_oled1a.overlay b/boards/shields/nxp_mx8_dsi_oled1a/nxp_mx8_dsi_oled1a.overlay new file mode 100644 index 0000000000000..fbc3d1da83497 --- /dev/null +++ b/boards/shields/nxp_mx8_dsi_oled1a/nxp_mx8_dsi_oled1a.overlay @@ -0,0 +1,90 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + chosen { + zephyr,display = &lcdif; + }; +}; + +&media_blk_ctrl { + status = "okay"; +}; + +&video_pll { + compatible = "nxp,imx93-video-pll"; + rdiv = <1>; + mfi = <121>; + mfn = <0>; + mfd = <1>; + odiv = <6>; + pll-frequency = <484000000>; + status = "okay"; +}; + +&zephyr_lcdif { + width = <1080>; + height = <1920>; + pixel-format = "argb-8888"; + media-axi-clk-rate = <400000000>; + media-apb-clk-rate = <133333334>; + status = "okay"; + + display-timings { + compatible = "zephyr,panel-timing"; + hsync-len = <2>; + hfront-porch = <20>; + hback-porch = <34>; + vsync-len = <2>; + vfront-porch = <10>; + vback-porch = <4>; + de-active = <0>; + pixelclk-active = <0>; + hsync-active = <1>; + vsync-active = <1>; + clock-frequency = <121000000>; + }; +}; + +&zephyr_mipi_dsi { + nxp,dc = <&lcdif>; + dpi-color-coding = "24-bit"; + dpi-video-mode = "non-burst-sync-pulse"; + dphy-ref-frequency = <24000000>; + data-rate-clock = <726000000>; + status = "okay"; + + rm67199_panel@0 { + compatible = "raydium,rm67199"; + reg = <0>; + bl-gpios = <&gpio_exp0 10 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio_exp0 9 GPIO_ACTIVE_HIGH>; + data-lanes = <4>; + pixel-format = ; + status = "okay"; + }; +}; + +&display_i2c { + status = "okay"; + + mfd0: adp5585@34 { + compatible = "adi,adp5585"; + reg = <0x34>; + status = "okay"; + + gpio_exp0: adp5585_gpio { + compatible = "adi,adp5585-gpio"; + gpio-controller; + #gpio-cells = <2>; + ngpios = <13>; + gpio-reserved-ranges = <5 3>; + status = "okay"; + }; + }; +}; diff --git a/boards/shields/nxp_mx8_dsi_oled1a/shield.yml b/boards/shields/nxp_mx8_dsi_oled1a/shield.yml new file mode 100644 index 0000000000000..41744d566a013 --- /dev/null +++ b/boards/shields/nxp_mx8_dsi_oled1a/shield.yml @@ -0,0 +1,6 @@ +shield: + name: nxp_mx8_dsi_oled1a + full_name: NXP MX8-DSI-OLED1A + vendor: nxp + supported_features: + - display diff --git a/boards/shields/waveshare_dsi_lcd/boards/frdm_imx93_mimx9352_a55.overlay b/boards/shields/waveshare_dsi_lcd/boards/frdm_imx93_mimx9352_a55.overlay index 33279d112ca97..679da8075dee8 100644 --- a/boards/shields/waveshare_dsi_lcd/boards/frdm_imx93_mimx9352_a55.overlay +++ b/boards/shields/waveshare_dsi_lcd/boards/frdm_imx93_mimx9352_a55.overlay @@ -59,3 +59,14 @@ &dsi_panel { mipi-dsi = <&mipi_dsi>; }; + +&video_pll { + compatible = "nxp,imx93-video-pll"; + rdiv = <1>; + mfi = <200>; + mfn = <0>; + mfd = <1>; + odiv = <12>; + pll-frequency = <400000000>; + status = "okay"; +}; diff --git a/drivers/display/CMakeLists.txt b/drivers/display/CMakeLists.txt index ba25fd60c2ccc..78dceec5672c7 100644 --- a/drivers/display/CMakeLists.txt +++ b/drivers/display/CMakeLists.txt @@ -37,6 +37,7 @@ zephyr_library_sources_ifdef(CONFIG_NT35510 display_nt35510.c) zephyr_library_sources_ifdef(CONFIG_OTM8009A display_otm8009a.c) zephyr_library_sources_ifdef(CONFIG_RENESAS_RA_GLCDC display_renesas_ra.c) zephyr_library_sources_ifdef(CONFIG_RM67162 display_rm67162.c) +zephyr_library_sources_ifdef(CONFIG_RM67199 display_rm67199.c) zephyr_library_sources_ifdef(CONFIG_RM68200 display_rm68200.c) zephyr_library_sources_ifdef(CONFIG_SH1122 display_sh1122.c) zephyr_library_sources_ifdef(CONFIG_SSD1306 ssd1306.c) diff --git a/drivers/display/Kconfig b/drivers/display/Kconfig index fbc48a39cc2d5..3fc27ac098684 100644 --- a/drivers/display/Kconfig +++ b/drivers/display/Kconfig @@ -45,6 +45,7 @@ source "drivers/display/Kconfig.otm8009a" source "drivers/display/Kconfig.renesas_lcdc" source "drivers/display/Kconfig.renesas_ra" source "drivers/display/Kconfig.rm67162" +source "drivers/display/Kconfig.rm67199" source "drivers/display/Kconfig.rm68200" source "drivers/display/Kconfig.sdl" source "drivers/display/Kconfig.sh1122" diff --git a/drivers/display/Kconfig.mcux_lcdifv3 b/drivers/display/Kconfig.mcux_lcdifv3 index c8599a26d980e..3cafe3558a0a4 100644 --- a/drivers/display/Kconfig.mcux_lcdifv3 +++ b/drivers/display/Kconfig.mcux_lcdifv3 @@ -7,7 +7,6 @@ menuconfig DISPLAY_MCUX_LCDIFV3 default y depends on DT_HAS_NXP_IMX_LCDIFV3_ENABLED depends on CLOCK_CONTROL - select INIT_VIDEO_PLL help Enable support for mcux LCDIFV3 driver. diff --git a/drivers/display/Kconfig.rm67199 b/drivers/display/Kconfig.rm67199 new file mode 100644 index 0000000000000..497d0184f461e --- /dev/null +++ b/drivers/display/Kconfig.rm67199 @@ -0,0 +1,10 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +config RM67199 + bool "RM67199 display controller driver" + default y + select MIPI_DSI + depends on DT_HAS_RAYDIUM_RM67199_ENABLED + help + Enable driver for RM67199 display controller. diff --git a/drivers/display/display_mcux_lcdifv3.c b/drivers/display/display_mcux_lcdifv3.c index e759f7c2b118c..8abd3c9662ae9 100644 --- a/drivers/display/display_mcux_lcdifv3.c +++ b/drivers/display/display_mcux_lcdifv3.c @@ -270,7 +270,7 @@ static int mcux_lcdifv3_init(const struct device *dev) LCDIFV3_SetLayerSize(base, 0, display_config.panelWidth, display_config.panelHeight); LCDIFV3_EnableLayer(base, 0, true); LCDIFV3_EnablePlanePanic(base); - LCDIFV3_SetLayerBufferAddr(base, 0, (uint64_t)data->fb[0]); + LCDIFV3_SetLayerBufferAddr(base, 0, (uint32_t)(uintptr_t)data->fb[0]); LCDIFV3_TriggerLayerShadowLoad(base, 0); LCDIFV3_EnableInterrupts(base, kLCDIFV3_VerticalBlankingInterrupt); diff --git a/drivers/display/display_rm67199.c b/drivers/display/display_rm67199.c new file mode 100644 index 0000000000000..5447efb5215e2 --- /dev/null +++ b/drivers/display/display_rm67199.c @@ -0,0 +1,466 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT raydium_rm67199 + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(rm67199, CONFIG_DISPLAY_LOG_LEVEL); + +/* RM67199 MIPI DSI Display Controller Commands */ + +/* Basic Commands */ +#define RM67199_NOP (0x00) +#define RM67199_SWRESET (0x01) + +/* Read Device Information */ +#define RM67199_RDDID (0x04) +#define RM67199_RDNUMED (0x05) + +/* Read Display Status */ +#define RM67199_RDDPM (0x0A) +#define RM67199_RDDMADCTR (0x0B) +#define RM67199_RDDCOLMOD (0x0C) +#define RM67199_RDDIM (0x0D) +#define RM67199_RDDSM (0x0E) +#define RM67199_RDDSDR (0x0F) + +/* Sleep Mode Control */ +#define RM67199_SLPIN (0x10) +#define RM67199_SLPOUT (0x11) + +/* Display Control */ +#define RM67199_INVOFF (0x20) +#define RM67199_INVON (0x21) +#define RM67199_ALLPOFF (0x22) +#define RM67199_DISPOFF (0x28) +#define RM67199_DISPON (0x29) + +/* Tearing Effect Control */ +#define RM67199_TEOFF (0x34) +#define RM67199_TEON (0x35) + +/* Display Configuration */ +#define RM67199_MADCTR (0x36) +#define RM67199_IDMOFF (0x38) +#define RM67199_IDMON (0x39) +#define RM67199_COLMOD (0x3A) + +/* Scan Line Control */ +#define RM67199_STES (0x44) +#define RM67199_GSL (0x45) + +/* Brightness Control */ +#define RM67199_RDDISBV (0x52) + +/* Color Enhancement */ +#define RM67199_WRCE1 (0x5A) +#define RM67199_WRCE2 (0x5C) +#define RM67199_RDCE2 (0x5D) + +/* Timer and Panel Control */ +#define RM67199_WRTMR (0x62) +#define RM67199_RDTMR (0x63) +#define RM67199_WRPA (0x64) +#define RM67199_RDPA (0x65) +#define RM67199_WRWB (0x66) +#define RM67199_RDWB (0x67) + +/* DDB and Checksum */ +#define RM67199_RDFC (0xAA) +#define RM67199_RDCC (0xAF) + +/* DSI Configuration */ +#define RM67199_SETDSIMODE (0xC2) + +/* Manufacturer Commands */ +#define RM67199_WRMAUCCTR (0xFE) + +/* + * These commands are taken from NXP's MCUXpresso SDK. + * Additional documentation is added where possible, but the + * Manufacture command set pages are not described in the datasheet + */ +static const struct { + uint8_t cmd; + uint8_t param; +} rm67199_init_setting[] = { + {.cmd = RM67199_WRMAUCCTR, .param = 0xA0}, + {.cmd = 0x2B, .param = 0x18}, + {.cmd = RM67199_WRMAUCCTR, .param = 0x70}, + {.cmd = 0x7D, .param = 0x05}, + {.cmd = RM67199_RDCE2, .param = 0x0A}, + {.cmd = RM67199_WRCE1, .param = 0x79}, + {.cmd = RM67199_WRCE2, .param = 0x00}, + {.cmd = RM67199_RDDISBV, .param = 0x00}, + {.cmd = RM67199_WRMAUCCTR, .param = 0xD0}, + {.cmd = 0x40, .param = 0x02}, + {.cmd = 0x13, .param = 0x40}, + {.cmd = RM67199_WRMAUCCTR, .param = 0x40}, + {.cmd = RM67199_RDNUMED, .param = 0x08}, + {.cmd = 0x06, .param = 0x08}, + {.cmd = 0x08, .param = 0x08}, + {.cmd = 0x09, .param = 0x08}, + {.cmd = RM67199_RDDPM, .param = 0xCA}, + {.cmd = RM67199_RDDMADCTR, .param = 0x88}, + {.cmd = RM67199_INVOFF, .param = 0x93}, + {.cmd = RM67199_INVON, .param = 0x93}, + {.cmd = 0x24, .param = 0x02}, + {.cmd = 0x26, .param = 0x02}, + {.cmd = RM67199_DISPOFF, .param = 0x05}, + {.cmd = 0x2A, .param = 0x05}, + {.cmd = 0x74, .param = 0x2F}, + {.cmd = 0x75, .param = 0x1E}, + {.cmd = 0xAD, .param = 0x00}, + {.cmd = RM67199_WRMAUCCTR, .param = 0x60}, + {.cmd = 0x00, .param = 0xCC}, + {.cmd = 0x01, .param = 0x00}, + {.cmd = 0x02, .param = 0x04}, + {.cmd = 0x03, .param = 0x00}, + {.cmd = 0x04, .param = 0x00}, + {.cmd = RM67199_RDNUMED, .param = 0x07}, + {.cmd = 0x06, .param = 0x00}, + {.cmd = 0x07, .param = 0x88}, + {.cmd = 0x08, .param = 0x00}, + {.cmd = 0x09, .param = 0xCC}, + {.cmd = RM67199_RDDPM, .param = 0x00}, + {.cmd = RM67199_RDDMADCTR, .param = 0x04}, + {.cmd = 0x0C, .param = 0x00}, + {.cmd = 0x0D, .param = 0x00}, + {.cmd = 0x0E, .param = 0x05}, + {.cmd = 0x0F, .param = 0x00}, + {.cmd = 0x10, .param = 0x88}, + {.cmd = 0x11, .param = 0x00}, + {.cmd = 0x12, .param = 0xCC}, + {.cmd = 0x13, .param = 0x0F}, + {.cmd = 0x14, .param = 0xFF}, + {.cmd = 0x15, .param = 0x04}, + {.cmd = 0x16, .param = 0x00}, + {.cmd = 0x17, .param = 0x06}, + {.cmd = 0x18, .param = 0x00}, + {.cmd = 0x19, .param = 0x96}, + {.cmd = 0x1A, .param = 0x00}, + {.cmd = 0x24, .param = 0xCC}, + {.cmd = 0x25, .param = 0x00}, + {.cmd = 0x26, .param = 0x02}, + {.cmd = 0x27, .param = 0x00}, + {.cmd = RM67199_DISPOFF, .param = 0x00}, + {.cmd = RM67199_DISPON, .param = 0x06}, + {.cmd = 0x2A, .param = 0x06}, + {.cmd = 0x2B, .param = 0x82}, + {.cmd = 0x2D, .param = 0x00}, + {.cmd = 0x2F, .param = 0xCC}, + {.cmd = 0x30, .param = 0x00}, + {.cmd = 0x31, .param = 0x02}, + {.cmd = 0x32, .param = 0x00}, + {.cmd = 0x33, .param = 0x00}, + {.cmd = RM67199_TEOFF, .param = 0x07}, + {.cmd = RM67199_TEON, .param = 0x06}, + {.cmd = RM67199_MADCTR, .param = 0x82}, + {.cmd = 0x37, .param = 0x00}, + {.cmd = RM67199_IDMOFF, .param = 0xCC}, + {.cmd = RM67199_IDMON, .param = 0x00}, + {.cmd = RM67199_COLMOD, .param = 0x02}, + {.cmd = 0x3B, .param = 0x00}, + {.cmd = 0x3D, .param = 0x00}, + {.cmd = 0x3F, .param = 0x07}, + {.cmd = 0x40, .param = 0x00}, + {.cmd = 0x41, .param = 0x88}, + {.cmd = 0x42, .param = 0x00}, + {.cmd = 0x43, .param = 0xCC}, + {.cmd = RM67199_STES, .param = 0x00}, + {.cmd = RM67199_GSL, .param = 0x02}, + {.cmd = 0x46, .param = 0x00}, + {.cmd = 0x47, .param = 0x00}, + {.cmd = 0x48, .param = 0x06}, + {.cmd = 0x49, .param = 0x02}, + {.cmd = 0x4A, .param = 0x8A}, + {.cmd = 0x4B, .param = 0x00}, + {.cmd = 0x5F, .param = 0xCA}, + {.cmd = 0x60, .param = 0x01}, + {.cmd = 0x61, .param = 0xE8}, + {.cmd = RM67199_WRTMR, .param = 0x09}, + {.cmd = RM67199_RDTMR, .param = 0x00}, + {.cmd = RM67199_WRPA, .param = 0x07}, + {.cmd = RM67199_RDPA, .param = 0x00}, + {.cmd = RM67199_WRWB, .param = 0x30}, + {.cmd = RM67199_RDWB, .param = 0x80}, + {.cmd = 0x9B, .param = 0x03}, + {.cmd = 0xA9, .param = 0x07}, + {.cmd = RM67199_RDFC, .param = 0x06}, + {.cmd = 0xAB, .param = 0x02}, + {.cmd = 0xAC, .param = 0x10}, + {.cmd = 0xAD, .param = 0x11}, + {.cmd = 0xAE, .param = 0x05}, + {.cmd = RM67199_RDCC, .param = 0x04}, + {.cmd = 0xB0, .param = 0x10}, + {.cmd = 0xB1, .param = 0x10}, + {.cmd = 0xB2, .param = 0x10}, + {.cmd = 0xB3, .param = 0x10}, + {.cmd = 0xB4, .param = 0x10}, + {.cmd = 0xB5, .param = 0x10}, + {.cmd = 0xB6, .param = 0x10}, + {.cmd = 0xB7, .param = 0x10}, + {.cmd = 0xB8, .param = 0x10}, + {.cmd = 0xB9, .param = 0x10}, + {.cmd = 0xBA, .param = 0x04}, + {.cmd = 0xBB, .param = 0x05}, + {.cmd = 0xBC, .param = 0x00}, + {.cmd = 0xBD, .param = 0x01}, + {.cmd = 0xBE, .param = 0x0A}, + {.cmd = 0xBF, .param = 0x10}, + {.cmd = 0xC0, .param = 0x11}, + {.cmd = RM67199_WRMAUCCTR, .param = 0xA0}, + {.cmd = RM67199_ALLPOFF, .param = 0x00}, +}; + +struct rm67199_config { + const struct device *mipi_dsi; + uint8_t channel; + uint8_t num_of_lanes; + const struct gpio_dt_spec reset_gpio; + const struct gpio_dt_spec bl_gpio; +}; + +struct rm67199_data { + uint8_t pixel_format; + uint8_t bytes_per_pixel; + struct k_sem te_sem; +}; + +static int rm67199_init(const struct device *dev) +{ + const struct rm67199_config *config = dev->config; + struct rm67199_data *data = dev->data; + struct mipi_dsi_device mdev = {0}; + int ret; + uint32_t i; + uint8_t buf[2]; + + LOG_INF("starting RM67199 init"); + + /* Attach to MIPI DSI host */ + mdev.data_lanes = config->num_of_lanes; + mdev.pixfmt = data->pixel_format; + mdev.mode_flags = MIPI_DSI_MODE_VIDEO; + + ret = mipi_dsi_attach(config->mipi_dsi, config->channel, &mdev); + if (ret < 0) { + LOG_ERR("Could not attach to MIPI-DSI host"); + return ret; + } + + if (config->reset_gpio.port != NULL) { + ret = gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_INACTIVE); + if (ret < 0) { + LOG_ERR("Could not configure reset GPIO (%d)", ret); + return ret; + } + + /* + * Power to the display has been enabled via the regulator fixed api during + * regulator init. Per datasheet, we must wait at least 10ms before + * starting reset sequence after power-on. + */ + k_sleep(K_MSEC(10)); + /* Start reset sequence */ + ret = gpio_pin_set_dt(&config->reset_gpio, 0); + if (ret < 0) { + LOG_ERR("Could not pull reset low (%d)", ret); + return ret; + } + /* Per datasheet, reset low pulse width should be at least 10usec */ + k_sleep(K_USEC(10)); + ret = gpio_pin_set_dt(&config->reset_gpio, 1); + if (ret < 0) { + LOG_ERR("Could not pull reset high (%d)", ret); + return ret; + } + /* + * It is necessary to wait at least 120msec after releasing reset, + * before sending additional commands. This delay can be 5msec + * if we are certain the display module is in SLEEP IN state, + * but this is not guaranteed (for example, with a warm reset) + */ + k_sleep(K_MSEC(150)); + } + + /* Write initialization settings for display */ + for (i = 0; i < ARRAY_SIZE(rm67199_init_setting); i++) { + buf[0] = rm67199_init_setting[i].cmd; + buf[1] = rm67199_init_setting[i].param; + ret = mipi_dsi_generic_write(config->mipi_dsi, config->channel, buf, 2); + if (ret < 0) { + return ret; + } + } + + /* Change to send user command. */ + buf[0] = RM67199_WRMAUCCTR; + buf[1] = 0x00; + ret = mipi_dsi_generic_write(config->mipi_dsi, config->channel, buf, 2); + if (ret < 0) { + return ret; + } + + /* Set DSI mode */ + buf[0] = RM67199_SETDSIMODE; + buf[1] = 0x03; + ret = mipi_dsi_generic_write(config->mipi_dsi, config->channel, buf, 2); + if (ret < 0) { + return ret; + } + + /* Set pixel format */ + if (data->pixel_format == MIPI_DSI_PIXFMT_RGB888) { + buf[1] = MIPI_DCS_PIXEL_FORMAT_24BIT; + data->bytes_per_pixel = 3; + } else if (data->pixel_format == MIPI_DSI_PIXFMT_RGB565) { + buf[1] = MIPI_DCS_PIXEL_FORMAT_16BIT; + data->bytes_per_pixel = 2; + } else { + /* Unsupported pixel format */ + LOG_ERR("Pixel format not supported"); + return -ENOTSUP; + } + buf[0] = MIPI_DCS_SET_PIXEL_FORMAT; + ret = mipi_dsi_generic_write(config->mipi_dsi, config->channel, buf, 2); + if (ret < 0) { + return ret; + } + + /* Brightness. */ + buf[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS; + buf[1] = 0xFF; + ret = mipi_dsi_generic_write(config->mipi_dsi, config->channel, buf, 2); + if (ret < 0) { + return ret; + } + + /* Delay 50 ms before exiting sleep mode */ + k_sleep(K_MSEC(50)); + buf[0] = MIPI_DCS_EXIT_SLEEP_MODE; + ret = mipi_dsi_generic_write(config->mipi_dsi, config->channel, &buf[0], 1); + if (ret < 0) { + return ret; + } + + /* + * We must wait 5 ms after exiting sleep mode before sending additional + * commands. If we intend to enter sleep mode, we must delay + * 120 ms before sending that command. To be safe, delay 150ms + */ + k_sleep(K_MSEC(150)); + + /* Setup backlight */ + if (config->bl_gpio.port != NULL) { + ret = gpio_pin_configure_dt(&config->bl_gpio, GPIO_OUTPUT_ACTIVE); + if (ret < 0) { + LOG_ERR("Could not configure bl GPIO (%d)", ret); + return ret; + } + } + + /* Now, enable display */ + buf[0] = MIPI_DCS_SET_DISPLAY_ON; + ret = mipi_dsi_generic_write(config->mipi_dsi, config->channel, &buf[0], 1); + if (ret < 0) { + LOG_ERR("%s failed", __func__); + } else { + LOG_INF("%s succeeded", __func__); + } + + return ret; +} + +static int rm67199_blanking_off(const struct device *dev) +{ + const struct rm67199_config *config = dev->config; + + if (config->bl_gpio.port != NULL) { + return gpio_pin_set_dt(&config->bl_gpio, 1); + } + + return -ENOTSUP; +} + +static int rm67199_blanking_on(const struct device *dev) +{ + const struct rm67199_config *config = dev->config; + + if (config->bl_gpio.port != NULL) { + return gpio_pin_set_dt(&config->bl_gpio, 0); + } + + return -ENOTSUP; +} + +static int rm67199_set_pixel_format(const struct device *dev, + const enum display_pixel_format pixel_format) +{ + const struct rm67199_config *config = dev->config; + struct rm67199_data *data = dev->data; + uint8_t param; + + switch (pixel_format) { + case PIXEL_FORMAT_RGB_565: + data->pixel_format = MIPI_DSI_PIXFMT_RGB565; + param = MIPI_DCS_PIXEL_FORMAT_16BIT; + data->bytes_per_pixel = 2; + break; + case PIXEL_FORMAT_RGB_888: + data->pixel_format = MIPI_DSI_PIXFMT_RGB888; + param = MIPI_DCS_PIXEL_FORMAT_24BIT; + data->bytes_per_pixel = 3; + break; + default: + /* Other display formats not implemented */ + return -ENOTSUP; + } + + return mipi_dsi_dcs_write(config->mipi_dsi, config->channel, MIPI_DCS_SET_PIXEL_FORMAT, + ¶m, 1); +} + +static int rm67199_set_orientation(const struct device *dev, + const enum display_orientation orientation) +{ + ARG_UNUSED(dev); + + if (orientation == DISPLAY_ORIENTATION_NORMAL) { + return 0; + } + LOG_ERR("Changing display orientation not implemented"); + return -ENOTSUP; +} + +static const struct display_driver_api rm67199_api = { + .blanking_on = rm67199_blanking_on, + .blanking_off = rm67199_blanking_off, + .set_pixel_format = rm67199_set_pixel_format, + .set_orientation = rm67199_set_orientation, +}; + +#define RM67199_CONTROLLER(id) \ + static const struct rm67199_config rm67199_config_##id = { \ + .mipi_dsi = DEVICE_DT_GET(DT_INST_BUS(id)), \ + .channel = DT_INST_REG_ADDR(id), \ + .reset_gpio = GPIO_DT_SPEC_INST_GET_OR(id, reset_gpios, {0}), \ + .bl_gpio = GPIO_DT_SPEC_INST_GET_OR(id, bl_gpios, {0}), \ + .num_of_lanes = DT_INST_PROP_BY_IDX(id, data_lanes, 0), \ + }; \ + static struct rm67199_data rm67199_data_##id = { \ + .pixel_format = DT_INST_PROP(id, pixel_format), \ + }; \ + DEVICE_DT_INST_DEFINE(id, &rm67199_init, NULL, &rm67199_data_##id, &rm67199_config_##id, \ + POST_KERNEL, CONFIG_DISPLAY_INIT_PRIORITY, &rm67199_api); + +DT_INST_FOREACH_STATUS_OKAY(RM67199_CONTROLLER) diff --git a/drivers/mipi_dsi/dsi_nxp_dwc.c b/drivers/mipi_dsi/dsi_nxp_dwc.c index 129fcd925b558..866f096e4812d 100644 --- a/drivers/mipi_dsi/dsi_nxp_dwc.c +++ b/drivers/mipi_dsi/dsi_nxp_dwc.c @@ -69,7 +69,7 @@ static int dsi_dwc_attach(const struct device *dev, uint8_t channel, DSI_SetDpiConfig(base, &dpi_config, mdev->data_lanes); -#if CONFIG_SOC_MIMX9352_A55 +#if defined(CONFIG_SOC_MIMX9352_A55) || defined(CONFIG_SOC_MIMX9352_M33) uint32_t phyByteClkFreq_Hz = config->data_rate_clock * mdev->data_lanes / 8; DSI_SetCommandModeConfig(base, &command_config, phyByteClkFreq_Hz); @@ -118,7 +118,7 @@ static int dsi_dwc_attach(const struct device *dev, uint8_t channel, DSI_ConfigDphy(base, config->dphy_ref_frequency, config->data_rate_clock); #endif -#if CONFIG_SOC_MIMX9352_A55 +#if defined(CONFIG_SOC_MIMX9352_A55) || defined(CONFIG_SOC_MIMX9352_M33) BLK_CTRL_MEDIAMIX->MIPI.DSI = MEDIAMIX_BLK_CTRL_DSI_updatepll(1) | MEDIAMIX_BLK_CTRL_DSI_HSFREQRANGE(phy_hsfreqrange) | MEDIAMIX_BLK_CTRL_DSI_CLKSEL(1) | diff --git a/drivers/misc/CMakeLists.txt b/drivers/misc/CMakeLists.txt index 03cd3089e2e59..fde7413bfe868 100644 --- a/drivers/misc/CMakeLists.txt +++ b/drivers/misc/CMakeLists.txt @@ -8,6 +8,7 @@ add_subdirectory_ifdef(CONFIG_GROVE_LCD_RGB grove_lcd_rgb) add_subdirectory_ifdef(CONFIG_MCUX_FLEXIO mcux_flexio) add_subdirectory_ifdef(CONFIG_NORDIC_VPR_LAUNCHER nordic_vpr_launcher) add_subdirectory_ifdef(CONFIG_NXP_FLEXRAM nxp_flexram) +add_subdirectory_ifdef(CONFIG_NXP_IMX93_VIDEO_PLL nxp_imx93_video_pll) add_subdirectory_ifdef(CONFIG_NXP_MEDIAMIX_BLK_CTRL nxp_imx93_mediamix) add_subdirectory_ifdef(CONFIG_NXP_RTXXX_DSP_CTRL nxp_rtxxx_dsp_ctrl) add_subdirectory_ifdef(CONFIG_NXP_S32_EMIOS nxp_s32_emios) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index d50f957a43065..6331bd250b8fa 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -15,6 +15,7 @@ source "drivers/misc/mcux_flexio/Kconfig" source "drivers/misc/nordic_vpr_launcher/Kconfig" source "drivers/misc/nxp_flexram/Kconfig" source "drivers/misc/nxp_imx93_mediamix/Kconfig" +source "drivers/misc/nxp_imx93_video_pll/Kconfig" source "drivers/misc/nxp_inputmux/Kconfig" source "drivers/misc/nxp_rtxxx_dsp_ctrl/Kconfig" source "drivers/misc/nxp_s32_emios/Kconfig" diff --git a/drivers/misc/nxp_imx93_video_pll/CMakeLists.txt b/drivers/misc/nxp_imx93_video_pll/CMakeLists.txt new file mode 100644 index 0000000000000..4726356a4aa7c --- /dev/null +++ b/drivers/misc/nxp_imx93_video_pll/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(nxp_video_pll_ctrl.c) diff --git a/drivers/misc/nxp_imx93_video_pll/Kconfig b/drivers/misc/nxp_imx93_video_pll/Kconfig new file mode 100644 index 0000000000000..e7b7ffbbdee91 --- /dev/null +++ b/drivers/misc/nxp_imx93_video_pll/Kconfig @@ -0,0 +1,21 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +config NXP_IMX93_VIDEO_PLL + bool "Initialize NXP iMX93 VIDEO PLL" + default y + depends on DT_HAS_NXP_IMX93_VIDEO_PLL_ENABLED + help + Enable initialization of VIDEO PLL using device tree configuration. + +if NXP_IMX93_VIDEO_PLL + +module = VIDEO_PLL +module-str = video pll +source "subsys/logging/Kconfig.template.log_config" + +config IMX93_VIDEO_PLL_INIT_PRIORITY + int "i.MX93 video pll initialization priority" + default 30 + +endif # NXP_IMX93_VIDEO_PLL diff --git a/drivers/misc/nxp_imx93_video_pll/nxp_video_pll_ctrl.c b/drivers/misc/nxp_imx93_video_pll/nxp_video_pll_ctrl.c new file mode 100644 index 0000000000000..409343f5399e6 --- /dev/null +++ b/drivers/misc/nxp_imx93_video_pll/nxp_video_pll_ctrl.c @@ -0,0 +1,52 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_imx93_video_pll +#include +#include +#include +#include +LOG_MODULE_REGISTER(video_pll, CONFIG_VIDEO_PLL_LOG_LEVEL); +#include + +struct video_pll_config { + uint32_t rdiv; + uint32_t mfi; + uint32_t mfn; + uint32_t mfd; + uint32_t odiv; + uint32_t freq; +}; + +#define VIDEO_PLL_INIT(inst) \ + static int video_pll_init_##inst(const struct device *dev) \ + { \ + const struct video_pll_config *cfg = dev->config; \ + const fracn_pll_init_t pll_cfg = { \ + .rdiv = cfg->rdiv, \ + .mfi = cfg->mfi, \ + .mfn = cfg->mfn, \ + .mfd = cfg->mfd, \ + .odiv = cfg->odiv, \ + }; \ + CLOCK_PllInit(VIDEOPLL, &pll_cfg); \ + g_clockSourceFreq[kCLOCK_VideoPll1] = cfg->freq; \ + g_clockSourceFreq[kCLOCK_VideoPll1Out] = cfg->freq; \ + printk("Initialized VIDEO PLL to %d Hz\n", cfg->freq); \ + return 0; \ + }; \ + static const struct video_pll_config video_pll_cfg_##inst = { \ + .rdiv = DT_INST_PROP(inst, rdiv), \ + .mfi = DT_INST_PROP(inst, mfi), \ + .mfn = DT_INST_PROP(inst, mfn), \ + .mfd = DT_INST_PROP(inst, mfd), \ + .odiv = DT_INST_PROP(inst, odiv), \ + .freq = DT_INST_PROP(inst, pll_frequency), \ + }; \ + DEVICE_DT_INST_DEFINE(inst, video_pll_init_##inst, NULL, NULL, &video_pll_cfg_##inst, \ + PRE_KERNEL_1, CONFIG_IMX93_VIDEO_PLL_INIT_PRIORITY, NULL); + +DT_INST_FOREACH_STATUS_OKAY(VIDEO_PLL_INIT) diff --git a/dts/arm/nxp/nxp_imx93_m33.dtsi b/dts/arm/nxp/nxp_imx93_m33.dtsi index 4c61033a2c272..aade864ea2914 100644 --- a/dts/arm/nxp/nxp_imx93_m33.dtsi +++ b/dts/arm/nxp/nxp_imx93_m33.dtsi @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -107,6 +108,47 @@ status = "disabled"; }; + lpi2c2: i2c@44350000 { + compatible = "nxp,lpi2c"; + clock-frequency = ; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x44350000 0x4000>; + interrupts = <14 0>; + clocks = <&ccm IMX_CCM_LPI2C2_CLK 0 0>; + status = "disabled"; + }; + + lcdif: display-controller@4ae30000 { + compatible = "nxp,imx-lcdifv3"; + pixel-format = "argb-8888"; + media-axi-clk-rate = <400000000>; + media-apb-clk-rate = <133333334>; + reg = <0x4ae30000 0x10000>; + interrupts = <176 0>; + clocks = <&ccm IMX_CCM_MEDIA_DISP_PIX_CLK 0 0>, + <&ccm IMX_CCM_MEDIA_AXI_CLK 0 0>, + <&ccm IMX_CCM_MEDIA_APB_CLK 0 0>; + status = "disabled"; + }; + + mipi_dsi: dsi@4ae10000 { + compatible = "nxp,mipi-dsi-dwc"; + reg = <0x4ae10000 0x10000>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <177 0>; + clocks = <&ccm IMX_CCM_MIPI_PHY_CFG_CLK 0 0>; + nxp,dc = <&lcdif>; + status = "disabled"; + }; + + media_blk_ctrl: mediamix@4ac10000 { + compatible = "nxp,imx93-mediamix"; + reg = <0x4ac10000 0x1000>; + status = "disabled"; + }; + tpm1: pwm@44310000 { compatible = "nxp,kinetis-tpm"; reg = <0x44310000 0x88>; @@ -175,6 +217,12 @@ #io-channel-cells = <1>; clocks = <&ccm IMX_CCM_SAR_ADC1_CLK 0x0 0>; }; + + video_pll: videopll@44481400 { + compatible = "nxp,imx93-video-pll"; + reg = <0x44481400 0x1000>; + status = "disabled"; + }; }; }; diff --git a/dts/arm64/nxp/nxp_mimx93_a55.dtsi b/dts/arm64/nxp/nxp_mimx93_a55.dtsi index 31305b51f2032..2977d6467c3f6 100644 --- a/dts/arm64/nxp/nxp_mimx93_a55.dtsi +++ b/dts/arm64/nxp/nxp_mimx93_a55.dtsi @@ -116,6 +116,12 @@ status = "disabled"; }; + video_pll: videopll@44481400 { + compatible = "nxp,imx93-video-pll"; + reg = <0x44481400 0x1000>; + status = "disabled"; + }; + wdog3: watchdog@42490000 { compatible = "nxp,wdog32"; reg = <0x42490000 0x1000>; diff --git a/dts/bindings/display/raydium,rm67199.yaml b/dts/bindings/display/raydium,rm67199.yaml new file mode 100644 index 0000000000000..5ba138aecc92b --- /dev/null +++ b/dts/bindings/display/raydium,rm67199.yaml @@ -0,0 +1,31 @@ +# +# Copyright 2025 NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: Raydium RM67199 controller for DSI panels + +compatible: "raydium,rm67199" + +include: [mipi-dsi-device.yaml] + +properties: + reset-gpios: + type: phandle-array + description: | + The RESETn pin is asserted to disable the sensor causing a hard + reset. The sensor receives this as an active-low signal. + + bl-gpios: + type: phandle-array + description: | + The BLn pin is asserted to control the backlight of the panel. + The sensor receives this as an active-high signal. + + te-gpios: + type: phandle-array + description: | + The tearing effect pin is asserted by the controller at a display + VSYNC interval. This permits the controller to send new display + data during a VSYNC interval, removing tearing. diff --git a/dts/bindings/misc/nxp,imx93-video-pll.yaml b/dts/bindings/misc/nxp,imx93-video-pll.yaml new file mode 100644 index 0000000000000..d28ed5a0c788f --- /dev/null +++ b/dts/bindings/misc/nxp,imx93-video-pll.yaml @@ -0,0 +1,42 @@ +# +# Copyright 2025 NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: NXP i.MX93 Video PLL controller + +compatible: "nxp,imx93-video-pll" + +include: base.yaml + +properties: + rdiv: + type: int + required: true + description: Reference divider value. + + mfi: + type: int + required: true + description: Integer multiplier factor. + + mfn: + type: int + required: true + description: Fractional numerator. + + mfd: + type: int + required: true + description: Fractional denominator. + + odiv: + type: int + required: true + description: Output divider value. + + pll-frequency: + type: int + required: true + description: Target PLL output frequency in Hz. diff --git a/soc/nxp/imx/imx9/imx93/CMakeLists.txt b/soc/nxp/imx/imx9/imx93/CMakeLists.txt index ff328de5eb5f7..1a05efea41044 100644 --- a/soc/nxp/imx/imx9/imx93/CMakeLists.txt +++ b/soc/nxp/imx/imx9/imx93/CMakeLists.txt @@ -7,7 +7,6 @@ if(CONFIG_SOC_MIMX9352_A55) zephyr_sources_ifdef(CONFIG_ARM_MMU a55/mmu_regions.c) zephyr_sources(common_clock_set.c) - zephyr_sources(a55/soc.c) set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm64/scripts/linker.ld CACHE INTERNAL "") elseif(CONFIG_SOC_MIMX9352_M33) zephyr_include_directories(.) diff --git a/soc/nxp/imx/imx9/imx93/Kconfig b/soc/nxp/imx/imx9/imx93/Kconfig index 0c8871e97330f..45e7f57413b00 100644 --- a/soc/nxp/imx/imx9/imx93/Kconfig +++ b/soc/nxp/imx/imx9/imx93/Kconfig @@ -26,9 +26,6 @@ config MCUX_CORE_SUFFIX default "_ca55" if SOC_MIMX9352_A55 default "_cm33" if SOC_MIMX9352_M33 -config INIT_VIDEO_PLL - bool "Init Video PLL" - if SOC_MIMX9352_M33 config TRDC_MCUX_TRDC_1 diff --git a/soc/nxp/imx/imx9/imx93/a55/soc.c b/soc/nxp/imx/imx9/imx93/a55/soc.c deleted file mode 100644 index 6665b4f495868..0000000000000 --- a/soc/nxp/imx/imx9/imx93/a55/soc.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2025 NXP - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include - -#define VIDEO_PLL_FREQ 400000000 - -#if defined(CONFIG_INIT_VIDEO_PLL) -static int soc_video_pll_init(void) -{ - /* Configure Video PLL to 400MHz */ - const fracn_pll_init_t videoPllCfg = { - .rdiv = 1, - .mfi = 200, - .mfn = 0, - .mfd = 100, - .odiv = 12, - }; - - /** PLL_CLKx = (24M / rdiv * (mfi + mfn/mfd) / odiv) */ - CLOCK_PllInit(VIDEOPLL, &videoPllCfg); - g_clockSourceFreq[kCLOCK_VideoPll1] = VIDEO_PLL_FREQ; - g_clockSourceFreq[kCLOCK_VideoPll1Out] = VIDEO_PLL_FREQ; - - printf("Initialized VIDEO PLL to %d\n", g_clockSourceFreq[kCLOCK_VideoPll1Out]); - - return 0; -} -#endif /* CONFIG_INIT_VIDEO_PLL */ - -static int soc_init(void) -{ -#if defined(CONFIG_INIT_VIDEO_PLL) - int ret = soc_video_pll_init(); - - if (ret) { - printf("SoC VIDEO PLL init failed"); - return ret; - } -#endif /* CONFIG_INIT_VIDEO_PLL */ - - return 0; -} -/* - * Init video pll based on config - */ -SYS_INIT(soc_init, PRE_KERNEL_2, 0); diff --git a/tests/drivers/build_all/display/app.overlay b/tests/drivers/build_all/display/app.overlay index f87b736713d46..8fc9cd5a9b8ba 100644 --- a/tests/drivers/build_all/display/app.overlay +++ b/tests/drivers/build_all/display/app.overlay @@ -525,6 +525,16 @@ height = <466>; pixel-format = <0>; }; + + test_rm67199: rm67199@8 { + status = "okay"; + compatible = "raydium,rm67199"; + reg = <8>; + bl-gpios = <&test_gpio 0 0>; + reset-gpios = <&test_gpio 0 0>; + data-lanes = <4>; + pixel-format = <0>; + }; }; test_spi: spi@33334444 { diff --git a/tests/drivers/display/display_check/testcase.yaml b/tests/drivers/display/display_check/testcase.yaml index 6fd328c3cf9f9..0725a32f29206 100644 --- a/tests/drivers/display/display_check/testcase.yaml +++ b/tests/drivers/display/display_check/testcase.yaml @@ -95,3 +95,4 @@ tests: - platform:ek_ra8d1:SHIELD=rtk7eka6m3b00001bu - platform:nucleo_g071rb/stm32g071xx:SHIELD=x_nucleo_gfx01m2 - platform:mimxrt700_evk/mimxrt798s/cm33_cpu0:SHIELD=zc143ac72mipi + - platform:imx93_evk/mimx9352/m33/ddr:SHIELD=nxp_mx8_dsi_oled1a diff --git a/west.yml b/west.yml index dfbc93a746720..adeb861787007 100644 --- a/west.yml +++ b/west.yml @@ -210,7 +210,7 @@ manifest: groups: - hal - name: hal_nxp - revision: a7f64ac242138179b7f893eb440ccb0c5655f8e9 + revision: pull/640/head path: modules/hal/nxp groups: - hal