| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | /* | 
 | 2 |  * st7335 LCD panel driver. | 
 | 3 |  * | 
 | 4 |  * Copyright (C) 2015 Marvell | 
 | 5 |  * | 
 | 6 |  * SPDX-License-Identifier:	GPL-2.0+ | 
 | 7 |  */ | 
 | 8 |  | 
 | 9 | #include <common.h> | 
 | 10 | #include <malloc.h> | 
 | 11 | #include <spi.h> | 
 | 12 | #include <asm/gpio.h> | 
 | 13 |  | 
 | 14 | #define ST7735_NOP      (0x0) | 
 | 15 | #define ST7735_SWRESET  (0x01) | 
 | 16 | #define ST7735_SLPIN    (0x10) | 
 | 17 | #define ST7735_SLPOUT   (0x11) | 
 | 18 | #define ST7735_PTLON    (0x12) | 
 | 19 | #define ST7735_NORON    (0x13) | 
 | 20 | #define ST7735_INVOFF   (0x20) | 
 | 21 | #define ST7735_INVON    (0x21) | 
 | 22 | #define ST7735_DISPON   (0x29) | 
 | 23 | #define ST7735_CASET    (0x2A) | 
 | 24 | #define ST7735_RASET    (0x2B) | 
 | 25 | #define ST7735_RAMWR    (0x2C) | 
 | 26 | #define ST7735_COLMOD   (0x3A) | 
 | 27 | #define ST7735_MADCTL   (0x36) | 
 | 28 | #define ST7735_FRMCTR1  (0xB1) | 
 | 29 | #define ST7735_FRMCTR2  (0xB2) | 
 | 30 | #define ST7735_FRMCTR3  (0xB3) | 
 | 31 | #define ST7735_INVCTR   (0xB4) | 
 | 32 | #define ST7735_DISSET5  (0xB6) | 
 | 33 | #define ST7735_PWCTR1   (0xC0) | 
 | 34 | #define ST7735_PWCTR2   (0xC1) | 
 | 35 | #define ST7735_PWCTR3   (0xC2) | 
 | 36 | #define ST7735_PWCTR4   (0xC3) | 
 | 37 | #define ST7735_PWCTR5   (0xC4) | 
 | 38 | #define ST7735_VMCTR1   (0xC5) | 
 | 39 | #define ST7735_PWCTR6   (0xFC) | 
 | 40 | #define ST7735_GMCTRP1  (0xE0) | 
 | 41 | #define ST7735_GMCTRN1  (0xE1) | 
 | 42 |  | 
 | 43 | #define LCD_177_SIZE | 
 | 44 |  | 
 | 45 | uint16_t cmd01[3] = {0x102,0x135,0x136}; | 
 | 46 | uint16_t cmd02[3] = {0x102,0x135,0x136}; | 
 | 47 | uint16_t cmd03[6] = {0x102,0x135,0x136,0x102,0x135,0x136}; | 
 | 48 | uint16_t cmd04[1] = {0x100}; | 
 | 49 | uint16_t cmd05[3] = {0x1a2,0x102,0x184}; | 
 | 50 | uint16_t cmd06[1] = {0x1c5}; | 
 | 51 | uint16_t cmd07[2] = {0x10d,0x100}; | 
 | 52 | uint16_t cmd08[2] = {0x18a,0x12a}; | 
 | 53 | uint16_t cmd09[2] = {0x18d,0x1ee}; | 
 | 54 | uint16_t cmd10[1] = {0x112}; | 
 | 55 |  | 
 | 56 | #if REVERSE_LEFT_90_DEGREE | 
 | 57 | uint16_t cmd11[1] = {0x108}; | 
 | 58 | #elif REVERSE_RIGHT_90_DEGREE | 
 | 59 | uint16_t cmd11[1] = {0x1c8}; | 
 | 60 | #else | 
 | 61 | uint16_t cmd11[1] = {0x168}; | 
 | 62 | #endif | 
 | 63 | uint16_t cmd12[16] = {0x112,0x11c,0x110,0x118,0x133,0x12c,0x125,0x128, | 
 | 64 | 			0x128,0x127,0x12f,0x13c,0x100,0x103,0x103,0x110}; | 
 | 65 | uint16_t cmd13[16] = {0x112,0x11c,0x110,0x118,0x12d,0x128,0x123,0x128, | 
 | 66 | 			0x128,0x126,0x12f,0x13b,0x100,0x103,0x103,0x110}; | 
 | 67 | uint16_t cmd14[1] = {0x105}; | 
 | 68 | uint16_t cmd15[4] = {0x100,0x102,0x100,0x181}; | 
 | 69 | uint16_t cmd16[4] = {0x100,0x103,0x100,0x1a2}; | 
 | 70 |  | 
 | 71 | #define data_size(x)		sizeof(x)/sizeof(uint16_t) | 
 | 72 |  | 
 | 73 | #define LCD_PWR_GPIO		13 | 
 | 74 | #define LCD_RST_GPIO35		35 | 
 | 75 | #define SPI_9BIT_MODE		9 | 
 | 76 |  | 
 | 77 | #ifdef LCD_177_SIZE | 
 | 78 | #define DEFAULT_FB_XRES    160 | 
 | 79 | #else | 
 | 80 | #define DEFAULT_FB_XRES    128 | 
 | 81 | #endif | 
 | 82 |  | 
 | 83 | #define DEFAULT_FB_YRES    128 | 
 | 84 | #define DEFAULT_FB_SIZE    (DEFAULT_FB_XRES * DEFAULT_FB_YRES * 4) | 
 | 85 |  | 
 | 86 | #define DATA_HR		0x100 | 
 | 87 | #define DataH(x)	DATA_HR | (x) | 
 | 88 |  | 
 | 89 | static uint16_t *lcd_buff; | 
 | 90 | struct spi_slave *spi; | 
 | 91 | void lcdFillRGB(uint16_t color); | 
 | 92 | static void st7735fb_draw_logo(void); | 
 | 93 |  | 
 | 94 | const u8 marvell_logo[1][64][4] = | 
 | 95 | { | 
 | 96 | 	{ // marvell | 
 | 97 | 		{0x01, 0x00, 0x00, 0x00}, {0x03, 0x00, 0x00, 0x00}, {0x07, 0x00, 0x00, 0x00}, {0x0f, 0x00, 0x00, 0x00}, | 
 | 98 | 		{0x1f, 0x00, 0x00, 0x00}, {0x3f, 0x00, 0x00, 0x00}, {0x7f, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00}, | 
 | 99 | 		{0xff, 0x01, 0x00, 0x00}, {0xff, 0x03, 0x00, 0x00}, {0xff, 0x07, 0x00, 0x00}, {0xff, 0x0f, 0x00, 0x00}, // 11 | 
 | 100 | 		{0xff, 0x1f, 0x00, 0x00}, {0xff, 0x3f, 0x00, 0x00}, {0xfe, 0x7f, 0x00, 0x00}, {0xfc, 0xff, 0x00, 0x00}, | 
 | 101 | 		{0xf8, 0xff, 0x01, 0x00}, {0xf0, 0xff, 0x03, 0x00}, {0xe0, 0xff, 0x07, 0x00}, {0xc0, 0xff, 0x0f, 0x00}, | 
 | 102 | 		{0x80, 0xff, 0x1f, 0x00}, {0x00, 0xff, 0x3f, 0x00}, {0x00, 0xfe, 0x7f, 0x00}, {0x01, 0xfc, 0xff, 0x00}, | 
 | 103 | 		{0x03, 0xf8, 0xff, 0x01}, {0x77, 0xf7, 0xff, 0x03}, {0x5d, 0x15, 0x00, 0x02}, {0x5d, 0x15, 0x00, 0x02}, | 
 | 104 | 		{0x5d, 0x15, 0x00, 0x02}, {0x5d, 0x15, 0x00, 0x02}, {0xdd, 0x15, 0x00, 0x02}, {0xdd, 0x15, 0x00, 0x02}, | 
 | 105 | 		{0xdd, 0x15, 0x00, 0x02}, {0xdd, 0x15, 0x00, 0x02}, {0xdd, 0x15, 0x00, 0x02}, {0xdd, 0x1d, 0x00, 0x02}, | 
 | 106 | 		{0xdd, 0x1d, 0x00, 0x02}, {0xdd, 0x1d, 0x00, 0x02}, {0xff, 0xff, 0xff, 0x03}, {0xfc, 0xff, 0x00, 0x00}, // 39 | 
 | 107 | 		{0xf8, 0xff, 0x01, 0x00}, {0xf0, 0xff, 0x03, 0x00}, {0xe0, 0xff, 0x07, 0x00}, {0xc0, 0xff, 0x0f, 0x00}, | 
 | 108 | 		{0x80, 0xff, 0x1f, 0x00}, {0x00, 0xff, 0x3f, 0x00}, {0x00, 0xfe, 0x7f, 0x00}, {0x00, 0xfc, 0xff, 0x00}, | 
 | 109 | 		{0x00, 0xf8, 0xff, 0x01}, {0x77, 0xf7, 0xff, 0x03}, {0x55, 0x15, 0x00, 0x02}, {0x55, 0x15, 0x00, 0x02}, | 
 | 110 | 		{0x55, 0x15, 0x00, 0x02}, {0x55, 0x15, 0x00, 0x02}, {0x55, 0x15, 0x00, 0x02}, {0x55, 0x15, 0x00, 0x02}, | 
 | 111 | 		{0x55, 0x15, 0x00, 0x02}, {0x55, 0x15, 0x00, 0x02}, {0x55, 0x15, 0x00, 0x02}, {0x55, 0x15, 0x00, 0x02}, | 
 | 112 | 		{0x55, 0x15, 0x00, 0x02}, {0x55, 0x15, 0x00, 0x02}, {0x55, 0x15, 0x00, 0x02}, {0x77, 0xf7, 0xff, 0x03} | 
 | 113 | 	} | 
 | 114 | }; | 
 | 115 |  | 
 | 116 | void write_command_data(uint16_t cmd, uint16_t *data_addr, uint32_t data_size) | 
 | 117 | { | 
 | 118 | 	int ret; | 
 | 119 |  | 
 | 120 | 	ret = spi_xfer(spi, SPI_9BIT_MODE, &cmd, NULL, | 
 | 121 | 				SPI_XFER_BEGIN | SPI_XFER_END); | 
 | 122 | 	if (ret) | 
 | 123 | 		debug("SPILCD: Failed to send command %d\n", cmd); | 
 | 124 |  | 
 | 125 | 	if (data_addr && data_size > 0) { | 
 | 126 | 		ret = spi_xfer(spi, SPI_9BIT_MODE * data_size, data_addr, NULL, | 
 | 127 | 					SPI_XFER_BEGIN | SPI_XFER_END); | 
 | 128 | 		if (ret) | 
 | 129 | 			debug("SPILCD: Failed to send data.\n"); | 
 | 130 | 	} | 
 | 131 | } | 
 | 132 |  | 
 | 133 | void st7735InitDisplay(void) | 
 | 134 | { | 
 | 135 | 	write_command_data(ST7735_SLPOUT, NULL, 0); | 
 | 136 | 	mdelay(300); | 
 | 137 |  | 
 | 138 | 	write_command_data(ST7735_FRMCTR1, cmd01, data_size(cmd01)); | 
 | 139 | 	write_command_data(ST7735_FRMCTR2, cmd02, data_size(cmd02)); | 
 | 140 | 	write_command_data(ST7735_FRMCTR3, cmd03, data_size(cmd03)); | 
 | 141 | 	write_command_data(ST7735_INVCTR, cmd04, data_size(cmd04)); | 
 | 142 | 	write_command_data(ST7735_PWCTR1, cmd05, data_size(cmd05)); | 
 | 143 | 	write_command_data(ST7735_PWCTR2, cmd06, data_size(cmd06)); | 
 | 144 | 	write_command_data(ST7735_PWCTR3, cmd07, data_size(cmd07)); | 
 | 145 | 	write_command_data(ST7735_PWCTR4, cmd08, data_size(cmd08)); | 
 | 146 | 	write_command_data(ST7735_PWCTR5, cmd09, data_size(cmd09)); | 
 | 147 | 	write_command_data(ST7735_PWCTR6, cmd10, data_size(cmd10)); | 
 | 148 | 	write_command_data(ST7735_MADCTL, cmd11, data_size(cmd11)); | 
 | 149 | 	write_command_data(ST7735_GMCTRP1, cmd12, data_size(cmd12)); | 
 | 150 | 	write_command_data(ST7735_GMCTRN1, cmd13, data_size(cmd13)); | 
 | 151 | 	write_command_data(ST7735_COLMOD, cmd14, data_size(cmd14)); | 
 | 152 | 	write_command_data(ST7735_CASET, cmd15, data_size(cmd15)); | 
 | 153 | 	write_command_data(ST7735_RASET, cmd16, data_size(cmd16)); | 
 | 154 |  | 
 | 155 | 	write_command_data(ST7735_DISPON, NULL, 0); | 
 | 156 | 	write_command_data(ST7735_RAMWR, NULL, 0); | 
 | 157 | } | 
 | 158 |  | 
 | 159 | void lcdBacklight(bool state) | 
 | 160 | { | 
 | 161 | 	gpio_direction_output(LCD_PWR_GPIO, state); | 
 | 162 | } | 
 | 163 |  | 
 | 164 | int ST7735_lcdInit(void) | 
 | 165 | { | 
 | 166 | 	int ret; | 
 | 167 | 	int bus = 0; | 
 | 168 |  | 
 | 169 | 	lcd_buff = malloc(DEFAULT_FB_SIZE); | 
 | 170 |  | 
 | 171 | 	spi_init(); | 
 | 172 | 	spi = spi_setup_slave(bus, 25, 0, SPI_MODE_3); | 
 | 173 | 	if (!spi) { | 
 | 174 | 		printf("SF: Failed to set up slave\n"); | 
 | 175 | 		return -1; | 
 | 176 | 	} | 
 | 177 |  | 
 | 178 | 	ret = spi_claim_bus(spi); | 
 | 179 | 	if (ret) { | 
 | 180 | 		debug("SF: Failed to claim SPI bus: %d\n", ret); | 
 | 181 | 		return -1; | 
 | 182 | 	} | 
 | 183 | 	spi_set_wordlen(spi, 9); | 
 | 184 |  | 
 | 185 | 	gpio_direction_output(LCD_RST_GPIO35, 1); | 
 | 186 | 	mdelay(50); | 
 | 187 | 	gpio_direction_output(LCD_RST_GPIO35, 0); | 
 | 188 | 	mdelay(50); | 
 | 189 | 	gpio_direction_output(LCD_RST_GPIO35, 1); | 
 | 190 | 	mdelay(150); | 
 | 191 |  | 
 | 192 | 	// Run LCD init sequence | 
 | 193 | 	st7735InitDisplay(); | 
 | 194 |  | 
 | 195 | 	//lcdFillRGB(0x0); | 
 | 196 | 	st7735fb_draw_logo(); | 
 | 197 | 	lcdBacklight(1); | 
 | 198 | 	return 0; | 
 | 199 | } | 
 | 200 |  | 
 | 201 | void st7735SetAddrWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) | 
 | 202 | { | 
 | 203 | 	uint16_t buf[4]; | 
 | 204 | 	buf[0] = DataH(0); | 
 | 205 | #ifdef LCD_177_SIZE | 
 | 206 | 	buf[1] = DataH(x0); | 
 | 207 | #else | 
 | 208 | 	buf[1] = DataH(x0+2); | 
 | 209 | #endif | 
 | 210 | 	buf[2] = DataH(0); | 
 | 211 | #ifdef LCD_177_SIZE | 
 | 212 | 	buf[3] = DataH(x1); | 
 | 213 | #else | 
 | 214 | 	buf[3] = DataH(x1+2); | 
 | 215 | #endif | 
 | 216 | 	write_command_data(ST7735_CASET, buf, 4);	// column addr set | 
 | 217 |  | 
 | 218 | 	buf[0] = DataH(0); | 
 | 219 | #ifdef LCD_177_SIZE | 
 | 220 | 	buf[1] = DataH(y0); | 
 | 221 | #else | 
 | 222 | 	buf[1] = DataH(y0+3); | 
 | 223 | #endif | 
 | 224 | 	buf[2] = DataH(0); | 
 | 225 | #ifdef LCD_177_SIZE | 
 | 226 | 	buf[3] = DataH(y1); | 
 | 227 | #else | 
 | 228 | 	buf[3] = DataH(y1+3); | 
 | 229 | #endif | 
 | 230 | 	write_command_data(ST7735_RASET, buf, 4);   // row addr set | 
 | 231 | } | 
 | 232 |  | 
 | 233 | void lcdFillRGB(uint16_t color) | 
 | 234 | { | 
 | 235 | 	uint8_t x, y; | 
 | 236 | 	uint16_t *buff; | 
 | 237 |  | 
 | 238 | 	buff = lcd_buff; | 
 | 239 | 	memset(buff, 0x00, DEFAULT_FB_SIZE); | 
 | 240 |  | 
 | 241 | 	st7735SetAddrWindow(0, 0, DEFAULT_FB_XRES - 1, DEFAULT_FB_YRES - 1); | 
 | 242 |  | 
 | 243 | 	for (x=0; x < DEFAULT_FB_XRES; x++) | 
 | 244 | 	{ | 
 | 245 | 		for (y=0; y < DEFAULT_FB_YRES; y++) | 
 | 246 | 		{ | 
 | 247 | 			*(uint16_t *)(buff++) = DataH(color >> 8); | 
 | 248 | 			*(uint16_t *)(buff++) = DataH(color); | 
 | 249 | 		} | 
 | 250 | 	} | 
 | 251 |  | 
 | 252 | 	flush_cache((unsigned long)lcd_buff, DEFAULT_FB_SIZE); | 
 | 253 | 	write_command_data(ST7735_RAMWR, lcd_buff, DEFAULT_FB_SIZE / 2); | 
 | 254 | } | 
 | 255 |  | 
 | 256 | void lcdDrawPixel(uint16_t x, uint16_t y, uint16_t color) | 
 | 257 | { | 
 | 258 | 	uint16_t buf[2]; | 
 | 259 | 	st7735SetAddrWindow(x, y, x+1, y+1); | 
 | 260 | 	buf[0] = DataH(color >> 8); | 
 | 261 | 	buf[1] = DataH(color); | 
 | 262 | 	write_command_data(ST7735_RAMWR, buf, 2); | 
 | 263 | } | 
 | 264 |  | 
 | 265 | void lcdDrawHLine(uint16_t x0, uint16_t x1, uint16_t y, uint16_t color) | 
 | 266 | { | 
 | 267 | 	// Allows for slightly better performance than setting individual pixels | 
 | 268 | 	uint16_t x, pixels; | 
 | 269 | 	uint16_t *buff = lcd_buff; | 
 | 270 |  | 
 | 271 | 	if (x1 < x0)	// Switch x1 and x0 | 
 | 272 | 	{ | 
 | 273 | 		x = x1; | 
 | 274 | 		x1 = x0; | 
 | 275 | 		x0 = x; | 
 | 276 | 	} | 
 | 277 |  | 
 | 278 | 	// Check limits | 
 | 279 | 	if (x1 >= DEFAULT_FB_XRES) | 
 | 280 | 		x1 = DEFAULT_FB_XRES - 1; | 
 | 281 | 	if (x0 >= DEFAULT_FB_XRES) | 
 | 282 | 		x0 = DEFAULT_FB_XRES - 1; | 
 | 283 |  | 
 | 284 | 	st7735SetAddrWindow(x0, y, DEFAULT_FB_XRES, y + 1); | 
 | 285 | 	for (pixels = 0; pixels < x1 - x0 + 1; pixels++) | 
 | 286 | 	{ | 
 | 287 | 		*(uint16_t *)buff = DataH(color >> 8); | 
 | 288 | 		buff++; | 
 | 289 | 	} | 
 | 290 | 	write_command_data(ST7735_RAMWR, lcd_buff, (x1 - x0)); | 
 | 291 | 	write_command_data(ST7735_NOP, NULL, 0); | 
 | 292 | } | 
 | 293 |  | 
 | 294 | /*************************************************/ | 
 | 295 | void lcdDrawVLine(uint16_t x, uint16_t y0, uint16_t y1, uint16_t color) | 
 | 296 | { | 
 | 297 | 	// Allows for slightly better performance than setting individual pixels | 
 | 298 | 	uint16_t y, pixels; | 
 | 299 |  | 
 | 300 | 	if (y1 < y0) | 
 | 301 | 	{ | 
 | 302 | 		y = y1; | 
 | 303 | 		y1 = y0; | 
 | 304 | 		y0 = y; | 
 | 305 | 	} | 
 | 306 |  | 
 | 307 | 	// Check limits | 
 | 308 | 	if (y1 >= DEFAULT_FB_YRES) | 
 | 309 | 		y1 = DEFAULT_FB_YRES - 1; | 
 | 310 | 	if (y0 >= DEFAULT_FB_YRES) | 
 | 311 | 		y0 = DEFAULT_FB_YRES - 1; | 
 | 312 |  | 
 | 313 | 	st7735SetAddrWindow(x, y0, x, DEFAULT_FB_YRES); | 
 | 314 | 	for (pixels = 0; pixels < y1 - y0 + 1; pixels++) | 
 | 315 | 	{ | 
 | 316 | 		*(uint16_t *)lcd_buff = DataH(color >> 8); | 
 | 317 | 		lcd_buff++; | 
 | 318 | 	} | 
 | 319 | 	write_command_data(ST7735_RAMWR, lcd_buff, (y1 - y0)); | 
 | 320 | } | 
 | 321 |  | 
 | 322 | static void st7735fb_display_picture(u8 p_x, u8 line, u8 W, u8 H, | 
 | 323 | 	u8 *buf,u16 txt_color, u16 bg_color){ | 
 | 324 | 	int i,j,k; | 
 | 325 | 	u8 c; | 
 | 326 | 	int index,size; | 
 | 327 | 	uint16_t *buff = lcd_buff; | 
 | 328 |  | 
 | 329 | 	st7735SetAddrWindow(line, p_x, (line+H-1), (p_x+W-1)); | 
 | 330 |  | 
 | 331 | 	index = 0; | 
 | 332 | 	for(i=0;i<W;i++){ | 
 | 333 | 		for(k=(H/8 -1);k>=0;k--){ | 
 | 334 | 			c = *buf++; | 
 | 335 | 			for(j=(8-1);j>=0;j--){ | 
 | 336 | 				if((1<<(7-j)) & c){ | 
 | 337 | 					*(uint16_t *)(buff++) = DataH((txt_color >> 8)&0xff); | 
 | 338 | 					*(uint16_t *)(buff++) = DataH(txt_color&0xff); | 
 | 339 | 				}else{ | 
 | 340 | 					*(uint16_t *)(buff++) = DataH((bg_color >> 8)&0xff); | 
 | 341 | 					*(uint16_t *)(buff++) = DataH(bg_color&0xff); | 
 | 342 | 				} | 
 | 343 | 				index++; | 
 | 344 | 			} | 
 | 345 | 	   } | 
 | 346 | 	} | 
 | 347 | 	size = index; | 
 | 348 | 	write_command_data(ST7735_RAMWR, lcd_buff, size*8); | 
 | 349 | } | 
 | 350 |  | 
 | 351 | static void st7735fb_draw_logo(void) | 
 | 352 | { | 
 | 353 | 	st7735fb_display_picture(0x20, 0x40, 0x40, 0x20, | 
 | 354 | 			(u8 *)marvell_logo, 0xffff, 0x0000); | 
 | 355 | } | 
 | 356 |  | 
 | 357 | void lcdTest(void) | 
 | 358 | { | 
 | 359 | 	uint8_t i = 0; | 
 | 360 | 	for (i = 0; i < 100; i++) | 
 | 361 | 	{ | 
 | 362 | 		lcdDrawPixel(i, i, 0xFFFF); | 
 | 363 | 	} | 
 | 364 | } |