acidportal

- 😈 Worlds smallest Evil Portal on a LilyGo T-QT
git clone git://git.acid.vegas/acidportal.git
Log | Files | Refs | Archive | README | LICENSE

TFT_eSPI_RP2040.c (25195B)

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