Skip to content

Commit 32c91e7

Browse files
committed
ULP and deep sleep support for ESP32S3 and better st7789v emulation.
1 parent 95604aa commit 32c91e7

28 files changed

Lines changed: 886 additions & 253 deletions

hw/display/rgb_led.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ static Property rgb_led_properties[] = {
176176
static void rgbled_class_init(ObjectClass *klass, void *data) {
177177
DeviceClass *dc = DEVICE_CLASS(klass);
178178
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
179-
dc->legacy_reset = rgbled_reset;
179+
device_class_set_legacy_reset(dc,rgbled_reset);
180180
k->realize = rgbled_realize;
181181
k->transfer = rgbled_transfer;
182182
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);

hw/display/st7789v.c

Lines changed: 100 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "sysemu/runstate.h"
2424
#include "qemu/timer.h"
2525
#include "hw/qdev-properties.h"
26+
#include <pixman.h>
2627

2728
#define PANEL_WIDTH 240
2829
#define PANEL_HEIGHT 135
@@ -46,11 +47,12 @@ typedef struct ConsoleState {
4647
int cmd_mode;
4748
int64_t lasttime;
4849
int lastlevel;
49-
//int64_t offtime;
50-
uint32_t *data; // surface data
50+
uint16_t *fb_data;
51+
uint16_t *data; // surface data
5152
int64_t time_off;
5253
int64_t time_on;
5354
QEMUTimer backlight_timer;
55+
5456
} ConsoleState;
5557

5658
// only one console
@@ -68,20 +70,27 @@ struct St7789vState {
6870
#define TYPE_ST7789V "st7789v"
6971
OBJECT_DECLARE_SIMPLE_TYPE(St7789vState, ST7789V)
7072

71-
#define PORTRAIT_X_OFFSET 52
72-
#define PORTRAIT_Y_OFFSET 40
73-
#define LANDSCAPE_X_OFFSET 40
74-
#define LANDSCAPE_Y_OFFSET 53
73+
#define PORTRAIT_X_OFFSET (52)
74+
#define PORTRAIT_Y_OFFSET (40)
75+
#define LANDSCAPE_X_OFFSET (40)
76+
#define LANDSCAPE_Y_OFFSET (53)
77+
78+
#define PORTRAIT_X_OFFSET_S3 (35)
79+
#define PORTRAIT_Y_OFFSET_S3 (0)
80+
#define LANDSCAPE_X_OFFSET_S3 (0)
81+
#define LANDSCAPE_Y_OFFSET_S3 (35)
7582

7683
#define SKIN_PORTRAIT_X_OFFSET (62/2)
7784
#define SKIN_PORTRAIT_Y_OFFSET (126/2)
7885
#define SKIN_LANDSCAPE_X_OFFSET (126/2)
7986
#define SKIN_LANDSCAPE_Y_OFFSET (82/2)
8087

81-
#define SKIN_PORTRAIT_X_OFFSET_S3 (82/2)
82-
#define SKIN_PORTRAIT_Y_OFFSET_S3 (126/2+48)
83-
#define SKIN_LANDSCAPE_X_OFFSET_S3 (126/2+48)
84-
#define SKIN_LANDSCAPE_Y_OFFSET_S3 (82/2-8)
88+
#define SKIN_PORTRAIT_X_OFFSET_S3 (38/2)
89+
#define SKIN_PORTRAIT_Y_OFFSET_S3 (126/2)
90+
#define SKIN_LANDSCAPE_X_OFFSET_S3 (126/2)
91+
#define SKIN_LANDSCAPE_Y_OFFSET_S3 (35/2)
92+
93+
#define DEBUG 0
8594

8695
typedef struct { uint8_t r; uint8_t g; uint8_t b; uint8_t a;} pixel;
8796

@@ -99,19 +108,34 @@ extern image_header ttgos3_board_skin;
99108
image_header *board_skin=&ttgos3_board_skin;
100109

101110

111+
static uint16_t argbto565(uint32_t argb)
112+
{
113+
uint8_t r = (argb >> 16) & 0xFF;
114+
uint8_t g = (argb >> 8) & 0xFF;
115+
uint8_t b = (argb ) & 0xFF;
116+
117+
return (uint16_t)(
118+
((r >> 3) << 11) | // 5 bits red
119+
((g >> 2) << 5) | // 6 bits green
120+
( b >> 3) // 5 bits blue
121+
);
122+
}
123+
102124
static void draw_skin(ConsoleState *c) {
103-
volatile uint32_t *dest = c->data;
125+
volatile uint16_t *dest = c->data;
104126
for (int i = 0; i < board_skin->height; i++)
105127
for (int j = 0; j < board_skin->width; j++) {
106128
pixel p = board_skin->pixel_data[i * board_skin->width + j];
107129
uint32_t rgba= (p.a<<24) | (p.r<<16) | (p.g<<8) | p.b;
108130
if (p.a < 200)
109131
rgba=0xff000000;
132+
uint16_t rgb565=argbto565(rgba);
110133
if (c->width < c->height) // portrait
111-
dest[i * board_skin->width + j] = rgba;
134+
dest[i * board_skin->width + j] = rgb565;
112135
else
113-
dest[(board_skin->width - j - 1) * board_skin->height +i] = rgba;
136+
dest[(board_skin->width - j - 1) * board_skin->height +i] = rgb565;
114137
}
138+
dpy_gfx_update_full(c->con);
115139
}
116140
static void bl_timer_cb(void *v) {
117141
uint64_t now=qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
@@ -121,7 +145,9 @@ static void bl_timer_cb(void *v) {
121145
} else {
122146
c->backlight=(c->time_on*256)/(c->time_on+c->time_off);
123147
}
124-
// printf("bl:%ld %ld %d\n",c->time_on,c->time_off, c->backlight);
148+
c->redraw=1;
149+
if(DEBUG)
150+
printf("bl:%d %d %d\n",(int)c->time_on,(int)c->time_off, c->backlight);
125151
c->time_on=0;
126152
c->time_off=0;
127153
timer_mod_ns(&console_state.backlight_timer, now + 100000000);
@@ -134,6 +160,11 @@ static void set_portrait(St7789vState *s) {
134160
board_skin=&ttgo_board_skin;
135161
qemu_console_resize(c->con, board_skin->width,
136162
board_skin->height);
163+
DisplaySurface *surface=qemu_create_displaysurface_from(board_skin->width, board_skin->height,
164+
PIXMAN_r5g6b5,
165+
board_skin->width*2, NULL);
166+
dpy_gfx_replace_surface(c->con,surface);
167+
137168
c->data=surface_data(qemu_console_surface(c->con));
138169
c->width = PANEL_HEIGHT;
139170
c->height = PANEL_WIDTH;
@@ -142,6 +173,10 @@ static void set_portrait(St7789vState *s) {
142173
c->skin_x_offset = SKIN_PORTRAIT_X_OFFSET;
143174
c->skin_y_offset = SKIN_PORTRAIT_Y_OFFSET;
144175
if(s->iss3) {
176+
c->width = 170;
177+
c->height = 320;
178+
c->x_offset = PORTRAIT_X_OFFSET_S3;
179+
c->y_offset = PORTRAIT_Y_OFFSET_S3;
145180
c->skin_x_offset = SKIN_PORTRAIT_X_OFFSET_S3;
146181
c->skin_y_offset = SKIN_PORTRAIT_Y_OFFSET_S3;
147182
}
@@ -157,6 +192,10 @@ static void set_landscape(St7789vState *s) {
157192
board_skin=&ttgo_board_skin;
158193
qemu_console_resize(c->con, board_skin->height,
159194
board_skin->width);
195+
DisplaySurface *surface=qemu_create_displaysurface_from(board_skin->height, board_skin->width,
196+
PIXMAN_r5g6b5,
197+
board_skin->height*2, NULL);
198+
dpy_gfx_replace_surface(c->con,surface);
160199
c->data=surface_data(qemu_console_surface(c->con));
161200
c->width = PANEL_WIDTH;
162201
c->height = PANEL_HEIGHT;
@@ -165,6 +204,10 @@ static void set_landscape(St7789vState *s) {
165204
c->skin_x_offset = SKIN_LANDSCAPE_X_OFFSET;
166205
c->skin_y_offset = SKIN_LANDSCAPE_Y_OFFSET;
167206
if(s->iss3) {
207+
c->width = 320;
208+
c->height = 170;
209+
c->x_offset = LANDSCAPE_X_OFFSET_S3;
210+
c->y_offset = LANDSCAPE_Y_OFFSET_S3;
168211
c->skin_x_offset = SKIN_LANDSCAPE_X_OFFSET_S3;
169212
c->skin_y_offset = SKIN_LANDSCAPE_Y_OFFSET_S3;
170213
}
@@ -178,11 +221,13 @@ static uint32_t st7789v_transfer(SSIPeripheral *dev, uint32_t data)
178221
{
179222
ConsoleState *c=&console_state;
180223
St7789vState *s = ST7789V(dev);
181-
// printf(" st7789 %x %x\n", c->current_command, data);
224+
182225
uint8_t *bytes;
183226
if(c->cmd_mode) {
184227
c->current_command=data;
185228
} else {
229+
if(DEBUG && c->current_command!=0x2c)
230+
printf(" st7789 %x %x\n", c->current_command, data);
186231
switch (c->current_command) {
187232
case ST7789_MADCTL:
188233
if (data == 0 || data == 8) { // portrait
@@ -221,30 +266,9 @@ static uint32_t st7789v_transfer(SSIPeripheral *dev, uint32_t data)
221266
if(!c->little_endian) {
222267
d16=(d16>>8) | (d16<<8);
223268
}
224-
int brightness=c->backlight;
225-
int d32;
226-
if(brightness==256) {
227-
d32=((d16 & 0xf800) << 8) |
228-
((d16 & 0x7e0) << 5) |
229-
((d16 & 0x1f) << 3) | (0x80<<24);
230-
} else {
231-
if(brightness==0) d32=0;
232-
else {
233-
int r=(d16 & 0xf800)>>8;
234-
int g=(d16 & 0x7e0)>>3;
235-
int b=(d16 & 0x1f)<<3;
236-
r=(r*brightness)>>8;
237-
g=(g*brightness)>>8;
238-
b=(b*brightness)>>8;
239-
d32=r<<16 | g<<8 | b;
240-
}
241-
}
242-
// if(!c->backlight)
243-
// d32=(d32>>2)&0x3f3f3f;
244-
uint32_t offset = (c->y - c->y_offset + c->skin_y_offset) *
245-
c->skin_width + c->x - c->x_offset + c->skin_x_offset;
246-
if(offset<board_skin->height*board_skin->width)
247-
c->data[offset] = d32;
269+
uint32_t offset = c->y * 320 + c->x;
270+
if(offset<320*320)
271+
c->fb_data[offset] = d16;
248272
c->x++;
249273
if (c->x > c->x_end) {
250274
c->x = c->x_start;
@@ -267,22 +291,24 @@ static uint32_t st7789v_transfer(SSIPeripheral *dev, uint32_t data)
267291
/* Command/data input. */
268292
static void st7789v_cd(void *opaque, int n, int level)
269293
{
294+
if(DEBUG)
295+
printf("st7789v_cd %d\n",level);
270296
ConsoleState *c=&console_state;
271297
c->cmd_mode = !level;
272298
}
273299

274300
static void st7789v_backlight(void *opaque, int n, int level)
275301
{
276302
int64_t now=qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
303+
if(DEBUG)
304+
printf("st7789v_backlight %d\n",level);
277305
ConsoleState *c=&console_state;
278-
// if(level==c->lastlevel) return;
279306
int v=level&1;
280307
int t=c->lastlevel>>1;
281308
c->lastlevel=level;
282309
if(t!=0) {
283310
now=c->lasttime+t;
284311
}
285-
// printf("bl=%ld %d\n",now, level);
286312
if(now-c->lasttime<100000000) {
287313
if(v==0) {
288314
c->time_on+=now-c->lasttime;
@@ -291,12 +317,7 @@ static void st7789v_backlight(void *opaque, int n, int level)
291317
}
292318
} else {
293319
c->backlight=256*v;
294-
volatile unsigned *dest = c->data;
295-
uint32_t px=v?(64<<16)|(64<<8)|(64):0;
296-
for(int y=0;y<c->height;y++)
297-
for(int x=0;x<c->width;x++)
298-
dest[(y+c->skin_y_offset)*c->skin_width+x+c->skin_x_offset]=px^(rand()&0x0f0f0f);
299-
dpy_gfx_update(c->con, c->skin_x_offset, c->skin_y_offset, c->width, c->height);
320+
c->redraw=1;
300321
}
301322
c->lasttime=now;
302323
}
@@ -305,8 +326,25 @@ static void st7789_update_display(void *opaque) {
305326
ConsoleState *c = &console_state;
306327
if (!c->redraw) return;
307328
c->redraw = 0;
308-
dpy_gfx_update_full(c->con);
309-
// dpy_gfx_update(c->con, c->skin_x_offset, c->skin_y_offset, c->width, c->height);
329+
330+
for(int y=0;y<c->height;y++) {
331+
for(int x=0;x<c->width;x++) {
332+
uint16_t rgb565=c->fb_data[(y+c->y_offset)*320+x+c->x_offset];
333+
if(c->backlight<255) {
334+
uint32_t r=(rgb565>>8) & 0xf8;
335+
uint32_t g=(rgb565>>3) & 0xfc;
336+
uint32_t b=(rgb565<<2) & 0xf8;
337+
r=(r*c->backlight)/256;
338+
g=(g*c->backlight)/256;
339+
b=(b*c->backlight)/256;
340+
rgb565=(uint16_t)(((r >> 3) << 11) | ((g >> 2) << 5) | ( b >> 3));
341+
}
342+
uint32_t index=(y+c->skin_y_offset)*c->skin_width+x+c->skin_x_offset;
343+
if(index<320*320)
344+
c->data[index]=rgb565;
345+
}
346+
}
347+
dpy_gfx_update(c->con, c->skin_x_offset, c->skin_y_offset, c->width, c->height);
310348
}
311349

312350
static void st7789_invalidate_display(void *opaque) {
@@ -319,7 +357,6 @@ static const GraphicHwOps st7789_ops = {
319357
.gfx_update = st7789_update_display,
320358
};
321359

322-
//extern int touch_sensor[14];
323360
#define PW 1200
324361
static void keyboard_event(DeviceState *dev, QemuConsole *src,
325362
InputEvent *evt) {
@@ -373,7 +410,6 @@ static void keyboard_event(DeviceState *dev, QemuConsole *src,
373410
qemu_set_irq(s->touch_sensor[i],0);
374411
break;
375412
}
376-
// printf("xpos=%d ypos=%d\n",xpos,ypos);
377413
if (portrait) {
378414
if(s->iss3) {
379415
if (xpos > 24575 && xpos < 30561 && ypos > 30063 &&
@@ -435,7 +471,6 @@ static void keyboard_event(DeviceState *dev, QemuConsole *src,
435471
ypos < 29931) {
436472
qemu_set_irq(s->button[0], up);
437473
}
438-
439474
if (xpos > 25382 && xpos < 30561 && ypos > 30063 &&
440475
ypos < 31382 && up == 0)
441476
qemu_set_irq(s->reset, 1);
@@ -452,20 +487,15 @@ static void keyboard_event(DeviceState *dev, QemuConsole *src,
452487
up == 0)
453488
qemu_set_irq(s->reset, 1);
454489
}
455-
/*
456-
int xs[] = {0, 0, 12166, 13618, 15277,
457-
16798, 0, 18388, 12166, 13791};
458-
int ys[] = {0, 0, 31743, 31743, 31743,
459-
31743, 0, 2993, 2993, 2993};
460-
for (int i = 2; i < 10; i++) {
461-
if (i != 6) {
462-
if (xpos > (xs[i] - PW) && xpos < (xs[i] + PW) &&
463-
ypos > (ys[i] - PW) && ypos < (ys[i] + PW))
464-
// touch_sensor[i] = 1000;
465-
466-
}
490+
491+
int xs[] = {12166, 13618, 12166, 13791};
492+
int ys[] = {31743, 31743, 2993, 2993};
493+
for (int i = 0; i < 4; i++) {
494+
if (xpos > (xs[i] - PW) && xpos < (xs[i] + PW) &&
495+
ypos > (ys[i] - PW) && ypos < (ys[i] + PW))
496+
qemu_set_irq(s->touch_sensor[i], 1000);
467497
}
468-
*/
498+
469499
}
470500
break;
471501
default:
@@ -500,10 +530,13 @@ static void st7789v_realize(SSIPeripheral *d, Error **errp) {
500530
if (console_state.con == 0) {
501531
ConsoleState *c=&console_state;
502532
s->con=c;
503-
console_state.con = graphic_console_init(dev, 0, &st7789_ops, s);
533+
c->con = graphic_console_init(dev, 0, &st7789_ops, s);
534+
504535
int64_t now=qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
505536
c->lastlevel=0;
506537
c->lasttime=now;
538+
c->cmd_mode=1;
539+
c->fb_data=malloc(320*320*2);
507540
timer_init_ns(&c->backlight_timer,QEMU_CLOCK_VIRTUAL, bl_timer_cb,0);
508541
timer_mod_ns(&c->backlight_timer, now + 100000000);
509542
set_landscape(s);
@@ -522,7 +555,7 @@ static void st7789v_reset(DeviceState *dev) {
522555
static void st7789v_class_init(ObjectClass *klass, void *data) {
523556
DeviceClass *dc = DEVICE_CLASS(klass);
524557
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
525-
dc->legacy_reset = st7789v_reset;
558+
device_class_set_legacy_reset(dc,st7789v_reset);
526559
k->realize = st7789v_realize;
527560
k->transfer = st7789v_transfer;
528561
k->cs_polarity = SSI_CS_NONE;

0 commit comments

Comments
 (0)