Minor performance improvements for ESP32
Remove legacy changes for double buffered SPI Eliminate unecessary ESP32 SPI read/modify write Eliminate two stage control bit changes (may impact some diplays with setup/hold timing and show ESP32 hardware register bug?) Add single register write for CS and DC command Make setAddrWindow smarter (as used in previous AVR library) Improve compatibility with GFX sketches Re-arrange comments slightly to improve positioning
This commit is contained in:
1 parent
c7735a8b8a
commit
7a3d6bcab9
9 files changed
+275
-158
No files matched your search
+140
-98
@@ -185,8 +185,8 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h)
|
||||
|
||||
_booted = true;
|
||||
|
||||
addr_row = 0xFFFF;
|
||||
addr_col = 0xFFFF;
|
||||
ys_row = ye_row = addr_row = 0xFFFF;
|
||||
xs_col = xe_col = addr_col = 0xFFFF;
|
||||
|
||||
#ifdef LOAD_GLCD
|
||||
fontsloaded = 0x0002; // Bit 1 set
|
||||
@@ -530,7 +530,7 @@ uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index)
|
||||
CS_L;
|
||||
tft_Write_8(cmd_function);
|
||||
DC_D;
|
||||
reg = tft_Write_8(0);
|
||||
reg = tft_Read_8(0);
|
||||
CS_H;
|
||||
|
||||
spi_end_read();
|
||||
@@ -628,15 +628,15 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0)
|
||||
// Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte
|
||||
// as the TFT stores colours as 18 bits
|
||||
#if !defined (ILI9488_DRIVER)
|
||||
uint8_t r = tft_Write_8(0);
|
||||
uint8_t g = tft_Write_8(0);
|
||||
uint8_t b = tft_Write_8(0);
|
||||
uint8_t r = tft_Read_8(0);
|
||||
uint8_t g = tft_Read_8(0);
|
||||
uint8_t b = tft_Read_8(0);
|
||||
#else
|
||||
// The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse
|
||||
// so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left
|
||||
uint8_t r = (tft_Write_8(0)&0x7E)<<1;
|
||||
uint8_t g = (tft_Write_8(0)&0x7E)<<1;
|
||||
uint8_t b = (tft_Write_8(0)&0x7E)<<1;
|
||||
uint8_t r = (tft_Read_8(0)&0x7E)<<1;
|
||||
uint8_t g = (tft_Read_8(0)&0x7E)<<1;
|
||||
uint8_t b = (tft_Read_8(0)&0x7E)<<1;
|
||||
#endif
|
||||
|
||||
CS_H;
|
||||
@@ -768,15 +768,15 @@ void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t
|
||||
// Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte
|
||||
// as the TFT stores colours as 18 bits
|
||||
#if !defined (ILI9488_DRIVER)
|
||||
uint8_t r = tft_Write_8(0);
|
||||
uint8_t g = tft_Write_8(0);
|
||||
uint8_t b = tft_Write_8(0);
|
||||
uint8_t r = tft_Read_8(0);
|
||||
uint8_t g = tft_Read_8(0);
|
||||
uint8_t b = tft_Read_8(0);
|
||||
#else
|
||||
// The 6 colour bits are in LS 6 bits of each byte but we do not include the extra clock pulse
|
||||
// so we use a trick and mask the middle 6 bits of the byte, then only shift 1 place left
|
||||
uint8_t r = (tft_Write_8(0)&0x7E)<<1;
|
||||
uint8_t g = (tft_Write_8(0)&0x7E)<<1;
|
||||
uint8_t b = (tft_Write_8(0)&0x7E)<<1;
|
||||
uint8_t r = (tft_Read_8(0)&0x7E)<<1;
|
||||
uint8_t g = (tft_Read_8(0)&0x7E)<<1;
|
||||
uint8_t b = (tft_Read_8(0)&0x7E)<<1;
|
||||
#endif
|
||||
|
||||
// Swapped colour byte order for compatibility with pushRect()
|
||||
@@ -1354,15 +1354,15 @@ void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_
|
||||
// Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte
|
||||
// as the TFT stores colours as 18 bits
|
||||
#if !defined (ILI9488_DRIVER)
|
||||
*data++ = tft_Write_8(0);
|
||||
*data++ = tft_Write_8(0);
|
||||
*data++ = tft_Write_8(0);
|
||||
*data++ = tft_Read_8(0);
|
||||
*data++ = tft_Read_8(0);
|
||||
*data++ = tft_Read_8(0);
|
||||
#else
|
||||
// The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse
|
||||
// so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left
|
||||
*data++ = (tft_Write_8(0)&0x7E)<<1;
|
||||
*data++ = (tft_Write_8(0)&0x7E)<<1;
|
||||
*data++ = (tft_Write_8(0)&0x7E)<<1;
|
||||
*data++ = (tft_Read_8(0)&0x7E)<<1;
|
||||
*data++ = (tft_Read_8(0)&0x7E)<<1;
|
||||
*data++ = (tft_Read_8(0)&0x7E)<<1;
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -2473,7 +2473,7 @@ void TFT_eSPI::setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1)
|
||||
// Chip select stays low, use setWindow() from sketches
|
||||
|
||||
#if defined (ESP8266) && !defined (RPI_WRITE_STROBE) && !defined (RPI_ILI9486_DRIVER)
|
||||
inline void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye)
|
||||
void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye)
|
||||
{
|
||||
//spi_begin();
|
||||
|
||||
@@ -2563,8 +2563,8 @@ void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye)
|
||||
|
||||
DC_D;
|
||||
|
||||
uint8_t xBin[] = { 0, (uint8_t) (xs>>8), 0, (uint8_t) (xs>>0), 0, (uint8_t) (xe>>8), 0, (uint8_t) (xe>>0), };
|
||||
SPI.writePattern(&xBin[0], 8, 1);
|
||||
uint8_t xb[] = { 0, (uint8_t) (xs>>8), 0, (uint8_t) (xs>>0), 0, (uint8_t) (xe>>8), 0, (uint8_t) (xe>>0), };
|
||||
SPI.writePattern(&xb[0], 8, 1);
|
||||
|
||||
// Row addr set
|
||||
DC_C;
|
||||
@@ -2577,8 +2577,8 @@ void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye)
|
||||
|
||||
DC_D;
|
||||
|
||||
uint8_t yBin[] = { 0, (uint8_t) (ys>>8), 0, (uint8_t) (ys>>0), 0, (uint8_t) (ye>>8), 0, (uint8_t) (ye>>0), };
|
||||
SPI.writePattern(&yBin[0], 8, 1);
|
||||
uint8_t yb[] = { 0, (uint8_t) (ys>>8), 0, (uint8_t) (ys>>0), 0, (uint8_t) (ye>>8), 0, (uint8_t) (ye>>0), };
|
||||
SPI.writePattern(&yb[0], 8, 1);
|
||||
|
||||
// write to RAM
|
||||
DC_C;
|
||||
@@ -2596,7 +2596,7 @@ void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye)
|
||||
#else
|
||||
|
||||
#if defined (ESP8266) && defined (RPI_ILI9486_DRIVER) // This is for the RPi display that needs 16 bits
|
||||
inline void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
|
||||
void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
|
||||
{
|
||||
//spi_begin();
|
||||
|
||||
@@ -2673,7 +2673,7 @@ inline void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t
|
||||
|
||||
#else // This is for the ESP32
|
||||
|
||||
inline void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
|
||||
void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
|
||||
{
|
||||
//spi_begin();
|
||||
|
||||
@@ -2687,42 +2687,48 @@ inline void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t
|
||||
y1+=rowstart;
|
||||
#endif
|
||||
|
||||
#if !defined (RPI_ILI9486_DRIVER)
|
||||
uint32_t xaw = ((uint32_t)x0 << 16) | x1;
|
||||
uint32_t yaw = ((uint32_t)y0 << 16) | y1;
|
||||
#endif
|
||||
CS_L_DC_C;
|
||||
|
||||
// Column addr set
|
||||
DC_C;
|
||||
CS_L;
|
||||
// No need to send x if it has not changed (small speed-up of complex transparent Sprites and bitmaps)
|
||||
// Nb: caller must fill the set address area so that setting same area again will fill from xs, ys
|
||||
if (xs_col != x0 || xe_col != x1) {
|
||||
// Column addr set
|
||||
|
||||
tft_Write_8(TFT_CASET);
|
||||
tft_Write_8(TFT_CASET);
|
||||
|
||||
DC_D;
|
||||
DC_D;
|
||||
|
||||
#if defined (RPI_ILI9486_DRIVER)
|
||||
uint8_t xBin[] = { 0, (uint8_t) (x0>>8), 0, (uint8_t) (x0>>0), 0, (uint8_t) (x1>>8), 0, (uint8_t) (x1>>0), };
|
||||
SPI.writePattern(&xBin[0], 8, 1);
|
||||
uint8_t xb[] = { 0, (uint8_t) (x0>>8), 0, (uint8_t) (x0>>0), 0, (uint8_t) (x1>>8), 0, (uint8_t) (x1>>0), };
|
||||
SPI.writePattern(&xb[0], 8, 1);
|
||||
#else
|
||||
tft_Write_32(xaw);
|
||||
tft_Write_32(SPI_32(x0, x1));
|
||||
#endif
|
||||
|
||||
// Row addr set
|
||||
DC_C;
|
||||
tft_Write_8(TFT_PASET);
|
||||
DC_C;
|
||||
xs_col = x0; xe_col = x1;
|
||||
}
|
||||
|
||||
DC_D;
|
||||
// No need to send y if it has not changed
|
||||
if (ys_row != y0 || ye_row != y1) {
|
||||
|
||||
// Row addr set
|
||||
tft_Write_8(TFT_PASET);
|
||||
|
||||
DC_D;
|
||||
|
||||
#if defined (RPI_ILI9486_DRIVER)
|
||||
uint8_t yBin[] = { 0, (uint8_t) (y0>>8), 0, (uint8_t) (y0>>0), 0, (uint8_t) (y1>>8), 0, (uint8_t) (y1>>0), };
|
||||
SPI.writePattern(&yBin[0], 8, 1);
|
||||
uint8_t yb[] = { 0, (uint8_t) (y0>>8), 0, (uint8_t) (y0>>0), 0, (uint8_t) (y1>>8), 0, (uint8_t) (y1>>0), };
|
||||
SPI.writePattern(&yb[0], 8, 1);
|
||||
#else
|
||||
tft_Write_32(yaw);
|
||||
tft_Write_32(SPI_32(y0, y1));
|
||||
#endif
|
||||
|
||||
DC_C;
|
||||
ys_row = y0; ye_row = y1;
|
||||
}
|
||||
|
||||
// write to RAM
|
||||
DC_C;
|
||||
|
||||
tft_Write_8(TFT_RAMWR);
|
||||
|
||||
DC_D;
|
||||
@@ -2819,19 +2825,15 @@ void TFT_eSPI::readAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
|
||||
y1+=rowstart;
|
||||
#endif
|
||||
|
||||
uint32_t xaw = ((uint32_t)x0 << 16) | x1;
|
||||
uint32_t yaw = ((uint32_t)y0 << 16) | y1;
|
||||
|
||||
// Column addr set
|
||||
DC_C;
|
||||
CS_L;
|
||||
CS_L_DC_C;
|
||||
|
||||
tft_Write_8(TFT_CASET);
|
||||
|
||||
|
||||
DC_D;
|
||||
|
||||
tft_Write_32(xaw);
|
||||
tft_Write_32(SPI_32(x0, x1));
|
||||
|
||||
// Row addr set
|
||||
DC_C;
|
||||
@@ -2840,7 +2842,7 @@ void TFT_eSPI::readAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
|
||||
|
||||
DC_D;
|
||||
|
||||
tft_Write_32(yaw);
|
||||
tft_Write_32(SPI_32(y0, y1));
|
||||
|
||||
DC_C;
|
||||
tft_Write_8(TFT_RAMRD); // Read CGRAM command
|
||||
@@ -3053,52 +3055,49 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color)
|
||||
y+=rowstart;
|
||||
#endif
|
||||
|
||||
#if !defined (RPI_ILI9486_DRIVER)
|
||||
uint32_t xaw = ((uint32_t)x << 16) | x;
|
||||
uint32_t yaw = ((uint32_t)y << 16) | y;
|
||||
#endif
|
||||
|
||||
CS_L;
|
||||
CS_L_DC_C;
|
||||
|
||||
// No need to send x if it has not changed (speeds things up)
|
||||
if (addr_col != x) {
|
||||
|
||||
DC_C;
|
||||
|
||||
tft_Write_8(TFT_CASET);
|
||||
|
||||
DC_D;
|
||||
|
||||
#if defined (RPI_ILI9486_DRIVER)
|
||||
uint8_t xBin[] = { 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0), 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0), };
|
||||
SPI.writePattern(&xBin[0], 8, 1);
|
||||
uint8_t xb[] = { 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0), 0, (uint8_t) (x>>8), 0, (uint8_t) (x>>0), };
|
||||
SPI.writePattern(&xb[0], 8, 1);
|
||||
#else
|
||||
tft_Write_32(xaw);
|
||||
tft_Write_32(SPI_32(x, x));
|
||||
#endif
|
||||
|
||||
|
||||
DC_C;
|
||||
|
||||
addr_col = x;
|
||||
xs_col = xe_col = x;
|
||||
|
||||
}
|
||||
|
||||
// No need to send y if it has not changed (speeds things up)
|
||||
if (addr_row != y) {
|
||||
|
||||
DC_C;
|
||||
|
||||
tft_Write_8(TFT_PASET);
|
||||
|
||||
DC_D;
|
||||
|
||||
#if defined (RPI_ILI9486_DRIVER)
|
||||
uint8_t yBin[] = { 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0), 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0), };
|
||||
SPI.writePattern(&yBin[0], 8, 1);
|
||||
uint8_t yb[] = { 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0), 0, (uint8_t) (y>>8), 0, (uint8_t) (y>>0), };
|
||||
SPI.writePattern(&yb[0], 8, 1);
|
||||
#else
|
||||
tft_Write_32(yaw);
|
||||
tft_Write_32(SPI_32(y, y));
|
||||
#endif
|
||||
|
||||
DC_C;
|
||||
|
||||
addr_row = y;
|
||||
ys_row = ye_row = y;
|
||||
}
|
||||
|
||||
DC_C;
|
||||
|
||||
tft_Write_8(TFT_RAMWR);
|
||||
|
||||
@@ -3159,6 +3158,48 @@ void TFT_eSPI::pushColor(uint16_t color, uint32_t len)
|
||||
spi_end();
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: startWrite
|
||||
** Description: begin transaction with CS low, MUST later call endWrite
|
||||
***************************************************************************************/
|
||||
void TFT_eSPI::startWrite(void)
|
||||
{
|
||||
spi_begin();
|
||||
|
||||
CS_L;
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: endWrite
|
||||
** Description: end transaction with CS high
|
||||
***************************************************************************************/
|
||||
void TFT_eSPI::endWrite(void)
|
||||
{
|
||||
|
||||
CS_H;
|
||||
|
||||
spi_end();
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: writeColor (use startWrite() and endWrite() before & after)
|
||||
** Description: raw write of "len" pixels avoiding transaction check
|
||||
***************************************************************************************/
|
||||
void TFT_eSPI::writeColor(uint16_t color, uint32_t len)
|
||||
{
|
||||
#ifdef RPI_WRITE_STROBE
|
||||
uint8_t colorBin[] = { (uint8_t) (color >> 8), (uint8_t) color };
|
||||
if(len) SPI.writePattern(&colorBin[0], 2, 1); len--;
|
||||
while(len--) {WR_L; WR_H;}
|
||||
#else
|
||||
#if defined (ESP32_PARALLEL)
|
||||
while (len--) {tft_Write_16(color);}
|
||||
#else
|
||||
writeBlock(color, len);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: pushColors
|
||||
** Description: push an array of pixels for 16 bit raw image drawing
|
||||
@@ -4655,7 +4696,7 @@ void writeBlock(uint16_t color, uint32_t repeat)
|
||||
#elif defined (ILI9488_DRIVER)
|
||||
|
||||
#ifdef ESP8266
|
||||
void writeBlock(uint16_t color, uint32_t repeat)
|
||||
void TFT_eSPI::writeBlock(uint16_t color, uint32_t repeat)
|
||||
{
|
||||
|
||||
uint32_t mask = ~(SPIMMOSI << SPILMOSI);
|
||||
@@ -4722,9 +4763,6 @@ void writeBlock(uint16_t color, uint32_t repeat)
|
||||
}
|
||||
#else // Now the code for ESP32 and ILI9488
|
||||
|
||||
#include "soc/spi_reg.h"
|
||||
#define SPI_NUM 0x3
|
||||
|
||||
void writeBlock(uint16_t color, uint32_t repeat)
|
||||
{
|
||||
// Split out the colours
|
||||
@@ -4795,38 +4833,46 @@ void writeBlock(uint16_t color, uint32_t repeat)
|
||||
|
||||
#else // Low level register based ESP32 code for 16 bit colour SPI TFTs
|
||||
|
||||
#include "soc/spi_reg.h"
|
||||
#define SPI_NUM 0x3
|
||||
|
||||
|
||||
void writeBlock(uint16_t color, uint32_t repeat)
|
||||
{
|
||||
uint16_t color16 = (color >> 8) | (color << 8);
|
||||
uint32_t color32 = color16 | color16 << 16;
|
||||
uint32_t color32 = SPI_32(color, color);
|
||||
|
||||
if (repeat > 15)
|
||||
if (repeat > 31) // Revert legacy toggle buffer change
|
||||
{
|
||||
SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_NUM), SPI_USR_MOSI_DBITLEN, 255, SPI_USR_MOSI_DBITLEN_S);
|
||||
|
||||
while(repeat>15)
|
||||
WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), 511);
|
||||
while(repeat>31)
|
||||
{
|
||||
while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR);
|
||||
for (uint32_t i=0; i<8; i++) WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + (i << 2)), color32);
|
||||
WRITE_PERI_REG(SPI_W0_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W1_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W2_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W3_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W4_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W5_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W6_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W7_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W8_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W9_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W10_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W11_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W12_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W13_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W14_REG(SPI_NUM), color32);
|
||||
WRITE_PERI_REG(SPI_W15_REG(SPI_NUM), color32);
|
||||
SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR);
|
||||
repeat -= 16;
|
||||
repeat -= 32;
|
||||
}
|
||||
while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR);
|
||||
}
|
||||
|
||||
if (repeat)
|
||||
{
|
||||
repeat = (repeat << 4) - 1;
|
||||
SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_NUM), SPI_USR_MOSI_DBITLEN, repeat, SPI_USR_MOSI_DBITLEN_S);
|
||||
for (uint32_t i=0; i<8; i++) WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + (i << 2)), color32);
|
||||
// Revert toggle buffer change
|
||||
WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_NUM), (repeat << 4) - 1);
|
||||
for (uint32_t i=0; i <= (repeat>>1); i++) WRITE_PERI_REG((SPI_W0_REG(SPI_NUM) + (i << 2)), color32);
|
||||
SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR);
|
||||
while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM))&SPI_USR);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4975,10 +5021,6 @@ void TFT_eSPI::getSetup(setup_t &tft_settings)
|
||||
|
||||
#include "Extensions/Sprite.cpp"
|
||||
|
||||
// #ifdef ESP32
|
||||
// #include "Extensions/pSprite.cpp"
|
||||
// #endif
|
||||
|
||||
#ifdef SMOOTH_FONT
|
||||
#include "Extensions/Smooth_font.cpp"
|
||||
#endif
|
||||
|
||||
Reference in new issue
Block a user