xref: /aosp_15_r20/external/pigweed/seed/0104.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _seed-0104:
2
3=====================
40104: Display Support
5=====================
6.. seed::
7   :number: 104
8   :name: Display Support
9   :status: Accepted
10   :proposal_date: 2023-06-12
11   :cl: 150793
12   :authors: Chris Mumford
13   :facilitator: Anthony DiGirolamo
14
15-------
16Summary
17-------
18Add support for graphics displays. This includes display drivers for a few
19popular display controllers, framebuffer management, and a framework to simplify
20adding a graphics display to a Pigweed application.
21
22----------
23Motivation
24----------
25Pigweed currently has no specific support for a display device. Projects that
26require a display currently must do the full implementation, including the
27display driver in most instances, to add display support.
28
29This proposes the addition of a basic framework for display devices, as well
30as implementations for a few common Pigweed test devices - specifically the
31STM32F429I. This enables developers to quickly and easily add display support
32for supported devices and an implementation to model when adding new device
33support.
34
35--------
36Proposal
37--------
38This proposes no changes to existing modules, but suggests several new libraries
39all within a single new module titled ``pw_display`` that together define a
40framework for rendering to displays.
41
42
43New Libraries
44=============
45.. list-table::
46   :widths: 5 45
47   :header-rows: 1
48
49   * - Library
50     - Function
51
52   * - pw_display/display
53     - Manage draw thread, framebuffers, and driver
54
55   * - pw_display/driver
56     - Display driver interface definition
57
58   * - pw_display/pixel_pusher
59     - Transport of pixel data to display controller
60
61   * - pw_display/drivers/ili9341
62     - Display driver for the ILI9341 display controller
63
64   * - pw_display/drivers/imgui
65     - Host display driver using `Dear ImGui <https://www.dearimgui.com/>`_
66
67   * - pw_display/drivers/mipi
68     - Display driver for `MIPI DSI <https://www.mipi.org/specifications/dsi>`_ controllers
69
70   * - pw_display/drivers/null
71     - Null display driver for headless devices
72
73   * - pw_display/drivers/st7735
74     - Display driver for the `ST7735 <https://www.displayfuture.com/Display/datasheet/controller/ST7735.pdf>`_ display controller
75
76   * - pw_display/drivers/st7789
77     - Display driver for the ST7789 display controller
78
79   * - pw_display/draw
80     - Very basic drawing library for test and bring-up purposes
81
82   * - pw_display/framebuffer
83     - Manage access to pixel buffer.
84
85   * - pw_display/framebuffer_mcuxpresso
86     - Specialization of the framebuffer for the MCUxpresso devices
87
88   * - pw_display/geometry
89     - Basic shared math types such as 2D vectors, etc.
90
91
92Geometry
93========
94``pw_display/geometry`` contains two helper structures for common values usually
95used as a pair.
96
97.. code-block:: cpp
98
99   namespace pw::display {
100
101   template <typename T>
102   struct Size {
103     T width;
104     T height;
105   };
106
107   template <typename T>
108   struct Vector2 {
109     T x;
110     T y;
111   };
112
113   }  // namespace pw::display
114
115
116Framebuffer
117===========
118A framebuffer is a small class that provides access to a pixel buffer. It
119keeps a copy of the pixel buffer metadata and provides accessor methods for
120those values.
121
122.. code-block:: cpp
123
124   namespace pw::display {
125
126   enum class PixelFormat {
127     None,
128     RGB565,
129   };
130
131   class Framebuffer {
132   public:
133     // Construct a default invalid framebuffer.
134     Framebuffer();
135
136     Framebuffer(void* data,
137                 PixelFormat pixel_format,
138                 pw::math::Size<uint16_t> size,
139                 uint16_t row_bytes);
140
141     Framebuffer(const Framebuffer&) = delete;
142     Framebuffer(Framebuffer&& other);
143
144     Framebuffer& operator=(const Framebuffer&) = delete;
145     Framebuffer& operator=(Framebuffer&&);
146
147     bool is_valid() const;
148
149     pw::ConstByteSpan data() const;
150     pw::ByteSpan data();
151
152     PixelFormat pixel_format() const;
153
154     pw::math::Size<uint16_t> size();
155
156     uint16_t row_bytes() const;
157   };
158
159   }  // namespace pw::display
160
161FrameBuffer is a moveable class that is intended to signify read/write
162privileges to the underlying pixel data. This makes it easier to track when the
163pixel data may be read from, or written to, without conflict.
164
165The framebuffer does not own the underlying pixel buffer. In other words
166the deletion of a framebuffer will not free the underlying pixel data.
167
168Framebuffers do not have methods for reading or writing to the underlying pixel
169buffer. This is the responsibility of the the selected graphics library which
170can be given the pixel buffer pointer retrieved by calling ``data()``.
171
172.. code-block:: cpp
173
174   constexpr size_t kWidth = 64;
175   constexpr size_t kHeight = 32;
176   uint16_t pixel_data[kWidth * kHeight];
177
178   void DrawScreen(Framebuffer* fb) {
179     // Clear framebuffer to black.
180     std::memset(fb->data(), 0, fb->height() * fb->row_bytes());
181
182     // Set first pixel to white.
183     uint16_t* pixel_data = static_cast<uint16_t*>(fb->data());
184     pixel_data[0] = 0xffff;
185   }
186
187   Framebuffer fb(pixel_data, {kWidth, kHeight},
188                  PixelFormat::RGB565,
189                  kWidth * sizeof(uint16_t));
190   DrawScreen(&fb);
191
192FramebufferPool
193===============
194The FramebufferPool is intended to simplify the use of multiple framebuffers
195when multi-buffered rendering is being used. It is a collection of framebuffers
196which can be retrieved, used, and then returned to the pool for reuse. All
197framebuffers in the pool share identical attributes. A framebuffer that is
198returned to a caller of ``GetFramebuffer()`` can be thought of as "on loan" to
199that caller and will not be given to any other caller of ``GetFramebuffer()``
200until it has been returned by calling ``ReleaseFramebuffer()``.
201
202.. code-block:: cpp
203
204   namespace pw::display {
205
206   class FramebufferPool {
207   public:
208     using BufferArray = std::array<void*, FRAMEBUFFER_COUNT>;
209
210     // Constructor parameters.
211     struct Config {
212       BufferArray fb_addr;  // Address of each buffer in this pool.
213       pw::math::Size<uint16_t> dimensions;  // width/height of each buffer.
214       uint16_t row_bytes;                   // row bytes of each buffer.
215       pw::framebuffer::PixelFormat pixel_format;
216     };
217
218     FramebufferPool(const Config& config);
219     virtual ~FramebufferPool();
220
221     uint16_t row_bytes() const;
222
223     pw::math::Size<uint16_t> dimensions() const;
224
225     pw::framebuffer::PixelFormat pixel_format() const;
226
227     // Return a framebuffer to the caller for use. This call WILL BLOCK until a
228     // framebuffer is returned for use. Framebuffers *must* be returned to this
229     // pool by a corresponding call to ReleaseFramebuffer. This function will only
230     // return a valid framebuffer.
231     //
232     // This call is thread-safe, but not interrupt safe.
233     virtual pw::framebuffer::Framebuffer GetFramebuffer();
234
235     // Return the framebuffer to the pool available for use by the next call to
236     // GetFramebuffer.
237     //
238     // This may be called on another thread or during an interrupt.
239     virtual Status ReleaseFramebuffer(pw::framebuffer::Framebuffer framebuffer);
240   };
241
242   }  // namespace pw::display
243
244An example use of the framebuffer pool is:
245
246.. code-block:: cpp
247
248   // Retrieve a framebuffer for drawing. May block if pool has no framebuffers
249   // to issue.
250   FrameBuffer fb = framebuffer_pool.GetFramebuffer();
251
252   // Draw to the framebuffer.
253   UpdateDisplay(&fb);
254
255   // Return the framebuffer to the pool for reuse.
256   framebuffer_pool.ReleaseFramebuffer(std::move(fb));
257
258DisplayDriver
259=============
260A DisplayDriver is usually the sole class responsible for communicating with the
261display controller. Its primary responsibilities are the display controller
262initialization, and the writing of pixel data when a display update is needed.
263
264This proposal supports multiple heterogenous display controllers. This could be:
265
2661. A single display of any given type (e.g. ILI9341).
2672. Two ILI9341 displays.
2683. Two ILI9341 displays and a second one of a different type.
269
270Because of this approach the DisplayDriver is defined as an interface:
271
272.. code-block:: cpp
273
274   namespace pw::display {
275
276   class DisplayDriver {
277   public:
278     // Called on the completion of a write operation.
279     using WriteCallback = Callback<void(framebuffer::Framebuffer, Status)>;
280
281     virtual ~DisplayDriver() = default;
282
283     virtual Status Init() = 0;
284
285     virtual void WriteFramebuffer(pw::framebuffer::Framebuffer framebuffer,
286                                   WriteCallback write_callback) = 0;
287
288     virtual pw::math::Size<uint16_t> size() const = 0;
289   };
290
291   }  // namespace pw::display
292
293Each driver then provides a concrete implementation of the driver. Below is the
294definition of the display driver for the ILI9341:
295
296.. code-block:: cpp
297
298   namespace pw::display {
299
300   class DisplayDriverILI9341 : public DisplayDriver {
301   public:
302     struct Config {
303       // Device specific initialization parameters.
304     };
305
306     DisplayDriverILI9341(const Config& config);
307
308     // DisplayDriver implementation:
309     Status Init() override;
310     void WriteFramebuffer(pw::framebuffer::Framebuffer framebuffer,
311                           WriteCallback write_callback) override;
312     Status WriteRow(span<uint16_t> row_pixels,
313                     uint16_t row_idx,
314                     uint16_t col_idx) override;
315     pw::math::Size<uint16_t> size() const override;
316
317   private:
318     enum class Mode {
319       kData,
320       kCommand,
321     };
322
323     // A command and optional data to write to the ILI9341.
324     struct Command {
325       uint8_t command;
326       ConstByteSpan command_data;
327     };
328
329     // Toggle the reset GPIO line to reset the display controller.
330     Status Reset();
331
332     // Set the command/data mode of the display controller.
333     void SetMode(Mode mode);
334     // Write the command to the display controller.
335     Status WriteCommand(pw::spi::Device::Transaction& transaction,
336                         const Command& command);
337   };
338
339   }  // namespace pw::display
340
341Here is an example retrieving a framebuffer from the framebuffer pool, drawing
342into the framebuffer, using the display driver to write the pixel data, and then
343returning the framebuffer back to the pool for use.
344
345.. code-block:: cpp
346
347   FrameBuffer fb = framebuffer_pool.GetFramebuffer();
348
349   // DrawScreen is a function that will draw to the framebuffer's underlying
350   // pixel buffer using a drawing library. See example above.
351   DrawScreen(&fb);
352
353   display_driver_.WriteFramebuffer(
354       std::move(framebuffer),
355       [&framebuffer_pool](pw::framebuffer::Framebuffer fb, Status status) {
356         // Return the framebuffer back to the pool for reuse once the display
357         // write is complete.
358         framebuffer_pool.ReleaseFramebuffer(std::move(fb));
359       });
360
361In the example above that the framebuffer (``fb``) is moved when calling
362``WriteFramebuffer()`` passing ownership to the display driver. From this point
363forward the application code may not access the framebuffer in any way. When the
364framebuffer write is complete the framebuffer is then moved to the callback
365which in turn moves it when calling ``ReleaseFramebuffer()``.
366
367``WriteFramebuffer()`` always does a write of the full framebuffer - sending all
368pixel data.
369
370``WriteFramebuffer()`` may be a blocking call, but on some platforms the driver
371may use a background write and the write callback is called when the write
372is complete. The write callback **may be called during an interrupt**.
373
374PixelPusher
375===========
376Pixel data for Simple SPI based display controllers can be written to the
377display controller using ``pw_spi``. There are some controllers which use
378other interfaces (RGB, MIPI, etc.). Also, some vendors provide an API for
379interacting with these display controllers for writing pixel data.
380
381To allow the drivers to be hardware/vendor independent the ``PixelPusher``
382may be used. This defines an interface whose sole responsibility is to write
383a framebuffer to the display controller. Specializations of this will use
384``pw_spi`` or vendor proprietary calls to write pixel data.
385
386.. code-block:: cpp
387
388   namespace pw::display {
389
390   class PixelPusher {
391    public:
392     using WriteCallback = Callback<void(framebuffer::Framebuffer, Status)>;
393
394     virtual ~PixelPusher() = default;
395
396     virtual Status Init(
397         const pw::framebuffer_pool::FramebufferPool& framebuffer_pool) = 0;
398
399     virtual void WriteFramebuffer(framebuffer::Framebuffer framebuffer,
400                                   WriteCallback complete_callback) = 0;
401   };
402
403   }  // namespace pw::display
404
405Display
406=======
407Each display has:
408
4091. One and only one display driver.
4102. A reference to a single framebuffer pool. This framebuffer pool may be shared
411   with other displays.
4123. A drawing thread, if so configured, for asynchronous display updates.
413
414.. code-block:: cpp
415
416   namespace pw::display {
417
418   class Display {
419   public:
420     // Called on the completion of an update.
421     using WriteCallback = Callback<void(Status)>;
422
423     Display(pw::display_driver::DisplayDriver& display_driver,
424             pw::math::Size<uint16_t> size,
425             pw::framebuffer_pool::FramebufferPool& framebuffer_pool);
426     virtual ~Display();
427
428     pw::framebuffer::Framebuffer GetFramebuffer();
429
430     void ReleaseFramebuffer(pw::framebuffer::Framebuffer framebuffer,
431                             WriteCallback callback);
432
433     pw::math::Size<uint16_t> size() const;
434   };
435
436   }  // namespace pw::display
437
438Once applications are initialized they typically will not directly interact with
439display drivers or framebuffer pools. These will be utilized by the display
440which will provide a simpler interface.
441
442``Display::GetFramebuffer()`` must always be called on the same thread and is not
443interrupt safe. It will block if there is no available framebuffer in the
444framebuffer pool waiting for a framebuffer to be returned.
445
446``Display::ReleaseFramebuffer()`` must be called for each framebuffer returned by
447``Display::GetFramebuffer()``. This will initiate the display update using the
448displays associated driver. The ``callback`` will be called when this update is
449complete.
450
451A simplified application rendering loop would resemble:
452
453.. code-block:: cpp
454
455   // Get a framebuffer for drawing.
456   FrameBuffer fb = display.GetFramebuffer();
457
458   // DrawScreen is a function that will draw to |fb|'s pixel buffer using a
459   // drawing library. See example above.
460   DrawScreen(&fb);
461
462   // Return the framebuffer to the display which will be written to the display
463   // controller by the display's display driver.
464   display.ReleaseFramebuffer(std::move(fb), [](Status){});
465
466Drawing Library
467===============
468``pw_display/draw`` was created for testing and verification purposes only. It is
469not intended to be feature rich or performant in any way. This is small
470collection of basic drawing primitives not intended to be used by shipping
471applications.
472
473.. code-block:: cpp
474
475   namespace pw::display {
476
477   void DrawLine(pw::framebuffer::Framebuffer& fb,
478                 int x1,
479                 int y1,
480                 int x2,
481                 int y2,
482                 pw::color::color_rgb565_t pen_color);
483
484   // Draw a circle at center_x, center_y with given radius and color. Only a
485   // one-pixel outline is drawn if filled is false.
486   void DrawCircle(pw::framebuffer::Framebuffer& fb,
487                   int center_x,
488                   int center_y,
489                   int radius,
490                   pw::color::color_rgb565_t pen_color,
491                   bool filled);
492
493   void DrawHLine(pw::framebuffer::Framebuffer& fb,
494                  int x1,
495                  int x2,
496                  int y,
497                  pw::color::color_rgb565_t pen_color);
498
499   void DrawRect(pw::framebuffer::Framebuffer& fb,
500                 int x1,
501                 int y1,
502                 int x2,
503                 int y2,
504                 pw::color::color_rgb565_t pen_color,
505                 bool filled);
506
507   void DrawRectWH(pw::framebuffer::Framebuffer& fb,
508                   int x,
509                   int y,
510                   int w,
511                   int h,
512                   pw::color::color_rgb565_t pen_color,
513                   bool filled);
514
515   void Fill(pw::framebuffer::Framebuffer& fb,
516             pw::color::color_rgb565_t pen_color);
517
518   void DrawSprite(pw::framebuffer::Framebuffer& fb,
519                   int x,
520                   int y,
521                   pw::draw::SpriteSheet* sprite_sheet,
522                   int integer_scale);
523
524   void DrawTestPattern();
525
526   pw::math::Size<int> DrawCharacter(int ch,
527                                     pw::math::Vector2<int> pos,
528                                     pw::color::color_rgb565_t fg_color,
529                                     pw::color::color_rgb565_t bg_color,
530                                     const FontSet& font,
531                                     pw::framebuffer::Framebuffer& framebuffer);
532
533   pw::math::Size<int> DrawString(std::wstring_view str,
534                                  pw::math::Vector2<int> pos,
535                                  pw::color::color_rgb565_t fg_color,
536                                  pw::color::color_rgb565_t bg_color,
537                                  const FontSet& font,
538                                  pw::framebuffer::Framebuffer& framebuffer);
539
540   }  // namespace pw::display
541
542Class Interaction Diagram
543=========================
544.. mermaid::
545   :alt: Framebuffer Classes
546   :align: center
547
548   classDiagram
549       class FramebufferPool {
550           uint16_t row_bytes()
551           PixelFormat pixel_format()
552           dimensions() : Size~uint16_t~
553           row_bytes() : uint16_t
554           GetFramebuffer() : Framebuffer
555
556           BufferArray buffer_addresses_
557           Size~uint16_t~ buffer_dimensions_
558           uint16_t row_bytes_
559           PixelFormat pixel_format_
560       }
561
562       class Framebuffer {
563           is_valid() : bool const
564           data() : void* const
565           pixel_format() : PixelFormat const
566           size() : Size~uint16_t~ const
567           row_bytes() uint16_t const
568
569           void* pixel_data_
570           Size~uint16_t~ size_
571           PixelFormat pixel_format_
572           uint16_t row_bytes_
573       }
574
575       class DisplayDriver {
576           <<DisplayDriver>>
577           Init() : Status
578           WriteFramebuffer(Framebuffer fb, WriteCallback cb): void
579           dimensions() : Size~uint16_t~
580
581           PixelPusher& pixel_pusher_
582       }
583
584       class Display {
585           DisplayDriver& display_driver_
586           const Size~uint16_t~ size_
587           FramebufferPool& framebuffer_pool_
588
589           GetFramebuffer() : Framebuffer
590       }
591
592       class PixelPusher {
593           Init() : Status
594           WriteFramebuffer(Framebuffer fb, WriteCallback cb) : void
595       }
596
597       <<interface>> DisplayDriver
598       FramebufferPool --> "FRAMEBUFFER_COUNT" Framebuffer : buffer_addresses_
599
600       Display --> "1" DisplayDriver : display_driver_
601       Display --> "1" FramebufferPool : framebuffer_pool_
602       DisplayDriver --> "1" PixelPusher : pixel_pusher_
603
604---------------------
605Problem investigation
606---------------------
607With no direct display support in Pigweed and no example programs implementing
608a solution Pigweed developers are essentially on their own. Depending on their
609hardware this means starting with a GitHub project with a sample application
610from MCUXpresso or STMCube. Each of these use a specific HAL and may be
611coupled to other frameworks, such as FreeRTOS. This places the burden of
612substituting the HAL calls with the Pigweed API, making the sample program
613with the application screen choice, etc.
614
615This chore is time consuming and often requires that the application developer
616acquire some level of driver expertise. Having direct display support in
617Pigweed will allow the developer to more quickly add display support.
618
619The primary use-case being targeted is an application with a single display
620using multiple framebuffers with display update notifications delivered during
621an interrupt. The initial implementation is designed to support multiple
622heterogenous displays, but that will not be the focus of development or testing
623for the first release.
624
625Touch sensors, or other input devices, are not part of this effort. Display
626and touch input often accompany each other, but to simplify this already large
627display effort, touch will be added in a separate activity.
628
629There are many other embedded libraries for embedded drawing. Popular  libraries
630are LVGL, emWin, GUIslice, HAGL, µGFX, and VGLite (to just name a few). These
631existing solutions generally offer one or more of: display drivers, drawing
632library, widget library. The display drivers usually rely on an abstraction
633layer, which they often refer to as a HAL, to interface with the underlying
634hardware API. This HAL may rely on macros, or sometimes a structure with
635function pointers for specific operations.
636
637The approach in this SEED was selected because it offers a low level API focused
638on display update performance. It offers no drawing or GUI library, but should
639be easily interfaced with those libraries.
640
641---------------
642Detailed design
643---------------
644This proposal suggests no changes to existing APIs. All changes introduce new
645modules that leverage the existing API. It supports static allocation of the
646pixel buffers and all display framework objects. Additionally pixel buffers
647may be hard-coded addresses or dynamically allocated from SRAM.
648
649The ``Framebuffer`` class is intended to simplify code that interacts with the
650pixel buffer. It includes the pixel buffer format, dimensions, and the buffer
651address. The framebuffer is 16 bytes in size (14 when packed). Framebuffer
652objects are created when requested and moved as a means of signifying ownership.
653In other words, whenever code has an actual framebuffer object it is allowed
654to both write to and read from the pixel buffer.
655
656The ``FramebufferPool`` is an object intended to simplify the management of a
657collection of framebuffers. It tracks those that are available for use and
658loans out framebuffers when requested. For single display devices this is
659generally not a difficult task as the application would maintain an array of
660framebuffers and a next available index. In this case framebuffers are always
661used in order and the buffer collection is implemented as a queue.
662
663Because RAM is often limited, the framebuffer pool is designed to be shared
664between multiple displays. Because display rendering and update may be at
665different speeds framebuffers do not need to be retrieved
666(via ``GetFramebuffer()``) and returned (via ``ReleaseFramebuffer()``) in the same
667order.
668
669Whenever possible asynchronous display updates will be used. Depending on the
670implementation this usually offloads the CPU from the pixel writing to the
671display controller. In this case the CPU will initiate the update and using
672some type of notification, usually an interrupt raised by a GPIO pin connected
673to the display, will be notified of the completion of the display update.
674Because of this the framebuffer pool ``ReleaseFramebuffer()`` call is interrupt
675safe.
676
677``FramebufferPool::GetFramebuffer()`` will block indefinitely if no framebuffer
678is available. This unburdens the application drawing loop from the task of
679managing framebuffers or tracking screen update completion.
680
681Testing
682=======
683All classes will be accompanied by a robust set of unit tests. These can be
684run on the host or the device. Test applications will be able to run on a
685workstation (i.e. not an MCU) in order to enable tests that depend on
686hardware available in most CPUs - like an MMU. This will enable the use of
687`AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_
688based tests. Desktop tests will use
689`Xvfb <https://www.x.org/releases/X11R7.6/doc/man/man1/Xvfb.1.xhtml>`_ to allow
690them to be run in a headless continuous integration environment.
691
692Performance
693===========
694Display support will include performance tests. Although this proposal does not
695include a rendering library, it will include support for specific platforms
696that will utilize means of transferring pixel data to the display controller
697in the background.
698
699------------
700Alternatives
701------------
702One alternative is to create the necessary port/HAL, the terminology varies by
703library, for the popular embedded graphics libraries. This would make it easier
704for Pigweed applications to add display support - bot only for those supported
705libraries. This effort is intended to be more focused on performance, which is
706not always the focus of other libraries.
707
708Another alternative is to do nothing - leaving the job of adding display
709support to the developers. As a significant percentage of embedded projects
710contain a display, it will beneficial to have built-in display support in
711Pigweed. This will allow all user to benefit by the shared display expertise,
712continuous integration, testing, and performance testing.
713
714--------------
715Open questions
716--------------
717
718Parameter Configuration
719=======================
720One open question is what parameters to specify in initialization parameters
721to a driver ``Init()`` function, which to set in build flags via ``config(...)``
722in GN, and which to hard-code into the driver. The most ideal, from the
723perspective of reducing binary size, is to hard-code all values in a single
724block of contiguous data. The decision to support multiple displays requires
725that the display initialization parameters, at least some of them, be defined
726at runtime and cannot be hard-coded into the driver code - that is, if the
727goal is to allow two of the same display to be in use with different settings.
728
729Additionally many drivers support dozens of configuration values. The ILI9341
730has 82 different commands, some with complex values like gamma tables or
731multiple values packed into a single register.
732
733The current approach is to strike a balance where the most commonly set
734values, for example display width/height and pixel format, are configurable
735via build flags, and the remainder is hard-coded in the driver. If a developer
736wants to set a parameter that is currently hard-coded in the driver, for
737example display refresh rate or gamma table, they would need to copy the display
738driver from Pigweed, or create a Pigweed branch.
739
740``Display::WriteFramebuffer()`` always writes the full framebuffer. It is expected
741that partial updates will be supported. This will likely come as a separate
742function. This is being pushed off until needed to provide as much experience
743with the various display controller APIs as possible to increase the likelihood
744of a well crafted API.
745
746Module Hierarchy
747================
748At present Pigweed's module structure is flat and at the project root level.
749There are currently 134 top level ``pw_*`` directories. This proposal could
750significantly increase this count as each new display driver will be a new
751module. This might be a good time to consider putting modules into a hierarchy.
752
753Pixel Pusher
754============
755``PixelPusher`` was created to remove the details of writing pixels from the
756display driver. Many displays support multiple ways to send pixel data. For
757example the ILI9341 supports SPI and a parallel bus for pixel transport.
758The `STM32F429I-DISC1 <https://www.st.com/en/evaluation-tools/32f429idiscovery.html>`_
759also has a display controller (`LTDC
760<https://www.st.com/resource/en/application_note/an4861-lcdtft-display-controller-ltdc-on-stm32-mcus-stmicroelectronics.pdf>`_)
761which uses an STM proprietary API. The ``PixelPusher`` was essentially created
762to allow that driver to use the LTDC API without the need to be coupled in any
763way to a vendor API.
764
765At present some display drivers use ``pw_spi`` to send commands to the display
766controller, and the ``PixelPusher`` for writing pixel data. It will probably
767be cleaner to move the command writes into the ``PixelPusher`` and remove any
768``pw_spi`` interaction from the display drivers. At this time ``PixelPusher``
769should be renamed.
770
771Copyrighted SDKs
772================
773Some vendors have copyrighted SDKs which cannot be included in the Pigweed
774source code unless the project is willing to have the source covered by more
775than one license. Additionally some SDKs have no simple download link and the
776vendor requires that a developer use a web application to build and download
777an SDK with the desired components. NXP's
778`MCUXpresso SDK Builder <https://mcuxpresso.nxp.com/en/welcome>`_ is an example
779of this. This download process makes it difficult to provide simple instructions
780to the developer and for creating reliable builds as it may be difficult to
781select an older SDK for download.
782