Add pw_display_driver_mipi for the mimxrt595 target
Major changes in this CL:
* Add a MIPI DSI display driver: pw_display_driver_mipi.
* Add pw_mipi_dsi which is a generic mipi_dsi module somewhat
similar to pw_spi.
* Add pw_mipi_dsi_mcuxpresso which is a MCUxpresso implementtion
of pw_mipi_dsi.
* Switched pw_display_driver from Update to GetFramebuffer/
ReleaseFramebuffer. This is needed because the MCUxpresso
MIPI DSI API manages the framebuffer and not the application.
Change-Id: Icf386dc1b1b70463a53f89858fb2603398e5f547
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/experimental/+/124830
Reviewed-by: Anthony DiGirolamo <tonymd@google.com>
Commit-Queue: Chris Mumford <cmumford@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index bfbf794..0663b34 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -199,6 +199,7 @@
"//applications/blinky:blinky(//targets/mimxrt595_evk:mimxrt595_evk_debug)",
"//applications/rpc:all(//targets/mimxrt595_evk:mimxrt595_evk_debug)",
"//applications/strings:all(//targets/mimxrt595_evk:mimxrt595_evk_debug)",
+ "//applications/terminal_display:all(//targets/mimxrt595_evk:mimxrt595_evk_debug)",
]
}
diff --git a/applications/app_common_impl/BUILD.gn b/applications/app_common_impl/BUILD.gn
index 208679d..38703d6 100644
--- a/applications/app_common_impl/BUILD.gn
+++ b/applications/app_common_impl/BUILD.gn
@@ -16,6 +16,7 @@
import("//build_overrides/pigweed.gni")
import("$dir_pigweed_experimental/third_party/imgui/imgui.gni")
import("$dir_pw_build/target_types.gni")
+import("$dir_pw_third_party/mcuxpresso/mcuxpresso.gni")
declare_args() {
# LCD width in pixels (ex. "320")
@@ -50,6 +51,15 @@
# SPI bus clock pin (ex. "14")
pw_spi_clock_pin_num = ""
+
+ # framebuffer width in pixels (ex. "320")
+ pw_nxp_buffer_width = ""
+
+ # framebuffer start pixel X coord (ex. "4")
+ pw_nxp_buffer_start_x = "0"
+
+ # framebuffer start pixel Y coord (ex. "4")
+ pw_nxp_buffer_start_y = "0"
}
config("common_standard_flags") {
@@ -79,6 +89,16 @@
]
}
+config("common_mimxrt595_flags") {
+ cflags = [
+ "-DLCD_WIDTH=" + pw_lcd_width,
+ "-DLCD_HEIGHT=" + pw_lcd_height,
+ "-DFRAMEBUFFER_WIDTH=" + pw_nxp_buffer_width,
+ "-DFRAMEBUFFER_START_X=" + pw_nxp_buffer_start_x,
+ "-DFRAMEBUFFER_START_Y=" + pw_nxp_buffer_start_y,
+ ]
+}
+
config("common_host_flags") {
cflags = [
"-DLCD_WIDTH=" + pw_lcd_width,
@@ -124,12 +144,19 @@
remove_configs = [ "$dir_pw_build:strict_warnings" ]
}
-pw_source_set("mimxrt595") {
- deps = [
- "$dir_pw_display_driver_null",
- "//applications/app_common:app_common.facade",
- ]
- sources = [ "common_mimxrt595.cc" ]
+if (pw_third_party_mcuxpresso_SDK != "") {
+ pw_source_set("mimxrt595") {
+ public_configs = [ ":common_mimxrt595_flags" ]
+ deps = [
+ "$dir_pw_display",
+ "$dir_pw_display_driver_mipi_dsi",
+ "$dir_pw_framebuffer_pool",
+ "$dir_pw_mipi_dsi_mcuxpresso",
+ "$pw_third_party_mcuxpresso_SDK",
+ "//applications/app_common:app_common.facade",
+ ]
+ sources = [ "common_mimxrt595.cc" ]
+ }
}
_pico_common_deps = [
diff --git a/applications/app_common_impl/common_arduino.cc b/applications/app_common_impl/common_arduino.cc
index c8d8ec7..96ecc2e 100644
--- a/applications/app_common_impl/common_arduino.cc
+++ b/applications/app_common_impl/common_arduino.cc
@@ -44,11 +44,9 @@
pw::spi::Device device;
};
-constexpr int kScaleFactor = 1;
-constexpr int kFramebufferWidth = LCD_WIDTH / kScaleFactor;
-constexpr int kFramebufferHeight = LCD_HEIGHT / kScaleFactor;
-constexpr int kNumPixels = kFramebufferWidth * kFramebufferHeight;
-constexpr int kFramebufferRowBytes = kFramebufferWidth * sizeof(uint16_t);
+constexpr pw::coordinates::Size<int> kDisplaySize = {LCD_WIDTH, LCD_HEIGHT};
+constexpr int kNumPixels = LCD_WIDTH * LCD_HEIGHT;
+constexpr int kDisplayRowBytes = sizeof(uint16_t) * LCD_WIDTH;
constexpr pw::spi::Config kSpiConfig8Bit{
.polarity = pw::spi::ClockPolarity::kActiveHigh,
@@ -79,6 +77,19 @@
SpiValues s_spi_16_bit(kSpiConfig16Bit,
s_spi_chip_selector,
s_spi_initiator_mutex);
+uint16_t s_pixel_data[kNumPixels];
+constexpr pw::framebuffer::pool::PoolData s_fb_pool_data = {
+ .fb_addr =
+ {
+ s_pixel_data,
+ nullptr,
+ nullptr,
+ },
+ .num_fb = 1,
+ .size = {LCD_WIDTH, LCD_HEIGHT},
+ .row_bytes = kDisplayRowBytes,
+ .start = {0, 0},
+};
DisplayDriver s_display_driver({
.data_cmd_gpio = s_display_dc_pin,
#if TFT_RST != -1
@@ -87,14 +98,9 @@
.reset_gpio = nullptr,
#endif
.spi_device_8_bit = s_spi_8_bit.device,
- .spi_device_16_bit = s_spi_16_bit.device,
+ .spi_device_16_bit = s_spi_16_bit.device, .pool_data = s_fb_pool_data,
});
-uint16_t s_pixel_data[kNumPixels];
-Display s_display(FramebufferRgb565(s_pixel_data,
- kFramebufferWidth,
- kFramebufferHeight,
- kFramebufferRowBytes),
- s_display_driver);
+Display s_display(s_display_driver, kDisplaySize);
SpiValues::SpiValues(pw::spi::Config config,
pw::spi::ChipSelector& selector,
@@ -114,9 +120,7 @@
SPI.begin();
- PW_TRY(s_display_driver.Init());
-
- return s_display.Init();
+ return s_display_driver.Init();
}
// static
diff --git a/applications/app_common_impl/common_host_imgui.cc b/applications/app_common_impl/common_host_imgui.cc
index 5e35085..48ec2d3 100644
--- a/applications/app_common_impl/common_host_imgui.cc
+++ b/applications/app_common_impl/common_host_imgui.cc
@@ -17,6 +17,7 @@
#include "pw_status/try.h"
using pw::Status;
+using pw::color::color_rgb565_t;
using pw::framebuffer::FramebufferRgb565;
namespace {
@@ -25,23 +26,29 @@
constexpr int kFramebufferWidth = LCD_WIDTH / kDisplayScaleFactor;
constexpr int kFramebufferHeight = LCD_HEIGHT / kDisplayScaleFactor;
constexpr int kNumPixels = kFramebufferWidth * kFramebufferHeight;
-constexpr int FramebufferRowBytes = sizeof(uint16_t) * kFramebufferWidth;
+constexpr int kFramebufferRowBytes = sizeof(uint16_t) * kFramebufferWidth;
+constexpr pw::coordinates::Size<int> kDisplaySize = {LCD_WIDTH, LCD_HEIGHT};
-uint16_t s_pixel_data[kNumPixels];
-pw::display_driver::DisplayDriverImgUI s_display_driver;
-pw::display::DisplayImgUI s_display(FramebufferRgb565(s_pixel_data,
- kFramebufferWidth,
- kFramebufferHeight,
- FramebufferRowBytes),
- s_display_driver);
+color_rgb565_t s_pixel_data[kNumPixels];
+constexpr pw::framebuffer::pool::PoolData s_fb_pool_data = {
+ .fb_addr =
+ {
+ s_pixel_data,
+ nullptr,
+ nullptr,
+ },
+ .num_fb = 1,
+ .size = {kFramebufferWidth, kFramebufferHeight},
+ .row_bytes = kFramebufferRowBytes,
+ .start = {0, 0},
+};
+pw::display_driver::DisplayDriverImgUI s_display_driver(s_fb_pool_data);
+pw::display::DisplayImgUI s_display(s_display_driver, kDisplaySize);
} // namespace
// static
-Status Common::Init() {
- PW_TRY(s_display_driver.Init());
- return s_display.Init();
-}
+Status Common::Init() { return s_display_driver.Init(); }
// static
pw::display::Display& Common::GetDisplay() { return s_display; }
diff --git a/applications/app_common_impl/common_host_null.cc b/applications/app_common_impl/common_host_null.cc
index 9984d0c..07978a0 100644
--- a/applications/app_common_impl/common_host_null.cc
+++ b/applications/app_common_impl/common_host_null.cc
@@ -21,22 +21,15 @@
namespace {
-constexpr int kNumPixels = LCD_WIDTH * LCD_HEIGHT;
-constexpr int kDisplayRowBytes = sizeof(uint16_t) * LCD_WIDTH;
+constexpr pw::coordinates::Size<int> kDisplaySize = {LCD_WIDTH, LCD_HEIGHT};
-uint16_t s_pixel_data[kNumPixels];
pw::display_driver::DisplayDriverNULL s_display_driver;
-pw::display::Display s_display(
- FramebufferRgb565(s_pixel_data, LCD_WIDTH, LCD_HEIGHT, kDisplayRowBytes),
- s_display_driver);
+pw::display::Display s_display(s_display_driver, kDisplaySize);
} // namespace
// static
-Status Common::Init() {
- PW_TRY(s_display_driver.Init());
- return s_display.Init();
-}
+Status Common::Init() { return s_display_driver.Init(); }
// static
pw::display::Display& Common::GetDisplay() { return s_display; }
diff --git a/applications/app_common_impl/common_mimxrt595.cc b/applications/app_common_impl/common_mimxrt595.cc
index eedd04a..541fa1f 100644
--- a/applications/app_common_impl/common_mimxrt595.cc
+++ b/applications/app_common_impl/common_mimxrt595.cc
@@ -12,18 +12,102 @@
// License for the specific language governing permissions and limitations under
// the License.
#include "app_common/common.h"
-#include "pw_display_driver_null/display_driver.h"
+#include "board.h"
+#include "fsl_iopctl.h"
+#include "pin_mux.h"
+#include "pw_display_driver_mipi/display_driver.h"
+#include "pw_mipi_dsi_mcuxpresso/device.h"
+#include "pw_status/try.h"
+
+using pw::Status;
+using pw::color::color_rgb565_t;
+using pw::display::Display;
+using pw::display_driver::DisplayDriverMipiDsi;
+using pw::framebuffer::FramebufferRgb565;
+using pw::mipi::dsi::MCUXpressoDevice;
namespace {
-pw::display_driver::DisplayDriverNULL s_display_driver;
-pw::display::Display s_display(pw::framebuffer::FramebufferRgb565(),
- s_display_driver);
+// Framebuffer addresses in on-board PSRAM.
+constexpr uint32_t kBuffer0Addr = 0x28000000U;
+constexpr uint32_t kBuffer1Addr = 0x28200000U;
+constexpr video_pixel_format_t kPixelFormat = kVIDEO_PixelFormatRGB565;
+constexpr uint16_t kBufferStrideBytes =
+ FRAMEBUFFER_WIDTH * pw::mipi::dsi::kBytesPerPixel;
+constexpr pw::coordinates::Size<int> kDisplaySize = {LCD_WIDTH, LCD_HEIGHT};
+
+const pw::framebuffer::pool::PoolData s_fb_pool_data = {
+ .fb_addr =
+ {
+ reinterpret_cast<pw::color::color_rgb565_t*>(kBuffer0Addr),
+ reinterpret_cast<pw::color::color_rgb565_t*>(kBuffer1Addr),
+ nullptr,
+ },
+ .num_fb = 2,
+ .size = {LCD_WIDTH, LCD_HEIGHT},
+ .row_bytes = kBufferStrideBytes,
+ .start = {FRAMEBUFFER_START_X, FRAMEBUFFER_START_Y},
+};
+
+MCUXpressoDevice s_mipi_device(s_fb_pool_data,
+ {.width = LCD_WIDTH, .height = LCD_HEIGHT},
+ kPixelFormat);
+DisplayDriverMipiDsi s_display_driver(
+ {
+ .mipi_device = s_mipi_device,
+ },
+ kDisplaySize);
+Display s_display(s_display_driver, kDisplaySize);
+
+void InitMipiPins(void) {
+ constexpr uint32_t kPwmModeFunc =
+ (IOPCTL_PIO_FUNC0 | IOPCTL_PIO_PUPD_DI | IOPCTL_PIO_PULLDOWN_EN |
+ IOPCTL_PIO_INBUF_EN | IOPCTL_PIO_SLEW_RATE_NORMAL |
+ IOPCTL_PIO_FULLDRIVE_DI | IOPCTL_PIO_ANAMUX_DI | IOPCTL_PIO_PSEDRAIN_DI |
+ IOPCTL_PIO_INV_DI);
+ IOPCTL_PinMuxSet(IOPCTL, BOARD_MIPI_BL_PORT, BOARD_MIPI_BL_PIN, kPwmModeFunc);
+
+ constexpr uint32_t kPwrEnModeFunc =
+ (IOPCTL_PIO_FUNC0 | IOPCTL_PIO_PUPD_DI | IOPCTL_PIO_PULLDOWN_EN |
+ IOPCTL_PIO_INBUF_EN | IOPCTL_PIO_SLEW_RATE_NORMAL |
+ IOPCTL_PIO_FULLDRIVE_DI | IOPCTL_PIO_ANAMUX_DI | IOPCTL_PIO_PSEDRAIN_DI |
+ IOPCTL_PIO_INV_DI);
+ IOPCTL_PinMuxSet(
+ IOPCTL, BOARD_MIPI_POWER_PORT, BOARD_MIPI_POWER_PIN, kPwrEnModeFunc);
+
+ constexpr uint32_t kPort2Pin18ModeFunc =
+ (IOPCTL_PIO_FUNC0 | IOPCTL_PIO_PUPD_EN | IOPCTL_PIO_PULLDOWN_EN |
+ IOPCTL_PIO_INBUF_EN | IOPCTL_PIO_SLEW_RATE_NORMAL |
+ IOPCTL_PIO_FULLDRIVE_DI | IOPCTL_PIO_ANAMUX_DI | IOPCTL_PIO_PSEDRAIN_DI |
+ IOPCTL_PIO_INV_DI);
+ IOPCTL_PinMuxSet(IOPCTL, 3U, 18U, kPort2Pin18ModeFunc);
+
+ constexpr uint32_t kResetBModeFunc =
+ (IOPCTL_PIO_FUNC0 | IOPCTL_PIO_PUPD_DI | IOPCTL_PIO_PULLDOWN_EN |
+ IOPCTL_PIO_INBUF_EN | IOPCTL_PIO_SLEW_RATE_NORMAL |
+ IOPCTL_PIO_FULLDRIVE_DI | IOPCTL_PIO_ANAMUX_DI | IOPCTL_PIO_PSEDRAIN_DI |
+ IOPCTL_PIO_INV_DI);
+ IOPCTL_PinMuxSet(
+ IOPCTL, BOARD_MIPI_RST_PORT, BOARD_MIPI_RST_PIN, kResetBModeFunc);
+}
} // namespace
// static
-pw::Status Common::Init() { return s_display.Init(); }
+Status Common::Init() {
+ InitMipiPins();
+ BOARD_InitPsRam();
+
+ GPIO_PortInit(GPIO, BOARD_MIPI_POWER_PORT);
+ GPIO_PortInit(GPIO, BOARD_MIPI_BL_PORT);
+ GPIO_PortInit(GPIO, BOARD_MIPI_RST_PORT);
+ GPIO_PortInit(GPIO, BOARD_MIPI_TE_PORT);
+
+ BOARD_BootClockRUN();
+
+ PW_TRY(s_mipi_device.Init());
+ return s_display_driver.Init();
+}
// static
pw::display::Display& Common::GetDisplay() { return s_display; }
diff --git a/applications/app_common_impl/common_pico.cc b/applications/app_common_impl/common_pico.cc
index 992aad6..11c86b0 100644
--- a/applications/app_common_impl/common_pico.cc
+++ b/applications/app_common_impl/common_pico.cc
@@ -68,10 +68,11 @@
};
constexpr int kDisplayScaleFactor = 1;
+constexpr pw::coordinates::Size<int> kDisplaySize{LCD_WIDTH, LCD_HEIGHT};
constexpr int kFramebufferWidth = LCD_WIDTH / kDisplayScaleFactor;
constexpr int kFramebufferHeight = LCD_HEIGHT / kDisplayScaleFactor;
constexpr int kNumPixels = kFramebufferWidth * kFramebufferHeight;
-constexpr int FramebufferRowBytes = sizeof(uint16_t) * kFramebufferWidth;
+constexpr int kFramebufferRowBytes = sizeof(uint16_t) * kFramebufferWidth;
constexpr uint32_t kBaudRate = 31'250'000;
constexpr pw::spi::Config kSpiConfig8Bit{
@@ -103,6 +104,19 @@
SpiValues s_spi_16_bit(kSpiConfig16Bit,
s_spi_chip_selector,
s_spi_initiator_mutex);
+uint16_t s_pixel_data[kNumPixels];
+constexpr pw::framebuffer::pool::PoolData s_fb_pool_data = {
+ .fb_addr =
+ {
+ s_pixel_data,
+ nullptr,
+ nullptr,
+ },
+ .num_fb = 1,
+ .size = {kFramebufferWidth, kFramebufferHeight},
+ .row_bytes = kFramebufferRowBytes,
+ .start = {0, 0},
+};
DisplayDriver s_display_driver({
.data_cmd_gpio = s_display_dc_pin,
#if TFT_RST != -1
@@ -111,14 +125,9 @@
.reset_gpio = nullptr,
#endif
.spi_device_8_bit = s_spi_8_bit.device,
- .spi_device_16_bit = s_spi_16_bit.device,
+ .spi_device_16_bit = s_spi_16_bit.device, .pool_data = s_fb_pool_data,
});
-uint16_t pixel_data[kNumPixels];
-Display s_display(FramebufferRgb565(pixel_data,
- kFramebufferWidth,
- kFramebufferHeight,
- FramebufferRowBytes),
- s_display_driver);
+Display s_display(s_display_driver, kDisplaySize);
#if TFT_BL != -1
void SetBacklight(uint16_t brightness) {
@@ -168,9 +177,7 @@
gpio_set_function(TFT_SCLK, GPIO_FUNC_SPI);
gpio_set_function(TFT_MOSI, GPIO_FUNC_SPI);
- PW_TRY(s_display_driver.Init());
-
- return s_display.Init();
+ return s_display_driver.Init();
}
// static
diff --git a/applications/app_common_impl/common_stm32cube.cc b/applications/app_common_impl/common_stm32cube.cc
index 873ecee..7724b93 100644
--- a/applications/app_common_impl/common_stm32cube.cc
+++ b/applications/app_common_impl/common_stm32cube.cc
@@ -56,6 +56,7 @@
constexpr int kNumPixels = LCD_WIDTH * LCD_HEIGHT;
constexpr int kDisplayRowBytes = sizeof(uint16_t) * LCD_WIDTH;
+constexpr pw::coordinates::Size<int> kDisplaySize = {LCD_WIDTH, LCD_HEIGHT};
constexpr pw::spi::Config kSpiConfig8Bit{
.polarity = pw::spi::ClockPolarity::kActiveHigh,
.phase = pw::spi::ClockPhase::kFallingEdge,
@@ -82,16 +83,27 @@
SpiValues s_spi_16_bit(kSpiConfig16Bit,
s_spi_chip_selector,
s_spi_initiator_mutex);
+uint16_t s_pixel_data[kNumPixels];
+constexpr pw::framebuffer::pool::PoolData s_fb_pool_data = {
+ .fb_addr =
+ {
+ s_pixel_data,
+ nullptr,
+ nullptr,
+ },
+ .num_fb = 1,
+ .size = {LCD_WIDTH, LCD_HEIGHT},
+ .row_bytes = kDisplayRowBytes,
+ .start = {0, 0},
+};
DisplayDriverILI9341 s_display_driver({
.data_cmd_gpio = s_display_dc_pin,
.reset_gpio = nullptr,
.spi_device_8_bit = s_spi_8_bit.device,
.spi_device_16_bit = s_spi_16_bit.device,
+ .pool_data = s_fb_pool_data,
});
-uint16_t pixel_data[kNumPixels];
-Display s_display(
- FramebufferRgb565(pixel_data, LCD_WIDTH, LCD_HEIGHT, kDisplayRowBytes),
- s_display_driver);
+Display s_display(s_display_driver, kDisplaySize);
SpiValues::SpiValues(pw::spi::Config config,
pw::spi::ChipSelector& selector,
@@ -130,9 +142,7 @@
};
HAL_GPIO_Init(GPIOF, &spi_pin_config);
- PW_TRY(s_display_driver.Init());
-
- return s_display.Init();
+ return s_display_driver.Init();
}
// static
diff --git a/applications/terminal_display/main.cc b/applications/terminal_display/main.cc
index cf2a178..3362873 100644
--- a/applications/terminal_display/main.cc
+++ b/applications/terminal_display/main.cc
@@ -522,6 +522,8 @@
uint32_t start = pw::spin_delay::Millis();
framebuffer = display.GetFramebuffer();
+ if (!framebuffer.IsValid())
+ continue;
pw::draw::Fill(&framebuffer, kBlack);
DrawFrame(framebuffer, fps_view);
uint32_t end = pw::spin_delay::Millis();
diff --git a/build_overrides/pigweed.gni b/build_overrides/pigweed.gni
index ee06bbe..9b9cb9f 100644
--- a/build_overrides/pigweed.gni
+++ b/build_overrides/pigweed.gni
@@ -67,6 +67,9 @@
dir_pw_display_driver_imgui =
get_path_info("$dir_pigweed_experimental/pw_display_driver_imgui",
"abspath")
+ dir_pw_display_driver_mipi_dsi =
+ get_path_info("$dir_pigweed_experimental/pw_display_driver_mipi",
+ "abspath")
dir_pw_display_driver_null =
get_path_info("$dir_pigweed_experimental/pw_display_driver_null",
"abspath")
@@ -84,6 +87,14 @@
dir_pw_framebuffer =
get_path_info("$dir_pigweed_experimental/pw_graphics/pw_framebuffer",
"abspath")
+ dir_pw_framebuffer_pool =
+ get_path_info("$dir_pigweed_experimental/pw_graphics/pw_framebuffer_pool",
+ "abspath")
+ dir_pw_mipi_dsi =
+ get_path_info("$dir_pigweed_experimental/pw_mipi_dsi", "abspath")
+ dir_pw_mipi_dsi_mcuxpresso =
+ get_path_info("$dir_pigweed_experimental/pw_mipi_dsi_mcuxpresso",
+ "abspath")
dir_pw_spi_arduino =
get_path_info("$dir_pigweed_experimental/pw_spi_arduino", "abspath")
dir_pw_spi_pico =
diff --git a/pw_display_driver/public/pw_display_driver/display_driver.h b/pw_display_driver/public/pw_display_driver/display_driver.h
index 38075fc..ac96983 100644
--- a/pw_display_driver/public/pw_display_driver/display_driver.h
+++ b/pw_display_driver/public/pw_display_driver/display_driver.h
@@ -22,7 +22,7 @@
namespace pw::display_driver {
// This interface defines a software display driver. This is the software
-// component responsible for *all* communications with a display controller.
+// component responsible for all communications with a display controller.
// The display controller is the hardware component of a display that
// controls pixel values and other physical display properties.
class DisplayDriver {
@@ -32,10 +32,14 @@
// Initialize the display controller.
virtual Status Init() = 0;
+ // Return a framebuffer to which the caller may draw. When drawing is complete
+ // the framebuffer must be returned using ReleaseFramebuffer().
+ virtual pw::framebuffer::FramebufferRgb565 GetFramebuffer() = 0;
+
// Send all pixels in the supplied |framebuffer| to the display controller
- // for the display.
- virtual Status Update(
- const pw::framebuffer::FramebufferRgb565& framebuffer) = 0;
+ // for display.
+ virtual Status ReleaseFramebuffer(
+ pw::framebuffer::FramebufferRgb565 framebuffer) = 0;
// Send a row of pixels to the display. The number of pixels must be <=
// display width.
diff --git a/pw_display_driver_ili9341/BUILD.gn b/pw_display_driver_ili9341/BUILD.gn
index 16044ee..293f242 100644
--- a/pw_display_driver_ili9341/BUILD.gn
+++ b/pw_display_driver_ili9341/BUILD.gn
@@ -31,6 +31,7 @@
public_deps = [
"$dir_pw_digital_io",
"$dir_pw_display_driver:display_driver",
+ "$dir_pw_framebuffer_pool",
"$dir_pw_spi:device",
]
sources = [ "display_driver.cc" ]
diff --git a/pw_display_driver_ili9341/display_driver.cc b/pw_display_driver_ili9341/display_driver.cc
index d9d9c2e..2ff2f77 100644
--- a/pw_display_driver_ili9341/display_driver.cc
+++ b/pw_display_driver_ili9341/display_driver.cc
@@ -291,7 +291,15 @@
return OkStatus();
}
-Status DisplayDriverILI9341::Update(const FramebufferRgb565& frame_buffer) {
+FramebufferRgb565 DisplayDriverILI9341::GetFramebuffer() {
+ return FramebufferRgb565(config_.pool_data.fb_addr[0],
+ config_.pool_data.size.width,
+ config_.pool_data.size.height,
+ config_.pool_data.row_bytes);
+}
+
+Status DisplayDriverILI9341::ReleaseFramebuffer(
+ FramebufferRgb565 frame_buffer) {
auto transaction = config_.spi_device_16_bit.StartTransaction(
ChipSelectBehavior::kPerTransaction);
const uint16_t* fb_data = frame_buffer.GetFramebufferData();
diff --git a/pw_display_driver_ili9341/public/pw_display_driver_ili9341/display_driver.h b/pw_display_driver_ili9341/public/pw_display_driver_ili9341/display_driver.h
index 6495557..8bcdf0f 100644
--- a/pw_display_driver_ili9341/public/pw_display_driver_ili9341/display_driver.h
+++ b/pw_display_driver_ili9341/public/pw_display_driver_ili9341/display_driver.h
@@ -17,6 +17,7 @@
#include "pw_digital_io/digital_io.h"
#include "pw_display_driver/display_driver.h"
+#include "pw_framebuffer_pool/framebuffer_pool.h"
#include "pw_spi/device.h"
namespace pw::display_driver {
@@ -36,13 +37,16 @@
// The SPI device to which the display controller is connected for 16-bit
// data.
pw::spi::Device& spi_device_16_bit;
+ const pw::framebuffer::pool::PoolData& pool_data;
};
DisplayDriverILI9341(const Config& config);
// DisplayDriver implementation:
Status Init() override;
- Status Update(const pw::framebuffer::FramebufferRgb565& framebuffer);
+ pw::framebuffer::FramebufferRgb565 GetFramebuffer(void) override;
+ Status ReleaseFramebuffer(
+ pw::framebuffer::FramebufferRgb565 framebuffer) override;
Status WriteRow(span<uint16_t> row_pixels, int row_idx, int col_idx) override;
int GetWidth() const override;
int GetHeight() const override;
diff --git a/pw_display_driver_imgui/BUILD.gn b/pw_display_driver_imgui/BUILD.gn
index fee948f..0ed596d 100644
--- a/pw_display_driver_imgui/BUILD.gn
+++ b/pw_display_driver_imgui/BUILD.gn
@@ -29,7 +29,10 @@
"$dir_pw_coordinates",
"$dir_pw_log",
]
- public_deps = [ "$dir_pw_display_driver:display_driver" ]
+ public_deps = [
+ "$dir_pw_display_driver:display_driver",
+ "$dir_pw_framebuffer_pool",
+ ]
sources = [ "display_driver.cc" ]
remove_configs = [ "$dir_pw_build:strict_warnings" ]
}
diff --git a/pw_display_driver_imgui/display_driver.cc b/pw_display_driver_imgui/display_driver.cc
index ec1534e..958f0e1 100644
--- a/pw_display_driver_imgui/display_driver.cc
+++ b/pw_display_driver_imgui/display_driver.cc
@@ -162,7 +162,9 @@
} // namespace
-DisplayDriverImgUI::DisplayDriverImgUI() = default;
+DisplayDriverImgUI::DisplayDriverImgUI(
+ const pw::framebuffer::pool::PoolData& pool_data)
+ : pool_data_(pool_data) {}
Status DisplayDriverImgUI::Init() {
// Setup window
@@ -390,7 +392,16 @@
}
}
-Status DisplayDriverImgUI::Update(const FramebufferRgb565& framebuffer) {
+FramebufferRgb565 DisplayDriverImgUI::GetFramebuffer() {
+ return FramebufferRgb565(pool_data_.fb_addr[0],
+ pool_data_.size.width,
+ pool_data_.size.height,
+ pool_data_.row_bytes);
+}
+
+Status DisplayDriverImgUI::ReleaseFramebuffer(FramebufferRgb565 framebuffer) {
+ if (!framebuffer.IsValid())
+ return Status::InvalidArgument();
RecreateLcdTexture();
// Copy frame_buffer into lcd_pixel_data
diff --git a/pw_display_driver_imgui/public/pw_display_driver_imgui/display_driver.h b/pw_display_driver_imgui/public/pw_display_driver_imgui/display_driver.h
index 97d90ba..16dee11 100644
--- a/pw_display_driver_imgui/public/pw_display_driver_imgui/display_driver.h
+++ b/pw_display_driver_imgui/public/pw_display_driver_imgui/display_driver.h
@@ -15,19 +15,22 @@
#include "pw_coordinates/vec_int.h"
#include "pw_display_driver/display_driver.h"
+#include "pw_framebuffer_pool/framebuffer_pool.h"
namespace pw::display_driver {
class DisplayDriverImgUI : public DisplayDriver {
public:
- DisplayDriverImgUI();
+ DisplayDriverImgUI(const pw::framebuffer::pool::PoolData& pool_data);
bool NewTouchEvent();
pw::coordinates::Vec3Int GetTouchPoint();
// pw::display_driver::DisplayDriver implementation:
Status Init() override;
- Status Update(const pw::framebuffer::FramebufferRgb565& framebuffer) override;
+ pw::framebuffer::FramebufferRgb565 GetFramebuffer() override;
+ Status ReleaseFramebuffer(
+ pw::framebuffer::FramebufferRgb565 framebuffer) override;
Status WriteRow(span<uint16_t> row_pixels, int row_idx, int col_idx) override;
int GetWidth() const override;
int GetHeight() const override;
@@ -35,6 +38,8 @@
private:
void RecreateLcdTexture();
void Render();
+
+ const pw::framebuffer::pool::PoolData& pool_data_;
};
} // namespace pw::display_driver
diff --git a/pw_display_driver_mipi/BUILD.gn b/pw_display_driver_mipi/BUILD.gn
new file mode 100644
index 0000000..1d74a1d
--- /dev/null
+++ b/pw_display_driver_mipi/BUILD.gn
@@ -0,0 +1,36 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+config("default_config") {
+ include_dirs = [ "public" ]
+}
+
+pw_source_set("pw_display_driver_mipi") {
+ public_configs = [ ":default_config" ]
+ public = [ "public/pw_display_driver_mipi/display_driver.h" ]
+ deps = [ "$dir_pw_log" ]
+ public_deps = [
+ "$dir_pw_coordinates",
+ "$dir_pw_display_driver:display_driver",
+ "$dir_pw_mipi_dsi",
+ "$dir_pw_spi:device",
+ "$dir_pw_status",
+ ]
+ sources = [ "display_driver.cc" ]
+ remove_configs = [ "$dir_pw_build:strict_warnings" ]
+}
diff --git a/pw_display_driver_mipi/display_driver.cc b/pw_display_driver_mipi/display_driver.cc
new file mode 100644
index 0000000..f75f4b8
--- /dev/null
+++ b/pw_display_driver_mipi/display_driver.cc
@@ -0,0 +1,48 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_display_driver_mipi/display_driver.h"
+
+using pw::framebuffer::FramebufferRgb565;
+
+namespace pw::display_driver {
+
+DisplayDriverMipiDsi::DisplayDriverMipiDsi(
+ pw::mipi::dsi::Device& device, pw::coordinates::Size<int> display_size)
+ : device_(device), display_size_(display_size) {}
+
+DisplayDriverMipiDsi::~DisplayDriverMipiDsi() = default;
+
+Status DisplayDriverMipiDsi::Init() { return OkStatus(); }
+
+FramebufferRgb565 DisplayDriverMipiDsi::GetFramebuffer() {
+ return device_.GetFramebuffer();
+}
+
+Status DisplayDriverMipiDsi::ReleaseFramebuffer(FramebufferRgb565 framebuffer) {
+ return device_.ReleaseFramebuffer(std::move(framebuffer));
+};
+
+Status DisplayDriverMipiDsi::WriteRow(span<uint16_t> row_pixels,
+ int row_idx,
+ int col_idx) {
+ // TODO(cmumford): Implement for debugging purposes.
+ return Status::Unimplemented();
+}
+
+int DisplayDriverMipiDsi::GetWidth() const { return display_size_.width; };
+
+int DisplayDriverMipiDsi::GetHeight() const { return display_size_.height; }
+
+} // namespace pw::display_driver
diff --git a/pw_display_driver_mipi/public/pw_display_driver_mipi/display_driver.h b/pw_display_driver_mipi/public/pw_display_driver_mipi/display_driver.h
new file mode 100644
index 0000000..6027fca
--- /dev/null
+++ b/pw_display_driver_mipi/public/pw_display_driver_mipi/display_driver.h
@@ -0,0 +1,44 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_coordinates/vec_int.h"
+#include "pw_display_driver/display_driver.h"
+#include "pw_mipi_dsi/device.h"
+
+namespace pw::display_driver {
+
+// A display driver that communicates with a display controller over the
+// MIPI Display Serial Interface (MIPI DSI).
+class DisplayDriverMipiDsi : public DisplayDriver {
+ public:
+ DisplayDriverMipiDsi(pw::mipi::dsi::Device& device,
+ pw::coordinates::Size<int> display_size);
+ ~DisplayDriverMipiDsi() override;
+
+ // pw::display_driver::DisplayDriver implementation:
+ Status Init() override;
+ pw::framebuffer::FramebufferRgb565 GetFramebuffer(void) override;
+ Status ReleaseFramebuffer(
+ pw::framebuffer::FramebufferRgb565 framebuffer) override;
+ Status WriteRow(span<uint16_t> row_pixels, int row_idx, int col_idx) override;
+ int GetWidth() const override;
+ int GetHeight() const override;
+
+ private:
+ pw::mipi::dsi::Device& device_;
+ const pw::coordinates::Size<int> display_size_;
+};
+
+} // namespace pw::display_driver
diff --git a/pw_display_driver_null/public/pw_display_driver_null/display_driver.h b/pw_display_driver_null/public/pw_display_driver_null/display_driver.h
index c63da73..e4ef816 100644
--- a/pw_display_driver_null/public/pw_display_driver_null/display_driver.h
+++ b/pw_display_driver_null/public/pw_display_driver_null/display_driver.h
@@ -25,8 +25,11 @@
// pw::display_driver::DisplayDriver implementation:
pw::Status Init() override { return pw::OkStatus(); }
- pw::Status Update(const pw::framebuffer::FramebufferRgb565&) override {
- return pw::OkStatus();
+ pw::framebuffer::FramebufferRgb565 GetFramebuffer(void) override {
+ return pw::framebuffer::FramebufferRgb565();
+ }
+ Status ReleaseFramebuffer(pw::framebuffer::FramebufferRgb565) override {
+ return OkStatus();
}
Status WriteRow(span<uint16_t>, int, int) override { return pw::OkStatus(); }
int GetWidth() const override { return 0; };
diff --git a/pw_display_driver_st7789/display_driver.cc b/pw_display_driver_st7789/display_driver.cc
index 5deb13a..3a811fd 100644
--- a/pw_display_driver_st7789/display_driver.cc
+++ b/pw_display_driver_st7789/display_driver.cc
@@ -183,7 +183,15 @@
return OkStatus();
}
-Status DisplayDriverST7789::Update(const FramebufferRgb565& frame_buffer) {
+FramebufferRgb565 DisplayDriverST7789::GetFramebuffer() {
+ return FramebufferRgb565(config_.pool_data.fb_addr[0],
+ config_.pool_data.size.width,
+ config_.pool_data.size.height,
+ config_.pool_data.row_bytes);
+}
+
+Status DisplayDriverST7789::ReleaseFramebuffer(
+ pw::framebuffer::FramebufferRgb565 frame_buffer) {
// Let controller know a write is coming.
{
auto transaction = config_.spi_device_8_bit.StartTransaction(
diff --git a/pw_display_driver_st7789/public/pw_display_driver_st7789/display_driver.h b/pw_display_driver_st7789/public/pw_display_driver_st7789/display_driver.h
index b3cdd68..23beb6d 100644
--- a/pw_display_driver_st7789/public/pw_display_driver_st7789/display_driver.h
+++ b/pw_display_driver_st7789/public/pw_display_driver_st7789/display_driver.h
@@ -36,6 +36,7 @@
// The SPI device to which the display controller is connected for 16-bit
// data.
pw::spi::Device& spi_device_16_bit;
+ const pw::framebuffer::pool::PoolData& pool_data;
int screen_width = 320;
int screen_height = 240;
};
@@ -44,7 +45,8 @@
// DisplayDriver implementation:
Status Init() override;
- Status Update(const pw::framebuffer::FramebufferRgb565& framebuffer);
+ FramebufferRgb565 GetFramebuffer(void) override;
+ Status ReleaseFramebuffer(FramebufferRgb565 framebuffer) override;
Status WriteRow(span<uint16_t> row_pixels, int row_idx, int col_idx) override;
int GetWidth() const override { return config_.screen_width; }
int GetHeight() const override { return config_.screen_height; }
diff --git a/pw_graphics/pw_display/BUILD.gn b/pw_graphics/pw_display/BUILD.gn
index 05e8ecd..82f9b31 100644
--- a/pw_graphics/pw_display/BUILD.gn
+++ b/pw_graphics/pw_display/BUILD.gn
@@ -27,6 +27,7 @@
"$dir_pw_coordinates",
"$dir_pw_display_driver:display_driver",
"$dir_pw_framebuffer",
+ "$dir_pw_framebuffer_pool",
"$dir_pw_status",
]
sources = [ "display.cc" ]
diff --git a/pw_graphics/pw_display/display.cc b/pw_graphics/pw_display/display.cc
index 22fe620..48d2ea9 100644
--- a/pw_graphics/pw_display/display.cc
+++ b/pw_graphics/pw_display/display.cc
@@ -13,6 +13,10 @@
// the License.
#include "pw_display/display.h"
+#include <utility>
+
+#include "pw_framebuffer/rgb565.h"
+#include "pw_framebuffer_pool/framebuffer_pool.h"
#include "pw_status/try.h"
using pw::color::color_rgb565_t;
@@ -20,9 +24,9 @@
namespace pw::display {
-Display::Display(FramebufferRgb565 framebuffer,
- pw::display_driver::DisplayDriver& display_driver)
- : framebuffer_(std::move(framebuffer)), display_driver_(display_driver) {}
+Display::Display(pw::display_driver::DisplayDriver& display_driver,
+ pw::coordinates::Size<int> size)
+ : display_driver_(display_driver), size_(size) {}
Display::~Display() = default;
@@ -41,8 +45,8 @@
constexpr int kBytesPerPixel = sizeof(color_rgb565_t);
const int num_src_row_pixels = framebuffer.GetRowBytes() / kBytesPerPixel;
- const int num_dst_rows = display_driver_.GetHeight();
- const int num_dst_cols = display_driver_.GetWidth();
+ const int num_dst_rows = size_.height;
+ const int num_dst_cols = size_.width;
for (int dst_row_idx = 0; dst_row_idx < num_dst_rows; dst_row_idx++) {
int src_row_idx = dst_row_idx * fb_last_row_idx / (num_dst_rows - 1);
PW_ASSERT(src_row_idx >= 0);
@@ -76,20 +80,17 @@
}
FramebufferRgb565 Display::GetFramebuffer() {
- return FramebufferRgb565(framebuffer_.GetFramebufferData(),
- framebuffer_.GetWidth(),
- framebuffer_.GetHeight(),
- framebuffer_.GetRowBytes());
+ return display_driver_.GetFramebuffer();
}
Status Display::ReleaseFramebuffer(FramebufferRgb565 framebuffer) {
if (!framebuffer.IsValid())
return Status::InvalidArgument();
- if (framebuffer.GetWidth() != display_driver_.GetWidth() ||
- framebuffer.GetHeight() != display_driver_.GetHeight()) {
+ if (framebuffer.GetWidth() != size_.width ||
+ framebuffer.GetHeight() != size_.height) {
return UpdateNearestNeighbor(framebuffer);
}
- return display_driver_.Update(framebuffer);
+ return display_driver_.ReleaseFramebuffer(std::move(framebuffer));
}
} // namespace pw::display
diff --git a/pw_graphics/pw_display/display_test.cc b/pw_graphics/pw_display/display_test.cc
index f598c5b..7f161ab 100644
--- a/pw_graphics/pw_display/display_test.cc
+++ b/pw_graphics/pw_display/display_test.cc
@@ -22,16 +22,18 @@
using pw::color::color_rgb565_t;
using pw::display_driver::DisplayDriver;
using pw::framebuffer::FramebufferRgb565;
+using Size = pw::coordinates::Size<int>;
namespace pw::display {
namespace {
-constexpr size_t kMaxSavedParams = 10;
+constexpr size_t kMaxSavedParams = 20;
enum class CallFunc {
Unset,
- Update,
+ GetFramebuffer,
+ ReleaseFramebuffer,
WriteRow,
};
@@ -39,7 +41,7 @@
CallFunc call_func = CallFunc::Unset;
struct {
color_rgb565_t* fb_data = nullptr;
- } update;
+ } release_framebuffer;
struct {
size_t num_pixels = 0;
int row_idx = 0;
@@ -49,15 +51,23 @@
class TestDisplayDriver : public DisplayDriver {
public:
- TestDisplayDriver(int width, int height) : width_(width), height_(height) {}
+ TestDisplayDriver(FramebufferRgb565 fb) : framebuffer_(std::move(fb)) {}
virtual ~TestDisplayDriver() = default;
Status Init() override { return OkStatus(); }
- Status Update(const FramebufferRgb565& framebuffer) override {
+ FramebufferRgb565 GetFramebuffer() override {
+ return FramebufferRgb565(framebuffer_.GetFramebufferData(),
+ framebuffer_.GetWidth(),
+ framebuffer_.GetHeight(),
+ framebuffer_.GetRowBytes());
+ }
+
+ Status ReleaseFramebuffer(FramebufferRgb565 framebuffer) override {
if (next_call_param_idx_ < kMaxSavedParams) {
- call_params_[next_call_param_idx_].call_func = CallFunc::Update;
- call_params_[next_call_param_idx_].update.fb_data =
+ call_params_[next_call_param_idx_].call_func =
+ CallFunc::ReleaseFramebuffer;
+ call_params_[next_call_param_idx_].release_framebuffer.fb_data =
framebuffer.GetFramebufferData();
next_call_param_idx_++;
}
@@ -78,9 +88,9 @@
return OkStatus();
}
- int GetWidth() const override { return width_; }
+ int GetWidth() const override { return framebuffer_.GetWidth(); }
- int GetHeight() const override { return height_; }
+ int GetHeight() const override { return framebuffer_.GetHeight(); }
int GetNumCalls() const {
int count = 0;
@@ -101,24 +111,21 @@
private:
size_t next_call_param_idx_ = 0;
std::array<CallParams, kMaxSavedParams> call_params_;
- const int width_;
- const int height_;
+ const FramebufferRgb565 framebuffer_;
};
TEST(Display, ReleaseNoResize) {
constexpr int kFramebufferWidth = 2;
constexpr int kFramebufferHeight = 1;
+ constexpr Size kDisplaySize = {kFramebufferWidth, kFramebufferHeight};
constexpr int kNumPixels = kFramebufferWidth * kFramebufferHeight;
constexpr int kFramebufferRowBytes =
sizeof(color_rgb565_t) * kFramebufferWidth;
color_rgb565_t pixel_data[kNumPixels];
- TestDisplayDriver test_driver(kFramebufferWidth, kFramebufferHeight);
- Display display(FramebufferRgb565(pixel_data,
- kFramebufferWidth,
- kFramebufferHeight,
- kFramebufferRowBytes),
- test_driver);
+ TestDisplayDriver test_driver(FramebufferRgb565(
+ pixel_data, kFramebufferWidth, kFramebufferHeight, kFramebufferRowBytes));
+ Display display(test_driver, kDisplaySize);
FramebufferRgb565 fb = display.GetFramebuffer();
EXPECT_TRUE(fb.IsValid());
EXPECT_EQ(kFramebufferWidth, fb.GetWidth());
@@ -128,13 +135,12 @@
display.ReleaseFramebuffer(std::move(fb));
ASSERT_EQ(1, test_driver.GetNumCalls());
auto call = test_driver.GetCall(0);
- EXPECT_EQ(CallFunc::Update, call.call_func);
- EXPECT_EQ(pixel_data, call.update.fb_data);
+ EXPECT_EQ(CallFunc::ReleaseFramebuffer, call.call_func);
+ EXPECT_EQ(pixel_data, call.release_framebuffer.fb_data);
}
TEST(Display, ReleaseSmallResize) {
- constexpr int kDisplayWidth = 8;
- constexpr int kDisplayHeight = 4;
+ constexpr Size kDisplaySize = {8, 4};
constexpr int kFramebufferWidth = 2;
constexpr int kFramebufferHeight = 1;
constexpr int kNumPixels = kFramebufferWidth * kFramebufferHeight;
@@ -142,12 +148,9 @@
sizeof(color_rgb565_t) * kFramebufferWidth;
color_rgb565_t pixel_data[kNumPixels];
- TestDisplayDriver test_driver(kDisplayWidth, kDisplayHeight);
- Display display(FramebufferRgb565(pixel_data,
- kFramebufferWidth,
- kFramebufferHeight,
- kFramebufferRowBytes),
- test_driver);
+ TestDisplayDriver test_driver(FramebufferRgb565(
+ pixel_data, kFramebufferWidth, kFramebufferHeight, kFramebufferRowBytes));
+ Display display(test_driver, kDisplaySize);
FramebufferRgb565 fb = display.GetFramebuffer();
EXPECT_TRUE(fb.IsValid());
EXPECT_EQ(kFramebufferWidth, fb.GetWidth());
@@ -183,8 +186,7 @@
TEST(Display, ReleaseWideResize) {
// Display width > resize buffer (80 px.) will cause two writes per row.
- constexpr int kDisplayWidth = 90;
- constexpr int kDisplayHeight = 4;
+ constexpr Size kDisplaySize = {90, 4};
constexpr int kFramebufferWidth = 2;
constexpr int kFramebufferHeight = 1;
constexpr int kNumPixels = kFramebufferWidth * kFramebufferHeight;
@@ -192,12 +194,9 @@
sizeof(color_rgb565_t) * kFramebufferWidth;
color_rgb565_t pixel_data[kNumPixels];
- TestDisplayDriver test_driver(kDisplayWidth, kDisplayHeight);
- Display display(FramebufferRgb565(pixel_data,
- kFramebufferWidth,
- kFramebufferHeight,
- kFramebufferRowBytes),
- test_driver);
+ TestDisplayDriver test_driver(FramebufferRgb565(
+ pixel_data, kFramebufferWidth, kFramebufferHeight, kFramebufferRowBytes));
+ Display display(test_driver, kDisplaySize);
FramebufferRgb565 fb = display.GetFramebuffer();
EXPECT_TRUE(fb.IsValid());
EXPECT_EQ(kFramebufferWidth, fb.GetWidth());
diff --git a/pw_graphics/pw_display/public/pw_display/display.h b/pw_graphics/pw_display/public/pw_display/display.h
index ac1b8c3..473184f 100644
--- a/pw_graphics/pw_display/public/pw_display/display.h
+++ b/pw_graphics/pw_display/public/pw_display/display.h
@@ -16,6 +16,7 @@
#include "pw_coordinates/vec_int.h"
#include "pw_display_driver/display_driver.h"
#include "pw_framebuffer/rgb565.h"
+#include "pw_framebuffer_pool/framebuffer_pool.h"
#include "pw_status/status.h"
namespace pw::display {
@@ -26,15 +27,14 @@
// use for rendering.
class Display {
public:
- Display(pw::framebuffer::FramebufferRgb565 framebuffer,
- pw::display_driver::DisplayDriver& display_driver);
+ Display(pw::display_driver::DisplayDriver& display_driver,
+ pw::coordinates::Size<int> size);
virtual ~Display();
- // Initialize the display instance.
- Status Init() { return OkStatus(); }
-
// Return a framebuffer to which the caller may draw. When drawing is complete
- // the framebuffer must be returned using ReleaseFramebuffer().
+ // the framebuffer must be returned using ReleaseFramebuffer(). An invalid
+ // framebuffer may be returned, so the caller should verify it is valid
+ // before use.
pw::framebuffer::FramebufferRgb565 GetFramebuffer();
// Release the framebuffer back to the display. The display will
@@ -46,10 +46,10 @@
Status ReleaseFramebuffer(pw::framebuffer::FramebufferRgb565 framebuffer);
// Return the width (in pixels) of the associated display.
- int GetWidth() const { return framebuffer_.GetWidth(); }
+ int GetWidth() const { return size_.width; }
// Return the height (in pixels) of the associated display.
- int GetHeight() const { return framebuffer_.GetHeight(); }
+ int GetHeight() const { return size_.height; }
// Does the associated screen have a touch screen?
virtual bool TouchscreenAvailable() const { return false; }
@@ -68,8 +68,8 @@
Status UpdateNearestNeighbor(
const pw::framebuffer::FramebufferRgb565& framebuffer);
- pw::framebuffer::FramebufferRgb565 framebuffer_;
pw::display_driver::DisplayDriver& display_driver_;
+ const pw::coordinates::Size<int> size_;
};
} // namespace pw::display
diff --git a/pw_graphics/pw_display_imgui/display.cc b/pw_graphics/pw_display_imgui/display.cc
index 71c4cac..25962e0 100644
--- a/pw_graphics/pw_display_imgui/display.cc
+++ b/pw_graphics/pw_display_imgui/display.cc
@@ -16,10 +16,9 @@
namespace pw::display {
DisplayImgUI::DisplayImgUI(
- pw::framebuffer::FramebufferRgb565 framebuffer,
- pw::display_driver::DisplayDriverImgUI& display_driver)
- : Display(std::move(framebuffer), display_driver),
- display_driver_(display_driver) {}
+ pw::display_driver::DisplayDriverImgUI& display_driver,
+ pw::coordinates::Size<int> size)
+ : Display(display_driver, size), display_driver_(display_driver) {}
DisplayImgUI::~DisplayImgUI() = default;
diff --git a/pw_graphics/pw_display_imgui/public/pw_display_imgui/display.h b/pw_graphics/pw_display_imgui/public/pw_display_imgui/display.h
index ac5940e..52c43e3 100644
--- a/pw_graphics/pw_display_imgui/public/pw_display_imgui/display.h
+++ b/pw_graphics/pw_display_imgui/public/pw_display_imgui/display.h
@@ -16,7 +16,6 @@
#include "pw_coordinates/vec_int.h"
#include "pw_display/display.h"
#include "pw_display_driver_imgui/display_driver.h"
-#include "pw_framebuffer/rgb565.h"
#include "pw_status/status.h"
namespace pw::display {
@@ -24,8 +23,8 @@
// A display that uses ImgUI and supports touch input.
class DisplayImgUI : public Display {
public:
- DisplayImgUI(pw::framebuffer::FramebufferRgb565 framebuffer,
- pw::display_driver::DisplayDriverImgUI& display_driver);
+ DisplayImgUI(pw::display_driver::DisplayDriverImgUI& display_driver,
+ pw::coordinates::Size<int> size);
~DisplayImgUI();
bool TouchscreenAvailable() const override { return true; }
diff --git a/pw_graphics/pw_framebuffer_pool/BUILD.gn b/pw_graphics/pw_framebuffer_pool/BUILD.gn
new file mode 100644
index 0000000..52777a2
--- /dev/null
+++ b/pw_graphics/pw_framebuffer_pool/BUILD.gn
@@ -0,0 +1,30 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+config("default_config") {
+ include_dirs = [ "public" ]
+}
+
+pw_source_set("pw_framebuffer_pool") {
+ public_configs = [ ":default_config" ]
+ public = [ "public/pw_framebuffer_pool/framebuffer_pool.h" ]
+ public_deps = [
+ "$dir_pw_color",
+ "$dir_pw_coordinates",
+ ]
+}
diff --git a/pw_graphics/pw_framebuffer_pool/public/pw_framebuffer_pool/framebuffer_pool.h b/pw_graphics/pw_framebuffer_pool/public/pw_framebuffer_pool/framebuffer_pool.h
new file mode 100644
index 0000000..8dda2f2
--- /dev/null
+++ b/pw_graphics/pw_framebuffer_pool/public/pw_framebuffer_pool/framebuffer_pool.h
@@ -0,0 +1,34 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#pragma once
+
+#include <array>
+
+#include "pw_color/color.h"
+#include "pw_coordinates/vec_int.h"
+
+namespace pw::framebuffer::pool {
+
+constexpr size_t kMaxFramebufferCount = 3;
+
+struct PoolData {
+ std::array<pw::color::color_rgb565_t*, kMaxFramebufferCount> fb_addr;
+ size_t num_fb; // <= fb_addr.size().
+ pw::coordinates::Size<int> size;
+ int row_bytes;
+ pw::coordinates::Vector2<int> start;
+};
+
+} // namespace pw::framebuffer::pool
diff --git a/pw_mipi_dsi/BUILD.gn b/pw_mipi_dsi/BUILD.gn
new file mode 100644
index 0000000..d0ac6d1
--- /dev/null
+++ b/pw_mipi_dsi/BUILD.gn
@@ -0,0 +1,36 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_docgen/docs.gni")
+
+config("public_include_path") {
+ include_dirs = [ "public" ]
+ visibility = [ ":*" ]
+}
+
+pw_source_set("pw_mipi_dsi") {
+ public_configs = [ ":public_include_path" ]
+ public = [ "public/pw_mipi_dsi/device.h" ]
+ public_deps = [
+ "$dir_pw_framebuffer",
+ "$dir_pw_status",
+ ]
+}
+
+pw_doc_group("docs") {
+ sources = [ "docs.rst" ]
+}
diff --git a/pw_mipi_dsi/docs.rst b/pw_mipi_dsi/docs.rst
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pw_mipi_dsi/docs.rst
diff --git a/pw_mipi_dsi/public/pw_mipi_dsi/device.h b/pw_mipi_dsi/public/pw_mipi_dsi/device.h
new file mode 100644
index 0000000..4fb6d98
--- /dev/null
+++ b/pw_mipi_dsi/public/pw_mipi_dsi/device.h
@@ -0,0 +1,46 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#pragma once
+
+#include "pw_framebuffer/rgb565.h"
+#include "pw_status/status.h"
+
+namespace pw::mipi::dsi {
+
+using pw::framebuffer::FramebufferRgb565;
+
+// Interface for to a MIPI Display Serial Interface(1) implementation.
+//
+// (1) https://www.mipi.org/specifications/dsi
+class Device {
+ public:
+ constexpr static size_t kMaxFramebufferCount = 3;
+
+ virtual ~Device() = default;
+
+ // Retrieve a framebuffer for rendering. An invalid framebuffer will be
+ // returned if there are no available framebuffers which can happen if
+ // all framebuffers are in the process of being sent to the display.
+ virtual pw::framebuffer::FramebufferRgb565 GetFramebuffer(void) = 0;
+
+ // Begin the process of transporting the |framebuffer| to the display.
+ virtual Status ReleaseFramebuffer(
+ pw::framebuffer::FramebufferRgb565 framebuffer) = 0;
+
+ protected:
+ Device() = default;
+};
+
+} // namespace pw::mipi::dsi
diff --git a/pw_mipi_dsi_mcuxpresso/BUILD.gn b/pw_mipi_dsi_mcuxpresso/BUILD.gn
new file mode 100644
index 0000000..45ba90d
--- /dev/null
+++ b/pw_mipi_dsi_mcuxpresso/BUILD.gn
@@ -0,0 +1,60 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_third_party/mcuxpresso/mcuxpresso.gni")
+
+declare_args() {
+ # Use Smart DMA for MIPI DSI transfers (0: disabled, 1: enabled).
+ pw_mipi_dsi_use_smart_dma = "1"
+}
+
+config("default_config") {
+ include_dirs = [ "public" ]
+ visibility = [ ":*" ]
+
+ cflags = [ "-DUSE_DSI_SMARTDMA=" + pw_mipi_dsi_use_smart_dma ]
+}
+
+pw_source_set("pw_mipi_dsi_mcuxpresso") {
+ public_configs = [ ":default_config" ]
+ public = [
+ "public/pw_mipi_dsi_mcuxpresso/device.h",
+ "public/pw_mipi_dsi_mcuxpresso/framebuffer_device.h",
+ ]
+ public_deps = [
+ "$dir_pw_framebuffer",
+ "$dir_pw_framebuffer_pool",
+ "$dir_pw_mipi_dsi",
+ "$dir_pw_status",
+ ]
+ deps = [
+ "$dir_pw_assert",
+ "$dir_pw_log",
+ "$pw_third_party_mcuxpresso_SDK",
+ ]
+ sources = [
+ "common.cc",
+ "common.h",
+ "device.cc",
+ "framebuffer_device.cc",
+ ]
+}
+
+pw_doc_group("docs") {
+ sources = [ "docs.rst" ]
+}
diff --git a/pw_mipi_dsi_mcuxpresso/common.cc b/pw_mipi_dsi_mcuxpresso/common.cc
new file mode 100644
index 0000000..9b7a27b
--- /dev/null
+++ b/pw_mipi_dsi_mcuxpresso/common.cc
@@ -0,0 +1,43 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "common.h"
+
+namespace pw::mipi::dsi {
+
+Status MCUXpressoToPigweedStatus(status_t mcux_status) {
+ switch (mcux_status) {
+ case kStatus_Success:
+ return OkStatus();
+ case kStatus_Fail:
+ return Status::Internal();
+ case kStatus_ReadOnly:
+ return Status::PermissionDenied();
+ case kStatus_OutOfRange:
+ return Status::OutOfRange();
+ case kStatus_InvalidArgument:
+ return Status::InvalidArgument();
+ case kStatus_Timeout:
+ return Status::DeadlineExceeded();
+ case kStatus_NoTransferInProgress:
+ return Status::FailedPrecondition();
+ case kStatus_Busy:
+ return Status::FailedPrecondition();
+ case kStatus_NoData:
+ return Status::Unavailable();
+ }
+ return Status::Internal();
+}
+
+} // namespace pw::mipi::dsi
diff --git a/pw_mipi_dsi_mcuxpresso/common.h b/pw_mipi_dsi_mcuxpresso/common.h
new file mode 100644
index 0000000..263f380
--- /dev/null
+++ b/pw_mipi_dsi_mcuxpresso/common.h
@@ -0,0 +1,24 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#pragma once
+
+#include "fsl_common.h"
+#include "pw_status/status.h"
+
+namespace pw::mipi::dsi {
+
+Status MCUXpressoToPigweedStatus(status_t mcux_status);
+
+} // namespace pw::mipi::dsi
diff --git a/pw_mipi_dsi_mcuxpresso/device.cc b/pw_mipi_dsi_mcuxpresso/device.cc
new file mode 100644
index 0000000..4e929eb
--- /dev/null
+++ b/pw_mipi_dsi_mcuxpresso/device.cc
@@ -0,0 +1,448 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_mipi_dsi_mcuxpresso/device.h"
+
+#include <cstring>
+
+#include "board.h"
+#include "common.h"
+#include "fsl_gpio.h"
+#include "fsl_inputmux.h"
+#include "fsl_mipi_dsi.h"
+#include "fsl_mipi_dsi_smartdma.h"
+#include "fsl_power.h"
+#include "pin_mux.h"
+
+using pw::color::color_rgb565_t;
+using pw::framebuffer::FramebufferRgb565;
+
+namespace pw::mipi::dsi {
+
+namespace {
+
+constexpr uint8_t kMipiDsiLaneNum = 1;
+constexpr IRQn_Type kMipiDsiIrqn = MIPI_IRQn;
+constexpr int kVideoLayer = 0;
+
+// This class is currently a singleton because the some callbacks and IRQ
+// handlers do not have a user-data param.
+MCUXpressoDevice* s_device;
+
+extern "C" {
+void GPIO_INTA_DriverIRQHandler(void) {
+ uint32_t intStat = GPIO_PortGetInterruptStatus(GPIO, BOARD_MIPI_TE_PORT, 0);
+
+ GPIO_PortClearInterruptFlags(GPIO, BOARD_MIPI_TE_PORT, 0, intStat);
+
+ if (s_device && intStat & (1U << BOARD_MIPI_TE_PIN)) {
+ s_device->DisplayTEPinHandler();
+ }
+}
+
+void SDMA_DriverIRQHandler(void) { SMARTDMA_HandleIRQ(); }
+} // extern "C"
+
+} // namespace
+
+MCUXpressoDevice::MCUXpressoDevice(
+ const pw::framebuffer::pool::PoolData& fb_pool,
+ const pw::coordinates::Size<uint16_t>& panel_size,
+ video_pixel_format_t pixel_format)
+ : fb_pool_(fb_pool),
+ fbdev_(kVideoLayer),
+ dsi_device_({
+ .virtualChannel = 0,
+ .xferFunc = MCUXpressoDevice::DSI_Transfer,
+ .memWriteFunc = MCUXpressoDevice::DSI_MemWrite,
+ .callback = nullptr,
+ .userData = nullptr,
+ }),
+ rm67162_resource_({
+ .dsiDevice = &dsi_device_,
+ .pullResetPin = MCUXpressoDevice::PullPanelResetPin,
+ .pullPowerPin = MCUXpressoDevice::PullPanelPowerPin,
+ }),
+ display_handle_({
+ .resource = &rm67162_resource_,
+ .ops = &rm67162_ops,
+ .width = panel_size.width,
+ .height = panel_size.height,
+ .pixelFormat = pixel_format,
+ }),
+ dc_fb_dsi_cmd_handle_({
+ .dsiDevice = &dsi_device_,
+ .panelHandle = &display_handle_,
+ .initTimes = 0,
+ .enabledLayerCount = 0,
+ .layers = {},
+ .useTEPin = true,
+ }),
+ panel_config_({
+ .commonConfig =
+ {
+ .resolution =
+ FSL_VIDEO_RESOLUTION(panel_size.width, panel_size.height),
+ .hsw = 0, // Unused.
+ .hfp = 0, // Unused.
+ .hbp = 0, // Unused.
+ .vsw = 0, // Unused.
+ .vfp = 0, // Unused.
+ .vbp = 0, // Unused.
+ .controlFlags = 0,
+ .dsiLanes = kMipiDsiLaneNum,
+ .pixelClock_Hz = 0, // Unsure of correct value.
+ .pixelFormat = pixel_format,
+ },
+ .useTEPin = true,
+ }),
+ dc_({
+ .ops = &g_dcFbOpsDsiCmd,
+ .prvData = &dc_fb_dsi_cmd_handle_,
+ .config = &panel_config_,
+ }) {
+ PW_ASSERT(s_device == nullptr);
+ s_device = this;
+}
+
+MCUXpressoDevice::~MCUXpressoDevice() = default;
+
+Status MCUXpressoDevice::Init() {
+ if (fb_pool_.num_fb == 0)
+ return Status::InvalidArgument();
+
+ Status s = PrepareDisplayController();
+ if (!s.ok())
+ return s;
+
+ s = fbdev_.Init(&dc_, fb_pool_);
+ if (!s.ok())
+ return s;
+
+ // Clear buffer to black - it is shown once screen is enabled.
+ void* buffer = fbdev_.GetFramebuffer();
+ if (!buffer) {
+ return Status::Internal();
+ }
+ std::memset(buffer, 0, fb_pool_.row_bytes * fb_pool_.size.height);
+ s = fbdev_.WriteFramebuffer(buffer);
+ if (!s.ok())
+ return s;
+
+ return fbdev_.Enable();
+}
+
+FramebufferRgb565 MCUXpressoDevice::GetFramebuffer() {
+ return FramebufferRgb565(
+ static_cast<color_rgb565_t*>(fbdev_.GetFramebuffer()),
+ fb_pool_.size.width,
+ fb_pool_.size.height,
+ fb_pool_.row_bytes);
+}
+
+Status MCUXpressoDevice::ReleaseFramebuffer(FramebufferRgb565 framebuffer) {
+ if (!framebuffer.IsValid())
+ return Status::InvalidArgument();
+ void* data = framebuffer.GetFramebufferData();
+ return fbdev_.WriteFramebuffer(data);
+}
+
+Status MCUXpressoDevice::PrepareDisplayController(void) {
+ Status status = InitDisplayInterface();
+ if (!status.ok())
+ return status;
+
+#if USE_DSI_SMARTDMA
+ InitSmartDMA();
+ status_t s = DSI_TransferCreateHandleSMARTDMA(
+ MIPI_DSI_HOST,
+ &dsi_smartdma_driver_handle_,
+ MCUXpressoDevice::DsiSmartDMAMemWriteCallback,
+ this);
+
+ return MCUXpressoToPigweedStatus(s);
+
+#else
+
+ NVIC_SetPriority(kMipiDsiIrqn, 6);
+
+ memset(&dsi_mem_write_ctx_, 0, sizeof(DSIMemWriteContext));
+
+ return MCUXpressoToPigweedStatus(
+ DSI_TransferCreateHandle(MIPI_DSI_HOST,
+ &dsi_driver_handle_,
+ MCUXpressoDevice::DsiMemWriteCallback,
+ this));
+#endif
+}
+
+// static
+Status MCUXpressoDevice::InitDisplayInterface() {
+ RESET_SetPeripheralReset(kMIPI_DSI_PHY_RST_SHIFT_RSTn);
+ InitMipiDsiClock();
+ RESET_ClearPeripheralReset(kMIPI_DSI_CTRL_RST_SHIFT_RSTn);
+ SetMipiDsiConfig();
+ RESET_ClearPeripheralReset(kMIPI_DSI_PHY_RST_SHIFT_RSTn);
+ return InitLcdPanel();
+}
+
+Status MCUXpressoDevice::InitLcdPanel() {
+ const gpio_pin_config_t pinConfig = {
+ .pinDirection = kGPIO_DigitalOutput,
+ .outputLogic = 0,
+ };
+
+ GPIO_PinInit(GPIO, BOARD_MIPI_POWER_PORT, BOARD_MIPI_POWER_PIN, &pinConfig);
+ GPIO_PinInit(GPIO, BOARD_MIPI_RST_PORT, BOARD_MIPI_RST_PIN, &pinConfig);
+
+ InitMipiPanelTEPin();
+
+ return OkStatus();
+}
+
+// static
+void MCUXpressoDevice::InitMipiDsiClock(void) {
+ POWER_DisablePD(kPDRUNCFG_APD_MIPIDSI_SRAM);
+ POWER_DisablePD(kPDRUNCFG_PPD_MIPIDSI_SRAM);
+ POWER_DisablePD(kPDRUNCFG_PD_MIPIDSI);
+ POWER_ApplyPD();
+
+ CLOCK_AttachClk(kFRO_DIV1_to_MIPI_DPHYESC_CLK);
+ CLOCK_SetClkDiv(kCLOCK_DivDphyEscRxClk, 4);
+ CLOCK_SetClkDiv(kCLOCK_DivDphyEscTxClk, 3);
+ mipi_dsi_tx_esc_clk_freq_hz_ = CLOCK_GetMipiDphyEscTxClkFreq();
+
+ CLOCK_AttachClk(kAUX1_PLL_to_MIPI_DPHY_CLK);
+#if (DEMO_RM67162_BUFFER_FORMAT == PIXEL_FORMAT_RGB565)
+ CLOCK_InitSysPfd(kCLOCK_Pfd3, 30);
+#else
+ CLOCK_InitSysPfd(kCLOCK_Pfd3, 19);
+#endif
+ CLOCK_SetClkDiv(kCLOCK_DivDphyClk, 1);
+ mipi_dsi_dphy_bit_clk_freq_hz_ = CLOCK_GetMipiDphyClkFreq();
+}
+
+// static
+void MCUXpressoDevice::InitMipiPanelTEPin(void) {
+ const gpio_pin_config_t tePinConfig = {
+ .pinDirection = kGPIO_DigitalInput,
+ .outputLogic = 0,
+ };
+
+ gpio_interrupt_config_t te_pin_int_config = {kGPIO_PinIntEnableEdge,
+ kGPIO_PinIntEnableHighOrRise};
+
+ GPIO_PinInit(GPIO, BOARD_MIPI_TE_PORT, BOARD_MIPI_TE_PIN, &tePinConfig);
+
+ GPIO_SetPinInterruptConfig(
+ GPIO, BOARD_MIPI_TE_PORT, BOARD_MIPI_TE_PIN, &te_pin_int_config);
+
+ GPIO_PinEnableInterrupt(GPIO, BOARD_MIPI_TE_PORT, BOARD_MIPI_TE_PIN, 0);
+
+ NVIC_SetPriority(GPIO_INTA_IRQn, 3);
+
+ NVIC_EnableIRQ(GPIO_INTA_IRQn);
+}
+
+// static
+void MCUXpressoDevice::SetMipiDsiConfig() {
+ dsi_config_t dsiConfig;
+ dsi_dphy_config_t dphyConfig;
+
+ DSI_GetDefaultConfig(&dsiConfig);
+ dsiConfig.numLanes = kMipiDsiLaneNum;
+ dsiConfig.autoInsertEoTp = true;
+
+ DSI_GetDphyDefaultConfig(&dphyConfig,
+ mipi_dsi_dphy_bit_clk_freq_hz_,
+ mipi_dsi_tx_esc_clk_freq_hz_);
+
+ DSI_Init(MIPI_DSI_HOST, &dsiConfig);
+
+ DSI_InitDphy(MIPI_DSI_HOST, &dphyConfig, 0);
+}
+
+#if USE_DSI_SMARTDMA
+// static
+void MCUXpressoDevice::InitSmartDMA() {
+ RESET_ClearPeripheralReset(kINPUTMUX_RST_SHIFT_RSTn);
+
+ INPUTMUX_Init(INPUTMUX);
+ INPUTMUX_AttachSignal(INPUTMUX, 0, kINPUTMUX_MipiIrqToSmartDmaInput);
+
+ INPUTMUX_Deinit(INPUTMUX);
+
+ POWER_DisablePD(kPDRUNCFG_APD_SMARTDMA_SRAM);
+ POWER_DisablePD(kPDRUNCFG_PPD_SMARTDMA_SRAM);
+ POWER_ApplyPD();
+
+ RESET_ClearPeripheralReset(kSMART_DMA_RST_SHIFT_RSTn);
+ CLOCK_EnableClock(kCLOCK_Smartdma);
+
+ SMARTDMA_InitWithoutFirmware();
+ NVIC_EnableIRQ(SDMA_IRQn);
+
+ NVIC_SetPriority(SDMA_IRQn, 3);
+}
+#endif
+
+// static
+status_t MCUXpressoDevice::DSI_Transfer(dsi_transfer_t* xfer) {
+ return DSI_TransferBlocking(MIPI_DSI_HOST, xfer);
+}
+
+// static
+status_t MCUXpressoDevice::DSI_MemWrite(uint8_t virtualChannel,
+ const uint8_t* data,
+ uint32_t length) {
+#if USE_DSI_SMARTDMA
+ dsi_smartdma_write_mem_transfer_t xfer = {
+#if (DEMO_RM67162_BUFFER_FORMAT == PIXEL_FORMAT_RGB565)
+ .inputFormat = kDSI_SMARTDMA_InputPixelFormatRGB565,
+ .outputFormat = kDSI_SMARTDMA_OutputPixelFormatRGB565,
+#elif (DEMO_RM67162_BUFFER_FORMAT == PIXEL_FORMAT_RGB888)
+ .inputFormat = kDSI_SMARTDMA_InputPixelFormatRGB888,
+ .outputFormat = kDSI_SMARTDMA_OutputPixelFormatRGB888,
+#else
+ .inputFormat = kDSI_SMARTDMA_InputPixelFormatXRGB8888,
+ .outputFormat = kDSI_SMARTDMA_OutputPixelFormatRGB888,
+#endif /* DEMO_RM67162_BUFFER_FORMAT */
+ .data = data,
+ .dataSize = length,
+
+ .virtualChannel = virtualChannel,
+ .disablePixelByteSwap = false,
+ };
+
+ return DSI_TransferWriteMemorySMARTDMA(
+ MIPI_DSI_HOST, &s_device->dsi_smartdma_driver_handle_, &xfer);
+
+#else /* USE_DSI_SMARTDMA */
+
+ status_t status;
+
+ if (s_device->dsi_mem_write_ctx_.ongoing) {
+ return kStatus_Fail;
+ }
+
+ s_device->dsi_mem_write_xfer_.virtualChannel = virtualChannel;
+ s_device->dsi_mem_write_xfer_.flags = kDSI_TransferUseHighSpeed;
+ s_device->dsi_mem_write_xfer_.sendDscCmd = true;
+
+ s_device->dsi_mem_write_ctx_.ongoing = true;
+ s_device->dsi_mem_write_ctx_.tx_data = data;
+ s_device->dsi_mem_write_ctx_.num_bytes_remaining = length;
+ s_device->dsi_mem_write_ctx_.dsc_cmd = kMIPI_DCS_WriteMemoryStart;
+
+ status = s_device->DsiMemWriteSendChunck();
+
+ if (status != kStatus_Success) {
+ /* Memory write does not start actually. */
+ s_device->dsi_mem_write_ctx_.ongoing = false;
+ }
+
+ return status;
+#endif
+}
+
+// static
+void MCUXpressoDevice::PullPanelResetPin(bool pullUp) {
+ if (pullUp) {
+ GPIO_PinWrite(GPIO, BOARD_MIPI_RST_PORT, BOARD_MIPI_RST_PIN, 1);
+ } else {
+ GPIO_PinWrite(GPIO, BOARD_MIPI_RST_PORT, BOARD_MIPI_RST_PIN, 0);
+ }
+}
+
+// static
+void MCUXpressoDevice::PullPanelPowerPin(bool pullUp) {
+ if (pullUp) {
+ GPIO_PinWrite(GPIO, BOARD_MIPI_POWER_PORT, BOARD_MIPI_POWER_PIN, 1);
+ } else {
+ GPIO_PinWrite(GPIO, BOARD_MIPI_POWER_PORT, BOARD_MIPI_POWER_PIN, 0);
+ }
+}
+
+// static
+void MCUXpressoDevice::DisplayTEPinHandler() {
+ DC_FB_DSI_CMD_TE_IRQHandler(&dc_);
+}
+
+// static
+status_t MCUXpressoDevice::DsiMemWriteSendChunck(void) {
+ uint32_t curSendLen;
+ uint32_t i;
+
+ curSendLen = kMaxDSITxArraySize > dsi_mem_write_ctx_.num_bytes_remaining
+ ? dsi_mem_write_ctx_.num_bytes_remaining
+ : kMaxDSITxArraySize;
+
+ dsi_mem_write_xfer_.txDataType = kDSI_TxDataDcsLongWr;
+ dsi_mem_write_xfer_.dscCmd = dsi_mem_write_ctx_.dsc_cmd;
+ dsi_mem_write_xfer_.txData = dsi_mem_write_tmp_array_;
+ dsi_mem_write_xfer_.txDataSize = curSendLen;
+
+#if (DEMO_RM67162_BUFFER_FORMAT == PIXEL_FORMAT_RGB565)
+ for (i = 0; i < curSendLen; i += 2) {
+ dsi_mem_write_tmp_array_[i] = *(dsi_mem_write_ctx_.tx_data + 1);
+ dsi_mem_write_tmp_array_[i + 1] = *(dsi_mem_write_ctx_.tx_data);
+
+ dsi_mem_write_ctx_.tx_data += 2;
+ }
+#else
+ for (i = 0; i < curSendLen; i += 3) {
+ dsi_mem_write_tmp_array_[i] = *(dsi_mem_write_ctx_.tx_data + 2);
+ dsi_mem_write_tmp_array_[i + 1] = *(dsi_mem_write_ctx_.tx_data + 1);
+ dsi_mem_write_tmp_array_[i + 2] = *(dsi_mem_write_ctx_.tx_data);
+
+ dsi_mem_write_ctx_.tx_data += 3;
+ }
+#endif
+
+ dsi_mem_write_ctx_.num_bytes_remaining -= curSendLen;
+ dsi_mem_write_ctx_.dsc_cmd = kMIPI_DCS_WriteMemoryContinue;
+
+ return DSI_TransferNonBlocking(
+ MIPI_DSI_HOST, &dsi_driver_handle_, &dsi_mem_write_xfer_);
+}
+
+// static
+void MCUXpressoDevice::DsiMemWriteCallback(MIPI_DSI_HOST_Type* base,
+ dsi_handle_t* handle,
+ status_t status,
+ void* userData) {
+ MCUXpressoDevice* device = static_cast<MCUXpressoDevice*>(userData);
+ if ((kStatus_Success == status) &&
+ (device->dsi_mem_write_ctx_.num_bytes_remaining > 0)) {
+ status = device->DsiMemWriteSendChunck();
+ if (kStatus_Success == status) {
+ return;
+ }
+ }
+
+ device->dsi_mem_write_ctx_.ongoing = false;
+ MIPI_DSI_MemoryDoneDriverCallback(status, &device->dsi_device_);
+}
+
+// static
+void MCUXpressoDevice::DsiSmartDMAMemWriteCallback(
+ MIPI_DSI_HOST_Type* base,
+ dsi_smartdma_handle_t* handle,
+ status_t status,
+ void* userData) {
+ MCUXpressoDevice* device = static_cast<MCUXpressoDevice*>(userData);
+ MIPI_DSI_MemoryDoneDriverCallback(status, &device->dsi_device_);
+}
+
+} // namespace pw::mipi::dsi
diff --git a/pw_mipi_dsi_mcuxpresso/docs.rst b/pw_mipi_dsi_mcuxpresso/docs.rst
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pw_mipi_dsi_mcuxpresso/docs.rst
diff --git a/pw_mipi_dsi_mcuxpresso/framebuffer_device.cc b/pw_mipi_dsi_mcuxpresso/framebuffer_device.cc
new file mode 100644
index 0000000..f4f12c5
--- /dev/null
+++ b/pw_mipi_dsi_mcuxpresso/framebuffer_device.cc
@@ -0,0 +1,128 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_mipi_dsi_mcuxpresso/framebuffer_device.h"
+
+#include "common.h"
+#include "pw_assert/assert.h"
+#include "pw_status/try.h"
+
+namespace pw::mipi::dsi {
+
+FramebufferDevice::FramebufferDevice(uint8_t layer)
+ : dc_(nullptr), layer_(layer), enabled_(false) {
+ VIDEO_MEMPOOL_InitEmpty(&video_mempool_);
+}
+
+Status FramebufferDevice::Init(
+ const dc_fb_t* dc, const pw::framebuffer::pool::PoolData& pool_data) {
+ PW_TRY(InitDisplayController(dc));
+ return InitVideoMemPool(pool_data);
+}
+
+Status FramebufferDevice::InitDisplayController(const dc_fb_t* dc) {
+ PW_ASSERT(!dc_);
+ dc_ = dc;
+
+ status_t status = dc_->ops->init(dc);
+ if (status != kStatus_Success) {
+ return MCUXpressoToPigweedStatus(status);
+ }
+
+ dc_fb_info_t buff_info;
+
+ status = dc_->ops->getLayerDefaultConfig(dc_, layer_, &buff_info);
+ if (status != kStatus_Success) {
+ return MCUXpressoToPigweedStatus(status);
+ }
+
+ dc_->ops->setCallback(dc_, layer_, BufferSwitchOffCallback, this);
+
+ status = dc_->ops->setLayerConfig(dc_, layer_, &buff_info);
+ if (status != kStatus_Success) {
+ return MCUXpressoToPigweedStatus(status);
+ }
+
+ return OkStatus();
+}
+
+Status FramebufferDevice::InitVideoMemPool(
+ const pw::framebuffer::pool::PoolData& pool_data) {
+ if (enabled_) {
+ return Status::FailedPrecondition();
+ }
+
+ for (uint8_t i = 0; i < pool_data.num_fb; i++) {
+ VIDEO_MEMPOOL_Put(&video_mempool_, pool_data.fb_addr[i]);
+ }
+
+ return OkStatus();
+}
+
+Status FramebufferDevice::Close() {
+ if (!dc_)
+ return Status::FailedPrecondition();
+
+ status_t status = dc_->ops->deinit(dc_);
+ dc_ = nullptr;
+ return MCUXpressoToPigweedStatus(status);
+}
+
+Status FramebufferDevice::Enable() {
+ if (enabled_)
+ return OkStatus();
+
+ status_t status = kStatus_Success;
+
+ if ((dc_->ops->getProperty(dc_) & (uint32_t)kDC_FB_ReserveFrameBuffer) ==
+ 0U) {
+ status = dc_->ops->enableLayer(dc_, layer_);
+ if (status != kStatus_Success) {
+ enabled_ = true;
+ }
+ }
+
+ return MCUXpressoToPigweedStatus(status);
+}
+
+Status FramebufferDevice::Disable() {
+ if (!enabled_)
+ return OkStatus();
+
+ status_t status = dc_->ops->disableLayer(dc_, layer_);
+ enabled_ = false;
+
+ return MCUXpressoToPigweedStatus(status);
+}
+
+Status FramebufferDevice::WriteFramebuffer(void* frameBuffer) {
+ return MCUXpressoToPigweedStatus(
+ dc_->ops->setFrameBuffer(dc_, layer_, frameBuffer));
+}
+
+void* FramebufferDevice::GetFramebuffer() {
+ return VIDEO_MEMPOOL_Get(&video_mempool_);
+}
+
+void FramebufferDevice::BufferSwitchOff(void* buffer) {
+ VIDEO_MEMPOOL_Put(&video_mempool_, buffer);
+}
+
+// static
+void FramebufferDevice::BufferSwitchOffCallback(void* param, void* buffer) {
+ PW_ASSERT(param != nullptr);
+ static_cast<FramebufferDevice*>(param)->BufferSwitchOff(buffer);
+}
+
+} // namespace pw::mipi::dsi
diff --git a/pw_mipi_dsi_mcuxpresso/public/pw_mipi_dsi_mcuxpresso/device.h b/pw_mipi_dsi_mcuxpresso/public/pw_mipi_dsi_mcuxpresso/device.h
new file mode 100644
index 0000000..9728b15
--- /dev/null
+++ b/pw_mipi_dsi_mcuxpresso/public/pw_mipi_dsi_mcuxpresso/device.h
@@ -0,0 +1,105 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#pragma once
+
+#include "fsl_dc_fb_dsi_cmd.h"
+#include "fsl_mipi_dsi.h"
+#include "fsl_mipi_dsi_smartdma.h"
+#include "fsl_rm67162.h"
+#include "pw_coordinates/vec_int.h"
+#include "pw_framebuffer_pool/framebuffer_pool.h"
+#include "pw_mipi_dsi/device.h"
+#include "pw_mipi_dsi_mcuxpresso/framebuffer_device.h"
+#include "pw_status/status.h"
+
+namespace pw::mipi::dsi {
+
+constexpr size_t kBytesPerPixel = sizeof(pw::color::color_rgb565_t);
+constexpr uint32_t kMaxDSITxArraySize =
+ (((FSL_DSI_TX_MAX_PAYLOAD_BYTE)-1U) / kBytesPerPixel) * kBytesPerPixel;
+
+#if !defined(USE_DSI_SMARTDMA)
+#error "USE_DSI_SMARTDMA not defined"
+#endif
+
+// MIPI DSI Device implementation for the MCUXpresso platform.
+class MCUXpressoDevice : public Device {
+ public:
+ MCUXpressoDevice(const pw::framebuffer::pool::PoolData& fb_pool,
+ const pw::coordinates::Size<uint16_t>& panel_size,
+ video_pixel_format_t pixel_format);
+ virtual ~MCUXpressoDevice();
+
+ Status Init();
+
+ // pw::mipi::dsi::Device implementation:
+ pw::framebuffer::FramebufferRgb565 GetFramebuffer() override;
+ Status ReleaseFramebuffer(
+ pw::framebuffer::FramebufferRgb565 framebuffer) override;
+
+ public:
+ static status_t DSI_Transfer(dsi_transfer_t* xfer);
+ static status_t DSI_MemWrite(uint8_t virtualChannel,
+ const uint8_t* data,
+ uint32_t length);
+ static void PullPanelResetPin(bool pullUp);
+ static void PullPanelPowerPin(bool pullUp);
+ void DisplayTEPinHandler();
+ Status PrepareDisplayController();
+
+ private:
+ struct DSIMemWriteContext {
+ volatile bool ongoing;
+ const uint8_t* tx_data;
+ uint32_t num_bytes_remaining;
+ uint8_t dsc_cmd;
+ };
+
+ static void DsiSmartDMAMemWriteCallback(MIPI_DSI_HOST_Type* base,
+ dsi_smartdma_handle_t* handle,
+ status_t status,
+ void* userData);
+ static void DsiMemWriteCallback(MIPI_DSI_HOST_Type* base,
+ dsi_handle_t* handle,
+ status_t status,
+ void* userData);
+ status_t DsiMemWriteSendChunck();
+ Status InitDisplayInterface();
+ Status InitLcdPanel();
+ void InitMipiPanelTEPin();
+ void InitMipiDsiClock();
+ void SetMipiDsiConfig();
+#if USE_DSI_SMARTDMA
+ void InitSmartDMA();
+#endif
+
+ const pw::framebuffer::pool::PoolData& fb_pool_;
+ FramebufferDevice fbdev_;
+ dsi_smartdma_handle_t dsi_smartdma_driver_handle_ = {};
+ DSIMemWriteContext dsi_mem_write_ctx_ = {};
+ dsi_transfer_t dsi_mem_write_xfer_ = {};
+ dsi_handle_t dsi_driver_handle_ = {};
+ uint8_t dsi_mem_write_tmp_array_[kMaxDSITxArraySize];
+ uint32_t mipi_dsi_tx_esc_clk_freq_hz_ = 0;
+ uint32_t mipi_dsi_dphy_bit_clk_freq_hz_ = 0;
+ mipi_dsi_device_t dsi_device_;
+ rm67162_resource_t rm67162_resource_;
+ display_handle_t display_handle_;
+ dc_fb_dsi_cmd_handle_t dc_fb_dsi_cmd_handle_;
+ const dc_fb_dsi_cmd_config_t panel_config_;
+ dc_fb_t dc_;
+};
+
+} // namespace pw::mipi::dsi
diff --git a/pw_mipi_dsi_mcuxpresso/public/pw_mipi_dsi_mcuxpresso/framebuffer_device.h b/pw_mipi_dsi_mcuxpresso/public/pw_mipi_dsi_mcuxpresso/framebuffer_device.h
new file mode 100644
index 0000000..38fa366
--- /dev/null
+++ b/pw_mipi_dsi_mcuxpresso/public/pw_mipi_dsi_mcuxpresso/framebuffer_device.h
@@ -0,0 +1,70 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#pragma once
+
+#include <cstdint>
+
+#include "fsl_dc_fb.h"
+#include "fsl_video_common.h"
+#include "pw_framebuffer_pool/framebuffer_pool.h"
+#include "pw_status/status.h"
+
+namespace pw::mipi::dsi {
+
+constexpr uint16_t kMaxBufferCount = 3;
+
+// FramebufferDevice manages a pool of framebuffers and is responsible for
+// writing them to the display using NXPs display controller provided by the
+// driver.dc-fb-common.MIMXRT595S SDK component. The framebuffer pool is
+// managed by the driver.video-common.MIMXRT595S SDK component.
+class FramebufferDevice {
+ public:
+ // Create a default uninitialized instance. Init must be called to fully
+ // initialize an instance before it can be used.
+ FramebufferDevice(uint8_t layer);
+
+ pw::Status Init(const dc_fb_t* dc,
+ const pw::framebuffer::pool::PoolData& pool_data);
+
+ // Close the device.
+ pw::Status Close();
+
+ // Enable the device.
+ pw::Status Enable();
+
+ // Disable the device.
+ pw::Status Disable();
+
+ // Send the framebuffer data to the device.
+ pw::Status WriteFramebuffer(void* buffer);
+
+ // Retrieve an unused framebuffer. If all framebuffers are still in the
+ // process of being transported to the device nullptr will be returned.
+ void* GetFramebuffer();
+
+ private:
+ static void BufferSwitchOffCallback(void* param, void* buffer);
+
+ void BufferSwitchOff(void* buffer);
+ pw::Status InitDisplayController(const dc_fb_t* dc);
+ Status InitVideoMemPool(const pw::framebuffer::pool::PoolData& pool_data);
+
+ video_mempool_t video_mempool_;
+ const dc_fb_t* dc_; // NXP Display controller.
+ const uint8_t layer_; // The video layer to write to.
+ bool enabled_; // Has this instance been initialized.
+};
+
+} // namespace pw::mipi::dsi
diff --git a/targets/mimxrt595_evk/target_toolchains.gni b/targets/mimxrt595_evk/target_toolchains.gni
index d67e5d9..920e17a 100644
--- a/targets/mimxrt595_evk/target_toolchains.gni
+++ b/targets/mimxrt595_evk/target_toolchains.gni
@@ -39,6 +39,13 @@
forward_variables_from(toolchain_overrides, "*")
app_common_BACKEND = "//applications/app_common_impl:mimxrt595"
+ pw_mipi_dsi_use_smart_dma = 1
+ pw_lcd_width = 400
+ pw_lcd_height = 392
+ pw_nxp_buffer_width = 392
+ pw_nxp_buffer_start_x = 4
+ pw_nxp_buffer_start_y = 0
+
pw_board_led_BACKEND = "$dir_pw_board_led_mimxrt595_evk"
pw_spin_delay_BACKEND = "$dir_pw_spin_delay_mcuxpresso"
pw_touchscreen_BACKEND = "$dir_pw_touchscreen_null"