acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

TFT_eSPI_RP2040.c (25768B)

      1         ////////////////////////////////////////////////////
      2         //       TFT_eSPI generic driver functions        //
      3         ////////////////////////////////////////////////////
      4 
      5 ////////////////////////////////////////////////////////////////////////////////////////
      6 // Global variables
      7 ////////////////////////////////////////////////////////////////////////////////////////
      8 
      9 #if !defined (RP2040_PIO_INTERFACE) // SPI
     10 
     11   // Select the SPI port and board package to use
     12   #ifdef ARDUINO_ARCH_MBED
     13     // Arduino RP2040 board package
     14     MbedSPI spi = MbedSPI(TFT_MISO, TFT_MOSI, TFT_SCLK);
     15   #else
     16     // Community RP2040 board package by Earle Philhower
     17     //SPIClass& spi = SPI; // will use board package default pins
     18     SPIClassRP2040 spi = SPIClassRP2040(SPI_X, TFT_MISO, -1, TFT_SCLK, TFT_MOSI);
     19   #endif
     20 
     21 #else // PIO interface used (8 bit parallel or SPI)
     22 
     23   #ifdef RP2040_PIO_SPI
     24     #if  defined (SPI_18BIT_DRIVER)
     25       // SPI PIO code for 18 bit colour transmit
     26       #include "pio_SPI_18bit.pio.h"
     27     #else
     28       // SPI PIO code for 16 bit colour transmit
     29       #include "pio_SPI.pio.h"
     30     #endif
     31   #elif defined (TFT_PARALLEL_8_BIT)
     32     #if defined (SSD1963_DRIVER)
     33       // PIO code for 8 bit parallel interface (18 bit colour)
     34       #include "pio_8bit_parallel_18bpp.pio.h"
     35     #else
     36       // PIO code for 8 bit parallel interface (16 bit colour)
     37       #include "pio_8bit_parallel.pio.h"
     38     #endif
     39   #else // must be TFT_PARALLEL_16_BIT
     40     // PIO code for 16 bit parallel interface (16 bit colour)
     41     #include "pio_16bit_parallel.pio.h"
     42   #endif
     43 
     44   // Board package specific differences
     45   #ifdef ARDUINO_ARCH_MBED
     46     // Not supported at the moment
     47     #error The Arduino RP2040 MBED board package is not supported when PIO is used. Use the community package by Earle Philhower.
     48   #endif
     49 
     50   // Community RP2040 board package by Earle Philhower
     51   PIO tft_pio = pio0;     // Code will try both pio's to find a free SM
     52   int8_t pio_sm = 0;  // pioinit will claim a free one
     53   // Updated later with the loading offset of the PIO program.
     54   uint32_t program_offset  = 0;
     55 
     56   // SM stalled mask
     57   uint32_t pull_stall_mask = 0;
     58 
     59   // SM jump instructions to change SM behaviour
     60   uint32_t pio_instr_jmp8  = 0;
     61   uint32_t pio_instr_fill  = 0;
     62   uint32_t pio_instr_addr  = 0;
     63 
     64   // SM "set" instructions to control DC control signal
     65   uint32_t pio_instr_set_dc = 0;
     66   uint32_t pio_instr_clr_dc = 0;
     67 
     68 #endif
     69 
     70 #ifdef RP2040_DMA
     71   int32_t            dma_tx_channel;
     72   dma_channel_config dma_tx_config;
     73 #endif
     74 
     75 ////////////////////////////////////////////////////////////////////////////////////////
     76 #if defined (TFT_SDA_READ) && !defined (RP2040_PIO_INTERFACE)
     77 ////////////////////////////////////////////////////////////////////////////////////////
     78 
     79 /***************************************************************************************
     80 ** Function name:           tft_Read_8
     81 ** Description:             Bit bashed SPI to read bidirectional SDA line
     82 ***************************************************************************************/
     83 uint8_t TFT_eSPI::tft_Read_8(void)
     84 {
     85   uint8_t  ret = 0;
     86 
     87   /*
     88   for (uint8_t i = 0; i < 8; i++) {  // read results
     89     ret <<= 1;
     90     SCLK_L;
     91     if (digitalRead(TFT_MOSI)) ret |= 1;
     92     SCLK_H;
     93   }
     94   */
     95   
     96   ret = spi.transfer(0x00);
     97 
     98   return ret;
     99 }
    100 
    101 /***************************************************************************************
    102 ** Function name:           beginSDA
    103 ** Description:             Detach SPI from pin to permit software SPI
    104 ***************************************************************************************/
    105 void TFT_eSPI::begin_SDA_Read(void)
    106 {
    107   // Release configured SPI port for SDA read
    108   spi.end();
    109 }
    110 
    111 /***************************************************************************************
    112 ** Function name:           endSDA
    113 ** Description:             Attach SPI pins after software SPI
    114 ***************************************************************************************/
    115 void TFT_eSPI::end_SDA_Read(void)
    116 {
    117   // Configure SPI port ready for next TFT access
    118   spi.begin();
    119 }
    120 
    121 ////////////////////////////////////////////////////////////////////////////////////////
    122 #endif // #if defined (TFT_SDA_READ)
    123 ////////////////////////////////////////////////////////////////////////////////////////
    124 
    125 ////////////////////////////////////////////////////////////////////////////////////////
    126 #if defined (RP2040_PIO_INTERFACE)
    127 ////////////////////////////////////////////////////////////////////////////////////////
    128 #ifdef RP2040_PIO_SPI
    129 void pioinit(uint32_t clock_freq) {
    130 
    131   // Find a free SM on one of the PIO's
    132   tft_pio = pio0;
    133   
    134   /*
    135   pio_sm = pio_claim_unused_sm(tft_pio, false); // false means don't panic
    136   // Try pio1 if SM not found
    137   if (pio_sm < 0) {
    138     tft_pio = pio1;
    139     pio_sm = pio_claim_unused_sm(tft_pio, true); // panic this time if no SM is free
    140   }
    141   */
    142 
    143   // Find enough free space on one of the PIO's
    144   tft_pio = pio0;
    145   if (!pio_can_add_program(tft_pio, &tft_io_program)) {
    146     tft_pio = pio1;
    147     if (!pio_can_add_program(tft_pio, &tft_io_program)) {
    148       Serial.println("No room for PIO program!");
    149       return;
    150     }
    151   }
    152 
    153   pio_sm = pio_claim_unused_sm(tft_pio, false);
    154 
    155   // Load the PIO program
    156   program_offset = pio_add_program(tft_pio, &tft_io_program);
    157 
    158   // Associate pins with the PIO
    159   pio_gpio_init(tft_pio, TFT_DC);
    160   pio_gpio_init(tft_pio, TFT_SCLK);
    161   pio_gpio_init(tft_pio, TFT_MOSI);
    162 
    163   // Configure the pins to be outputs
    164   pio_sm_set_consecutive_pindirs(tft_pio, pio_sm, TFT_DC, 1, true);
    165   pio_sm_set_consecutive_pindirs(tft_pio, pio_sm, TFT_SCLK, 1, true);
    166   pio_sm_set_consecutive_pindirs(tft_pio, pio_sm, TFT_MOSI, 1, true);
    167 
    168   // Configure the state machine
    169   pio_sm_config c = tft_io_program_get_default_config(program_offset);
    170 
    171   sm_config_set_set_pins(&c, TFT_DC, 1);
    172   // Define the single side-set pin
    173   sm_config_set_sideset_pins(&c, TFT_SCLK);
    174   // Define the pin used for data output
    175   sm_config_set_out_pins(&c, TFT_MOSI, 1);
    176   // Set clock divider, frequency is set up to 2% faster than specified, or next division down
    177   uint16_t clock_div = 0.98 + clock_get_hz(clk_sys) / (clock_freq * 2.0); // 2 cycles per bit
    178   sm_config_set_clkdiv(&c, clock_div);
    179   // Make a single 8 words FIFO from the 4 words TX and RX FIFOs
    180   sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
    181   // The OSR register shifts to the left, sm designed to send MS byte of a colour first, autopull off
    182   sm_config_set_out_shift(&c, false, false, 0);
    183   // Now load the configuration
    184   pio_sm_init(tft_pio, pio_sm, program_offset + tft_io_offset_start_tx, &c);
    185 
    186   // Start the state machine.
    187   pio_sm_set_enabled(tft_pio, pio_sm, true);
    188 
    189   // Create the pull stall bit mask
    190   pull_stall_mask = 1u << (PIO_FDEBUG_TXSTALL_LSB + pio_sm);
    191 
    192   // Create the assembler instruction for the jump to byte send routine
    193   pio_instr_jmp8  = pio_encode_jmp(program_offset + tft_io_offset_start_8);
    194   pio_instr_fill  = pio_encode_jmp(program_offset + tft_io_offset_block_fill);
    195   pio_instr_addr  = pio_encode_jmp(program_offset + tft_io_offset_set_addr_window);
    196 
    197   pio_instr_set_dc = pio_encode_set((pio_src_dest)0, 1);
    198   pio_instr_clr_dc = pio_encode_set((pio_src_dest)0, 0);
    199 }
    200 #else // 8 or 16 bit parallel
    201 void pioinit(uint16_t clock_div, uint16_t fract_div) {
    202 
    203   // Find a free SM on one of the PIO's
    204   tft_pio = pio0;
    205   pio_sm = pio_claim_unused_sm(tft_pio, false); // false means don't panic
    206   // Try pio1 if SM not found
    207   if (pio_sm < 0) {
    208     tft_pio = pio1;
    209     pio_sm = pio_claim_unused_sm(tft_pio, true); // panic this time if no SM is free
    210   }
    211 /*
    212   // Find enough free space on one of the PIO's
    213   tft_pio = pio0;
    214   if (!pio_can_add_program(tft_pio, &tft_io_program) {
    215     tft_pio = pio1;
    216     if (!pio_can_add_program(tft_pio, &tft_io_program) {
    217       Serial.println("No room for PIO program!");
    218       while(1) delay(100);
    219       return;
    220     }
    221   }
    222 */
    223   #if defined (TFT_PARALLEL_8_BIT)
    224     uint8_t bits = 8;
    225   #else // must be TFT_PARALLEL_16_BIT
    226     uint8_t bits = 16;
    227   #endif
    228   
    229   // Load the PIO program
    230   program_offset = pio_add_program(tft_pio, &tft_io_program);
    231 
    232   // Associate pins with the PIO
    233   pio_gpio_init(tft_pio, TFT_DC);
    234   pio_gpio_init(tft_pio, TFT_WR);
    235 
    236   for (int i = 0; i < bits; i++) {
    237     pio_gpio_init(tft_pio, TFT_D0 + i);
    238   }
    239 
    240   // Configure the pins to be outputs
    241   pio_sm_set_consecutive_pindirs(tft_pio, pio_sm, TFT_DC, 1, true);
    242   pio_sm_set_consecutive_pindirs(tft_pio, pio_sm, TFT_WR, 1, true);
    243   pio_sm_set_consecutive_pindirs(tft_pio, pio_sm, TFT_D0, bits, true);
    244 
    245   // Configure the state machine
    246   pio_sm_config c = tft_io_program_get_default_config(program_offset);
    247   // Define the set pin
    248   sm_config_set_set_pins(&c, TFT_DC, 1);
    249   // Define the single side-set pin
    250   sm_config_set_sideset_pins(&c, TFT_WR);
    251   // Define the consecutive pins that are used for data output
    252   sm_config_set_out_pins(&c, TFT_D0, bits);
    253   // Set clock divider and fractional divider
    254   sm_config_set_clkdiv_int_frac(&c, clock_div, fract_div);
    255   // Make a single 8 words FIFO from the 4 words TX and RX FIFOs
    256   sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
    257   // The OSR register shifts to the left, sm designed to send MS byte of a colour first
    258   sm_config_set_out_shift(&c, false, false, 0);
    259   // Now load the configuration
    260   pio_sm_init(tft_pio, pio_sm, program_offset + tft_io_offset_start_tx, &c);
    261 
    262   // Start the state machine.
    263   pio_sm_set_enabled(tft_pio, pio_sm, true);
    264 
    265   // Create the pull stall bit mask
    266   pull_stall_mask = 1u << (PIO_FDEBUG_TXSTALL_LSB + pio_sm);
    267 
    268   // Create the instructions for the jumps to send routines
    269   pio_instr_jmp8  = pio_encode_jmp(program_offset + tft_io_offset_start_8);
    270   pio_instr_fill  = pio_encode_jmp(program_offset + tft_io_offset_block_fill);
    271   pio_instr_addr  = pio_encode_jmp(program_offset + tft_io_offset_set_addr_window);
    272 
    273   // Create the instructions to set and clear the DC signal
    274   pio_instr_set_dc = pio_encode_set((pio_src_dest)0, 1);
    275   pio_instr_clr_dc = pio_encode_set((pio_src_dest)0, 0);
    276 }
    277 #endif
    278 
    279 /***************************************************************************************
    280 ** Function name:           pushBlock - for generic processor and parallel display
    281 ** Description:             Write a block of pixels of the same colour
    282 ***************************************************************************************/
    283 #ifdef RP2040_PIO_PUSHBLOCK
    284 // PIO handles pixel block fill writes
    285 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len)
    286 {
    287 #if  defined (SPI_18BIT_DRIVER) || (defined (SSD1963_DRIVER) && defined (TFT_PARALLEL_8_BIT))
    288   uint32_t col = ((color & 0xF800)<<8) | ((color & 0x07E0)<<5) | ((color & 0x001F)<<3);
    289   if (len) {
    290     WAIT_FOR_STALL;
    291     tft_pio->sm[pio_sm].instr = pio_instr_fill;
    292 
    293     TX_FIFO = col;
    294     TX_FIFO = --len; // Decrement first as PIO sends n+1
    295   }
    296 #else
    297   if (len) {
    298     WAIT_FOR_STALL;
    299     tft_pio->sm[pio_sm].instr = pio_instr_fill;
    300 
    301     TX_FIFO = color;
    302     TX_FIFO = --len; // Decrement first as PIO sends n+1
    303   }
    304 #endif
    305 }
    306 
    307 #else
    308 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){
    309 
    310   while (len > 4) {
    311     // 5 seems to be the optimum for maximum transfer rate
    312     WAIT_FOR_FIFO_FREE(5);
    313     TX_FIFO = color;
    314     TX_FIFO = color;
    315     TX_FIFO = color;
    316     TX_FIFO = color;
    317     TX_FIFO = color;
    318 
    319     len -= 5;
    320   }
    321 
    322   if (len) {
    323     // There could be a maximum of 4 words left  to send
    324     WAIT_FOR_FIFO_FREE(4);
    325     while (len--) TX_FIFO = color;
    326   }
    327 }
    328 #endif
    329 
    330 /***************************************************************************************
    331 ** Function name:           pushPixels - for generic processor and parallel display
    332 ** Description:             Write a sequence of pixels
    333 ***************************************************************************************/
    334 void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){
    335 #if  defined (SPI_18BIT_DRIVER) || (defined (SSD1963_DRIVER) && defined (TFT_PARALLEL_8_BIT))
    336   uint16_t *data = (uint16_t*)data_in;
    337   if (_swapBytes) {
    338     while ( len-- ) {
    339       uint32_t col = *data++;
    340       tft_Write_16(col);
    341     }
    342   }
    343   else {
    344     while ( len-- ) {
    345       uint32_t col = *data++;
    346       tft_Write_16S(col);
    347     }
    348   }
    349 #else
    350   const uint16_t *data = (uint16_t*)data_in;
    351 
    352   // PIO sends MS byte first, so bytes are already swapped on transmit
    353   if(_swapBytes) {
    354     while (len > 4) {
    355       WAIT_FOR_FIFO_FREE(5);
    356       TX_FIFO = data[0];
    357       TX_FIFO = data[1];
    358       TX_FIFO = data[2];
    359       TX_FIFO = data[3];
    360       TX_FIFO = data[4];
    361       data += 5;
    362       len  -= 5;
    363     }
    364 
    365     if (len) {
    366       WAIT_FOR_FIFO_FREE(4);
    367       while(len--) TX_FIFO = *data++;
    368     }
    369   }
    370   else {
    371     while (len > 4) {
    372       WAIT_FOR_FIFO_FREE(5);
    373       TX_FIFO = data[0] << 8 | data[0] >> 8;
    374       TX_FIFO = data[1] << 8 | data[1] >> 8;
    375       TX_FIFO = data[2] << 8 | data[2] >> 8;
    376       TX_FIFO = data[3] << 8 | data[3] >> 8;
    377       TX_FIFO = data[4] << 8 | data[4] >> 8;
    378       data += 5;
    379       len  -= 5;
    380     }
    381 
    382     if (len) {
    383       WAIT_FOR_FIFO_FREE(4);
    384       while(len--) {
    385         TX_FIFO = *data << 8 | *data >> 8;
    386         data++;
    387       }
    388     }
    389   }
    390 #endif
    391 }
    392 
    393 /***************************************************************************************
    394 ** Function name:           GPIO direction control  - supports class functions
    395 ** Description:             Set parallel bus to INPUT or OUTPUT
    396 ***************************************************************************************/
    397 void TFT_eSPI::busDir(uint32_t mask, uint8_t mode)
    398 {
    399   // Avoid warnings
    400   mask = mask;
    401   mode = mode;
    402 /*
    403   // mask is unused for generic processor
    404   // Arduino native functions suited well to a generic driver
    405   pinMode(TFT_D0, mode);
    406   pinMode(TFT_D1, mode);
    407   pinMode(TFT_D2, mode);
    408   pinMode(TFT_D3, mode);
    409   pinMode(TFT_D4, mode);
    410   pinMode(TFT_D5, mode);
    411   pinMode(TFT_D6, mode);
    412   pinMode(TFT_D7, mode);
    413 */
    414 }
    415 
    416 /***************************************************************************************
    417 ** Function name:           GPIO direction control  - supports class functions
    418 ** Description:             Faster GPIO pin input/output switch
    419 ***************************************************************************************/
    420 void TFT_eSPI::gpioMode(uint8_t gpio, uint8_t mode)
    421 {
    422   // Avoid warnings
    423   gpio = gpio;
    424   mode = mode;
    425 
    426 }
    427 
    428 /***************************************************************************************
    429 ** Function name:           read byte  - supports class functions
    430 ** Description:             Read a byte - parallel bus only - not supported yet
    431 ***************************************************************************************/
    432 uint8_t TFT_eSPI::readByte(void)
    433 {
    434   uint8_t b = 0;
    435 /*
    436   busDir(0, INPUT);
    437   digitalWrite(TFT_RD, LOW);
    438 
    439   b |= digitalRead(TFT_D0) << 0;
    440   b |= digitalRead(TFT_D1) << 1;
    441   b |= digitalRead(TFT_D2) << 2;
    442   b |= digitalRead(TFT_D3) << 3;
    443   b |= digitalRead(TFT_D4) << 4;
    444   b |= digitalRead(TFT_D5) << 5;
    445   b |= digitalRead(TFT_D6) << 6;
    446   b |= digitalRead(TFT_D7) << 7;
    447 
    448   digitalWrite(TFT_RD, HIGH);
    449   busDir(0, OUTPUT);
    450 */
    451   return b;
    452 }
    453 
    454 ////////////////////////////////////////////////////////////////////////////////////////
    455 #elif defined (RPI_WRITE_STROBE)  // For RPi TFT with write strobe
    456 ////////////////////////////////////////////////////////////////////////////////////////
    457 
    458 /***************************************************************************************
    459 ** Function name:           pushBlock - for ESP32 or RP2040 RPi TFT
    460 ** Description:             Write a block of pixels of the same colour
    461 ***************************************************************************************/
    462 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){
    463 
    464   if(len) { tft_Write_16(color); len--; }
    465   while(len--) {WR_L; WR_H;}
    466 }
    467 
    468 /***************************************************************************************
    469 ** Function name:           pushPixels - for ESP32 or RP2040 RPi TFT
    470 ** Description:             Write a sequence of pixels
    471 ***************************************************************************************/
    472 void TFT_eSPI::pushPixels(const void* data_in, uint32_t len)
    473 {
    474   uint16_t *data = (uint16_t*)data_in;
    475 
    476   if (_swapBytes) while ( len-- ) {tft_Write_16S(*data); data++;}
    477   else while ( len-- ) {tft_Write_16(*data); data++;}
    478 }
    479 
    480 ////////////////////////////////////////////////////////////////////////////////////////
    481 #elif defined (SPI_18BIT_DRIVER) // SPI 18 bit colour
    482 ////////////////////////////////////////////////////////////////////////////////////////
    483 
    484 /***************************************************************************************
    485 ** Function name:           pushBlock - for RP2040 and 3 byte RGB display
    486 ** Description:             Write a block of pixels of the same colour
    487 ***************************************************************************************/
    488 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len)
    489 {
    490   uint16_t r = (color & 0xF800)>>8;
    491   uint16_t g = (color & 0x07E0)>>3;
    492   uint16_t b = (color & 0x001F)<<3;
    493 
    494   // If more than 32 pixels then change to 16 bit transfers with concatenated pixels
    495   if (len > 32) {
    496     uint32_t rg = r<<8 | g;
    497     uint32_t br = b<<8 | r;
    498     uint32_t gb = g<<8 | b;
    499     // Must wait before changing to 16 bit
    500     while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {};
    501     hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS);
    502     while ( len > 1 ) {
    503       while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = rg;
    504       while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = br;
    505       while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = gb;
    506       len -= 2;
    507     }
    508     // Must wait before changing back to 8 bit
    509     while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {};
    510     hw_write_masked(&spi_get_hw(SPI_X)->cr0, (8 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS);
    511   }
    512 
    513   // Mop up the remaining pixels
    514   while ( len-- ) {tft_Write_8N(r);tft_Write_8N(g);tft_Write_8N(b);}
    515 }
    516 
    517 /***************************************************************************************
    518 ** Function name:           pushPixels - for RP2040 and 3 byte RGB display
    519 ** Description:             Write a sequence of pixels
    520 ***************************************************************************************/
    521 void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){
    522 
    523   uint16_t *data = (uint16_t*)data_in;
    524   if (_swapBytes) {
    525     while ( len-- ) {
    526       uint32_t col = *data++;
    527       tft_Write_16(col);
    528     }
    529   }
    530   else {
    531     while ( len-- ) {
    532       uint32_t col = *data++;
    533       tft_Write_16S(col);
    534     }
    535   }
    536 }
    537 
    538 ////////////////////////////////////////////////////////////////////////////////////////
    539 #else //                   Standard SPI 16 bit colour TFT
    540 ////////////////////////////////////////////////////////////////////////////////////////
    541 
    542 /***************************************************************************************
    543 ** Function name:           pushBlock - for RP2040
    544 ** Description:             Write a block of pixels of the same colour
    545 ***************************************************************************************/
    546 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){
    547   while(len--)
    548   {
    549     while (!spi_is_writable(SPI_X)){};
    550     spi_get_hw(SPI_X)->dr = (uint32_t)color;
    551   }
    552 }
    553 
    554 /***************************************************************************************
    555 ** Function name:           pushPixels - for RP2040
    556 ** Description:             Write a sequence of pixels
    557 ***************************************************************************************/
    558 void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){
    559   uint16_t *data = (uint16_t*)data_in;
    560   if (_swapBytes) {
    561     while(len--)
    562     {
    563       while (!spi_is_writable(SPI_X)){};
    564       spi_get_hw(SPI_X)->dr = (uint32_t)(*data++);
    565     }
    566   }
    567   else
    568   {
    569     while(len--)
    570     {
    571       uint16_t color = *data++;
    572       color = color >> 8 | color << 8;
    573       while (!spi_is_writable(SPI_X)){};
    574       spi_get_hw(SPI_X)->dr = (uint32_t)color;
    575     }
    576   }
    577 }
    578 
    579 ////////////////////////////////////////////////////////////////////////////////////////
    580 #endif // End of display interface specific functions
    581 ////////////////////////////////////////////////////////////////////////////////////////
    582 
    583 
    584 ////////////////////////////////////////////////////////////////////////////////////////
    585 #ifdef RP2040_DMA // DMA functions for 16 bit SPI and 8/16 bit parallel displays
    586 ////////////////////////////////////////////////////////////////////////////////////////
    587 /*
    588 These are created in header file:
    589   uint32_t           dma_tx_channel;
    590   dma_channel_config dma_tx_config;
    591 */
    592 
    593 /***************************************************************************************
    594 ** Function name:           dmaBusy
    595 ** Description:             Check if DMA is busy
    596 ***************************************************************************************/
    597 bool TFT_eSPI::dmaBusy(void) {
    598   if (!DMA_Enabled) return false;
    599 
    600   if (dma_channel_is_busy(dma_tx_channel)) return true;
    601 
    602 #if !defined (RP2040_PIO_INTERFACE)
    603   // For SPI must also wait for FIFO to flush and reset format
    604   while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {};
    605   hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS);
    606 #endif
    607 
    608   return false;
    609 }
    610 
    611 /***************************************************************************************
    612 ** Function name:           dmaWait
    613 ** Description:             Wait until DMA is over (blocking!)
    614 ***************************************************************************************/
    615 void TFT_eSPI::dmaWait(void)
    616 {
    617   while (dma_channel_is_busy(dma_tx_channel));
    618 
    619 #if !defined (RP2040_PIO_INTERFACE)
    620   // For SPI must also wait for FIFO to flush and reset format
    621   while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {};
    622   hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS);
    623 #endif
    624 }
    625 
    626 /***************************************************************************************
    627 ** Function name:           pushPixelsDMA
    628 ** Description:             Push pixels to TFT
    629 ***************************************************************************************/
    630 void TFT_eSPI::pushPixelsDMA(uint16_t* image, uint32_t len)
    631 {
    632   if ((len == 0) || (!DMA_Enabled)) return;
    633 
    634   dmaWait();
    635 
    636   channel_config_set_bswap(&dma_tx_config, !_swapBytes);
    637 
    638 #if !defined (RP2040_PIO_INTERFACE)
    639   dma_channel_configure(dma_tx_channel, &dma_tx_config, &spi_get_hw(SPI_X)->dr, (uint16_t*)image, len, true);
    640 #else
    641   dma_channel_configure(dma_tx_channel, &dma_tx_config, &tft_pio->txf[pio_sm], (uint16_t*)image, len, true);
    642 #endif
    643 }
    644 
    645 /***************************************************************************************
    646 ** Function name:           pushImageDMA
    647 ** Description:             Push image to a window
    648 ***************************************************************************************/
    649 // This will clip to the viewport
    650 void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t* image, uint16_t* buffer)
    651 {
    652   if ((x >= _vpW) || (y >= _vpH) || (!DMA_Enabled)) return;
    653 
    654   int32_t dx = 0;
    655   int32_t dy = 0;
    656   int32_t dw = w;
    657   int32_t dh = h;
    658 
    659   if (x < _vpX) { dx = _vpX - x; dw -= dx; x = _vpX; }
    660   if (y < _vpY) { dy = _vpY - y; dh -= dy; y = _vpY; }
    661 
    662   if ((x + dw) > _vpW ) dw = _vpW - x;
    663   if ((y + dh) > _vpH ) dh = _vpH - y;
    664 
    665   if (dw < 1 || dh < 1) return;
    666 
    667   uint32_t len = dw*dh;
    668 
    669   if (buffer == nullptr) {
    670     buffer = image;
    671     dmaWait();
    672   }
    673 
    674   // If image is clipped, copy pixels into a contiguous block
    675   if ( (dw != w) || (dh != h) ) {
    676     for (int32_t yb = 0; yb < dh; yb++) {
    677       memmove((uint8_t*) (buffer + yb * dw), (uint8_t*) (image + dx + w * (yb + dy)), dw << 1);
    678     }
    679   }
    680   // else, if a buffer pointer has been provided copy whole image to the buffer
    681   else if (buffer != image || _swapBytes) {
    682     memcpy(buffer, image, len*2);
    683   }
    684 
    685   dmaWait(); // In case we did not wait earlier
    686 
    687   setAddrWindow(x, y, dw, dh);
    688 
    689   channel_config_set_bswap(&dma_tx_config, !_swapBytes);
    690 
    691 #if !defined (RP2040_PIO_INTERFACE)
    692   dma_channel_configure(dma_tx_channel, &dma_tx_config, &spi_get_hw(SPI_X)->dr, (uint16_t*)buffer, len, true);
    693 #else
    694   dma_channel_configure(dma_tx_channel, &dma_tx_config, &tft_pio->txf[pio_sm], (uint16_t*)buffer, len, true);
    695 #endif
    696 }
    697 
    698 /***************************************************************************************
    699 ** Function name:           initDMA
    700 ** Description:             Initialise the DMA engine - returns true if init OK
    701 ***************************************************************************************/
    702 bool TFT_eSPI::initDMA(bool ctrl_cs)
    703 {
    704   if (DMA_Enabled) return false;
    705 
    706   ctrl_cs = ctrl_cs; // stop unused parameter warning
    707 
    708   dma_tx_channel = dma_claim_unused_channel(false);
    709   
    710   if (dma_tx_channel < 0) return false;
    711 
    712   dma_tx_config = dma_channel_get_default_config(dma_tx_channel);
    713 
    714   channel_config_set_transfer_data_size(&dma_tx_config, DMA_SIZE_16);
    715 #if !defined (RP2040_PIO_INTERFACE)
    716   channel_config_set_dreq(&dma_tx_config, spi_get_index(SPI_X) ? DREQ_SPI1_TX : DREQ_SPI0_TX);
    717 #else
    718   channel_config_set_dreq(&dma_tx_config, pio_get_dreq(tft_pio, pio_sm, true));
    719 #endif
    720 
    721   DMA_Enabled = true;
    722   return true;
    723 }
    724 
    725 /***************************************************************************************
    726 ** Function name:           deInitDMA
    727 ** Description:             Disconnect the DMA engine from SPI
    728 ***************************************************************************************/
    729 void TFT_eSPI::deInitDMA(void)
    730 {
    731   if (!DMA_Enabled) return;
    732   dma_channel_unclaim(dma_tx_channel);
    733   DMA_Enabled = false;
    734 }
    735 
    736 ////////////////////////////////////////////////////////////////////////////////////////
    737 #endif // End of DMA FUNCTIONS
    738 ////////////////////////////////////////////////////////////////////////////////////////