1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (c) 2013 Google, Inc 4 */ 5 6#include <errno.h> 7#include <unistd.h> 8#include <stdbool.h> 9#include <sysreset.h> 10#include <linux/input.h> 11#include <SDL2/SDL.h> 12#include <asm/state.h> 13 14/** 15 * struct buf_info - a data buffer holding audio data 16 * 17 * @pos: Current position playing in audio buffer 18 * @size: Size of data in audio buffer (0=empty) 19 * @alloced: Allocated size of audio buffer (max size it can hold) 20 * @data: Audio data 21 */ 22struct buf_info { 23 uint pos; 24 uint size; 25 uint alloced; 26 uint8_t *data; 27}; 28 29/** 30 * struct sdl_info - Information about our use of the SDL library 31 * 32 * @width: Width of simulated LCD display 33 * @height: Height of simulated LCD display 34 * @vis_width: Visible width (may be larger to allow for scaling up) 35 * @vis_height: Visible height (may be larger to allow for scaling up) 36 * @depth: Depth of the display in bits per pixel (16 or 32) 37 * @pitch: Number of bytes per line of the display 38 * @sample_rate: Current sample rate for audio 39 * @audio_active: true if audio can be used 40 * @inited: true if this module is initialised 41 * @cur_buf: Current audio buffer being used by sandbox_sdl_fill_audio (0 or 1) 42 * @buf: The two available audio buffers. SDL can be reading from one while we 43 * are setting up the next 44 * @running: true if audio is running 45 * @stopping: true if audio will stop once it runs out of data 46 * @texture: SDL texture to use for U-Boot display contents 47 * @renderer: SDL renderer to use 48 * @screen: SDL window to use 49 * @src_depth: Number of bits per pixel in the source frame buffer (that we read 50 * from and render to SDL) 51 */ 52static struct sdl_info { 53 int width; 54 int height; 55 int vis_width; 56 int vis_height; 57 int depth; 58 int pitch; 59 uint sample_rate; 60 bool audio_active; 61 bool inited; 62 int cur_buf; 63 struct buf_info buf[2]; 64 bool running; 65 bool stopping; 66 SDL_Texture *texture; 67 SDL_Renderer *renderer; 68 SDL_Window *screen; 69 int src_depth; 70} sdl; 71 72static void sandbox_sdl_poll_events(void) 73{ 74 /* 75 * We don't want to include cpu_func.h in this file since it uses 76 * system headers. So add a declation here. 77 */ 78 extern void reset_cpu(void); 79 SDL_Event event; 80 81 while (SDL_PollEvent(&event)) { 82 switch (event.type) { 83 case SDL_QUIT: 84 puts("LCD window closed - quitting\n"); 85 sysreset_walk(SYSRESET_POWER_OFF); 86 break; 87 } 88 } 89} 90 91static int sandbox_sdl_ensure_init(void) 92{ 93 if (!sdl.inited) { 94 if (SDL_Init(0) < 0) { 95 printf("Unable to initialise SDL: %s\n", 96 SDL_GetError()); 97 return -EIO; 98 } 99 100 atexit(SDL_Quit); 101 102 sdl.inited = true; 103 } 104 return 0; 105} 106 107int sandbox_sdl_remove_display(void) 108{ 109 if (!sdl.renderer) { 110 printf("SDL renderer does not exist\n"); 111 return -ENOENT; 112 } 113 114 SDL_DestroyTexture(sdl.texture); 115 SDL_DestroyRenderer(sdl.renderer); 116 SDL_DestroyWindow(sdl.screen); 117 sdl.texture = NULL; 118 sdl.renderer = NULL; 119 sdl.screen = NULL; 120 121 return 0; 122} 123 124int sandbox_sdl_init_display(int width, int height, int log2_bpp, 125 bool double_size) 126{ 127 struct sandbox_state *state = state_get_current(); 128 int err; 129 130 if (!width || !state->show_lcd) 131 return 0; 132 err = sandbox_sdl_ensure_init(); 133 if (err) 134 return err; 135 if (sdl.renderer) 136 sandbox_sdl_remove_display(); 137 138 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { 139 printf("Unable to initialise SDL LCD: %s\n", SDL_GetError()); 140 return -EPERM; 141 } 142 sdl.width = width; 143 sdl.height = height; 144 if (double_size) { 145 sdl.vis_width = sdl.width * 2; 146 sdl.vis_height = sdl.height * 2; 147 } else { 148 sdl.vis_width = sdl.width; 149 sdl.vis_height = sdl.height; 150 } 151 152 if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) 153 printf("Unable to init hinting: %s", SDL_GetError()); 154 155 sdl.src_depth = 1 << log2_bpp; 156 if (log2_bpp != 4 && log2_bpp != 5) 157 log2_bpp = 5; 158 sdl.depth = 1 << log2_bpp; 159 sdl.pitch = sdl.width * sdl.depth / 8; 160 sdl.screen = SDL_CreateWindow("U-Boot", SDL_WINDOWPOS_UNDEFINED, 161 SDL_WINDOWPOS_UNDEFINED, sdl.vis_width, 162 sdl.vis_height, SDL_WINDOW_RESIZABLE); 163 if (!sdl.screen) { 164 printf("Unable to initialise SDL screen: %s\n", 165 SDL_GetError()); 166 return -EIO; 167 } 168 sdl.renderer = SDL_CreateRenderer(sdl.screen, -1, 169 SDL_RENDERER_ACCELERATED | 170 SDL_RENDERER_PRESENTVSYNC); 171 if (!sdl.renderer) { 172 printf("Unable to initialise SDL renderer: %s\n", 173 SDL_GetError()); 174 return -EIO; 175 } 176 177 sdl.texture = SDL_CreateTexture(sdl.renderer, log2_bpp == 4 ? 178 SDL_PIXELFORMAT_RGB565 : 179 SDL_PIXELFORMAT_RGB888, 180 SDL_TEXTUREACCESS_STREAMING, 181 width, height); 182 if (!sdl.texture) { 183 printf("Unable to initialise SDL texture: %s\n", 184 SDL_GetError()); 185 return -EBADF; 186 } 187 sandbox_sdl_poll_events(); 188 189 return 0; 190} 191 192static int copy_to_texture(void *lcd_base) 193{ 194 char *dest; 195 int pitch, x, y; 196 int src_pitch; 197 void *pixels; 198 char *src; 199 int ret; 200 201 if (sdl.src_depth == sdl.depth) { 202 SDL_UpdateTexture(sdl.texture, NULL, lcd_base, sdl.pitch); 203 return 0; 204 } 205 206 /* 207 * We only support copying from an 8bpp to a 32bpp texture since the 208 * other cases are supported directly by the texture. 209 */ 210 if (sdl.depth != 32 && sdl.src_depth != 8) { 211 printf("Need depth 32bpp for copy\n"); 212 return -EINVAL; 213 } 214 215 ret = SDL_LockTexture(sdl.texture, NULL, &pixels, &pitch); 216 if (ret) { 217 printf("SDL lock %d: %s\n", ret, SDL_GetError()); 218 return ret; 219 } 220 221 /* Copy the pixels one by one */ 222 src_pitch = sdl.width * sdl.src_depth / 8; 223 for (y = 0; y < sdl.height; y++) { 224 char val; 225 226 dest = pixels + y * pitch; 227 src = lcd_base + src_pitch * y; 228 for (x = 0; x < sdl.width; x++, dest += 4) { 229 val = *src++; 230 dest[0] = val; 231 dest[1] = val; 232 dest[2] = val; 233 dest[3] = 0; 234 } 235 } 236 SDL_UnlockTexture(sdl.texture); 237 238 return 0; 239} 240 241int sandbox_sdl_sync(void *lcd_base) 242{ 243 struct SDL_Rect rect; 244 int ret; 245 246 if (!sdl.texture) 247 return 0; 248 SDL_RenderClear(sdl.renderer); 249 ret = copy_to_texture(lcd_base); 250 if (ret) { 251 printf("copy_to_texture: %d: %s\n", ret, SDL_GetError()); 252 return -EIO; 253 } 254 ret = SDL_RenderCopy(sdl.renderer, sdl.texture, NULL, NULL); 255 if (ret) { 256 printf("SDL copy %d: %s\n", ret, SDL_GetError()); 257 return -EIO; 258 } 259 260 /* 261 * On some machines this does not appear. Draw an empty rectangle which 262 * seems to fix that. 263 */ 264 rect.x = 0; 265 rect.y = 0; 266 rect.w = 0; 267 rect.h = 0; 268 SDL_RenderDrawRect(sdl.renderer, &rect); 269 270 SDL_RenderPresent(sdl.renderer); 271 sandbox_sdl_poll_events(); 272 273 return 0; 274} 275 276static const unsigned short sdl_to_keycode[SDL_NUM_SCANCODES] = { 277 [SDL_SCANCODE_ESCAPE] = KEY_ESC, 278 [SDL_SCANCODE_1] = KEY_1, 279 [SDL_SCANCODE_2] = KEY_2, 280 [SDL_SCANCODE_3] = KEY_3, 281 [SDL_SCANCODE_4] = KEY_4, 282 [SDL_SCANCODE_5] = KEY_5, 283 [SDL_SCANCODE_6] = KEY_6, 284 [SDL_SCANCODE_7] = KEY_7, 285 [SDL_SCANCODE_8] = KEY_8, 286 [SDL_SCANCODE_9] = KEY_9, 287 [SDL_SCANCODE_0] = KEY_0, 288 [SDL_SCANCODE_MINUS] = KEY_MINUS, 289 [SDL_SCANCODE_EQUALS] = KEY_EQUAL, 290 [SDL_SCANCODE_BACKSPACE] = KEY_BACKSPACE, 291 [SDL_SCANCODE_TAB] = KEY_TAB, 292 [SDL_SCANCODE_Q] = KEY_Q, 293 [SDL_SCANCODE_W] = KEY_W, 294 [SDL_SCANCODE_E] = KEY_E, 295 [SDL_SCANCODE_R] = KEY_R, 296 [SDL_SCANCODE_T] = KEY_T, 297 [SDL_SCANCODE_Y] = KEY_Y, 298 [SDL_SCANCODE_U] = KEY_U, 299 [SDL_SCANCODE_I] = KEY_I, 300 [SDL_SCANCODE_O] = KEY_O, 301 [SDL_SCANCODE_P] = KEY_P, 302 [SDL_SCANCODE_LEFTBRACKET] = KEY_LEFTBRACE, 303 [SDL_SCANCODE_RIGHTBRACKET] = KEY_RIGHTBRACE, 304 [SDL_SCANCODE_RETURN] = KEY_ENTER, 305 [SDL_SCANCODE_LCTRL] = KEY_LEFTCTRL, 306 [SDL_SCANCODE_A] = KEY_A, 307 [SDL_SCANCODE_S] = KEY_S, 308 [SDL_SCANCODE_D] = KEY_D, 309 [SDL_SCANCODE_F] = KEY_F, 310 [SDL_SCANCODE_G] = KEY_G, 311 [SDL_SCANCODE_H] = KEY_H, 312 [SDL_SCANCODE_J] = KEY_J, 313 [SDL_SCANCODE_K] = KEY_K, 314 [SDL_SCANCODE_L] = KEY_L, 315 [SDL_SCANCODE_SEMICOLON] = KEY_SEMICOLON, 316 [SDL_SCANCODE_APOSTROPHE] = KEY_APOSTROPHE, 317 [SDL_SCANCODE_GRAVE] = KEY_GRAVE, 318 [SDL_SCANCODE_LSHIFT] = KEY_LEFTSHIFT, 319 [SDL_SCANCODE_BACKSLASH] = KEY_BACKSLASH, 320 [SDL_SCANCODE_Z] = KEY_Z, 321 [SDL_SCANCODE_X] = KEY_X, 322 [SDL_SCANCODE_C] = KEY_C, 323 [SDL_SCANCODE_V] = KEY_V, 324 [SDL_SCANCODE_B] = KEY_B, 325 [SDL_SCANCODE_N] = KEY_N, 326 [SDL_SCANCODE_M] = KEY_M, 327 [SDL_SCANCODE_COMMA] = KEY_COMMA, 328 [SDL_SCANCODE_PERIOD] = KEY_DOT, 329 [SDL_SCANCODE_SLASH] = KEY_SLASH, 330 [SDL_SCANCODE_RSHIFT] = KEY_RIGHTSHIFT, 331 [SDL_SCANCODE_KP_MULTIPLY] = KEY_KPASTERISK, 332 [SDL_SCANCODE_LALT] = KEY_LEFTALT, 333 [SDL_SCANCODE_SPACE] = KEY_SPACE, 334 [SDL_SCANCODE_CAPSLOCK] = KEY_CAPSLOCK, 335 [SDL_SCANCODE_F1] = KEY_F1, 336 [SDL_SCANCODE_F2] = KEY_F2, 337 [SDL_SCANCODE_F3] = KEY_F3, 338 [SDL_SCANCODE_F4] = KEY_F4, 339 [SDL_SCANCODE_F5] = KEY_F5, 340 [SDL_SCANCODE_F6] = KEY_F6, 341 [SDL_SCANCODE_F7] = KEY_F7, 342 [SDL_SCANCODE_F8] = KEY_F8, 343 [SDL_SCANCODE_F9] = KEY_F9, 344 [SDL_SCANCODE_F10] = KEY_F10, 345 [SDL_SCANCODE_NUMLOCKCLEAR] = KEY_NUMLOCK, 346 [SDL_SCANCODE_SCROLLLOCK] = KEY_SCROLLLOCK, 347 [SDL_SCANCODE_KP_7] = KEY_KP7, 348 [SDL_SCANCODE_KP_8] = KEY_KP8, 349 [SDL_SCANCODE_KP_9] = KEY_KP9, 350 [SDL_SCANCODE_KP_MINUS] = KEY_KPMINUS, 351 [SDL_SCANCODE_KP_4] = KEY_KP4, 352 [SDL_SCANCODE_KP_5] = KEY_KP5, 353 [SDL_SCANCODE_KP_6] = KEY_KP6, 354 [SDL_SCANCODE_KP_PLUS] = KEY_KPPLUS, 355 [SDL_SCANCODE_KP_1] = KEY_KP1, 356 [SDL_SCANCODE_KP_2] = KEY_KP2, 357 [SDL_SCANCODE_KP_3] = KEY_KP3, 358 [SDL_SCANCODE_KP_0] = KEY_KP0, 359 [SDL_SCANCODE_KP_PERIOD] = KEY_KPDOT, 360 /* key 84 does not exist linux_input.h */ 361 [SDL_SCANCODE_LANG5] = KEY_ZENKAKUHANKAKU, 362 [SDL_SCANCODE_NONUSBACKSLASH] = KEY_102ND, 363 [SDL_SCANCODE_F11] = KEY_F11, 364 [SDL_SCANCODE_F12] = KEY_F12, 365 [SDL_SCANCODE_INTERNATIONAL1] = KEY_RO, 366 [SDL_SCANCODE_LANG3] = KEY_KATAKANA, 367 [SDL_SCANCODE_LANG4] = KEY_HIRAGANA, 368 [SDL_SCANCODE_INTERNATIONAL4] = KEY_HENKAN, 369 [SDL_SCANCODE_INTERNATIONAL2] = KEY_KATAKANAHIRAGANA, 370 [SDL_SCANCODE_INTERNATIONAL5] = KEY_MUHENKAN, 371 /* [SDL_SCANCODE_INTERNATIONAL5] -> [KEY_KPJPCOMMA] */ 372 [SDL_SCANCODE_KP_ENTER] = KEY_KPENTER, 373 [SDL_SCANCODE_RCTRL] = KEY_RIGHTCTRL, 374 [SDL_SCANCODE_KP_DIVIDE] = KEY_KPSLASH, 375 [SDL_SCANCODE_SYSREQ] = KEY_SYSRQ, 376 [SDL_SCANCODE_RALT] = KEY_RIGHTALT, 377 /* KEY_LINEFEED */ 378 [SDL_SCANCODE_HOME] = KEY_HOME, 379 [SDL_SCANCODE_UP] = KEY_UP, 380 [SDL_SCANCODE_PAGEUP] = KEY_PAGEUP, 381 [SDL_SCANCODE_LEFT] = KEY_LEFT, 382 [SDL_SCANCODE_RIGHT] = KEY_RIGHT, 383 [SDL_SCANCODE_END] = KEY_END, 384 [SDL_SCANCODE_DOWN] = KEY_DOWN, 385 [SDL_SCANCODE_PAGEDOWN] = KEY_PAGEDOWN, 386 [SDL_SCANCODE_INSERT] = KEY_INSERT, 387 [SDL_SCANCODE_DELETE] = KEY_DELETE, 388 /* KEY_MACRO */ 389 [SDL_SCANCODE_MUTE] = KEY_MUTE, 390 [SDL_SCANCODE_VOLUMEDOWN] = KEY_VOLUMEDOWN, 391 [SDL_SCANCODE_VOLUMEUP] = KEY_VOLUMEUP, 392 [SDL_SCANCODE_POWER] = KEY_POWER, 393 [SDL_SCANCODE_KP_EQUALS] = KEY_KPEQUAL, 394 [SDL_SCANCODE_KP_PLUSMINUS] = KEY_KPPLUSMINUS, 395 [SDL_SCANCODE_PAUSE] = KEY_PAUSE, 396 /* KEY_SCALE */ 397 [SDL_SCANCODE_KP_COMMA] = KEY_KPCOMMA, 398 [SDL_SCANCODE_LANG1] = KEY_HANGUEL, 399 [SDL_SCANCODE_LANG2] = KEY_HANJA, 400 [SDL_SCANCODE_INTERNATIONAL3] = KEY_YEN, 401 [SDL_SCANCODE_LGUI] = KEY_LEFTMETA, 402 [SDL_SCANCODE_RGUI] = KEY_RIGHTMETA, 403 [SDL_SCANCODE_APPLICATION] = KEY_COMPOSE, 404}; 405 406int sandbox_sdl_scan_keys(int key[], int max_keys) 407{ 408 const Uint8 *keystate; 409 int num_keys; 410 int i, count; 411 412 sandbox_sdl_poll_events(); 413 keystate = SDL_GetKeyboardState(&num_keys); 414 for (i = count = 0; i < num_keys; i++) { 415 if (count < max_keys && keystate[i]) { 416 int keycode = sdl_to_keycode[i]; 417 418 if (keycode) 419 key[count++] = keycode; 420 } 421 } 422 423 return count; 424} 425 426int sandbox_sdl_key_pressed(int keycode) 427{ 428 int key[8]; /* allow up to 8 keys to be pressed at once */ 429 int count; 430 int i; 431 432 count = sandbox_sdl_scan_keys(key, sizeof(key) / sizeof(key[0])); 433 for (i = 0; i < count; i++) { 434 if (key[i] == keycode) 435 return 0; 436 } 437 438 return -ENOENT; 439} 440 441void sandbox_sdl_fill_audio(void *udata, Uint8 *stream, int len) 442{ 443 struct buf_info *buf; 444 int avail; 445 int i; 446 447 for (i = 0; i < 2; i++) { 448 buf = &sdl.buf[sdl.cur_buf]; 449 avail = buf->size - buf->pos; 450 if (avail <= 0) { 451 sdl.cur_buf = 1 - sdl.cur_buf; 452 continue; 453 } 454 if (avail > len) 455 avail = len; 456 457 memcpy(stream, buf->data + buf->pos, avail); 458 stream += avail; 459 buf->pos += avail; 460 len -= avail; 461 462 /* Move to next buffer if we are at the end */ 463 if (buf->pos == buf->size) 464 buf->size = 0; 465 else 466 break; 467 } 468 memset(stream, 0, len); 469 sdl.stopping = !!len; 470} 471 472int sandbox_sdl_sound_init(int rate, int channels) 473{ 474 SDL_AudioSpec wanted, have; 475 int i; 476 477 if (sandbox_sdl_ensure_init()) 478 return -1; 479 480 if (sdl.audio_active) 481 return 0; 482 483 /* Set the audio format */ 484 wanted.freq = rate; 485 wanted.format = AUDIO_S16; 486 wanted.channels = channels; 487 wanted.samples = 960; /* Good low-latency value for callback */ 488 wanted.callback = sandbox_sdl_fill_audio; 489 wanted.userdata = NULL; 490 491 for (i = 0; i < 2; i++) { 492 struct buf_info *buf = &sdl.buf[i]; 493 494 buf->alloced = sizeof(uint16_t) * wanted.freq * wanted.channels; 495 buf->data = malloc(buf->alloced); 496 if (!buf->data) { 497 printf("%s: Out of memory\n", __func__); 498 if (i == 1) 499 free(sdl.buf[0].data); 500 return -1; 501 } 502 buf->pos = 0; 503 buf->size = 0; 504 } 505 506 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { 507 printf("Unable to initialise SDL audio: %s\n", SDL_GetError()); 508 goto err; 509 } 510 511 /* Open the audio device, forcing the desired format */ 512 if (SDL_OpenAudio(&wanted, &have) < 0) { 513 printf("Couldn't open audio: %s\n", SDL_GetError()); 514 goto err; 515 } 516 if (have.format != wanted.format) { 517 printf("Couldn't select required audio format\n"); 518 goto err; 519 } 520 sdl.audio_active = true; 521 sdl.sample_rate = wanted.freq; 522 sdl.cur_buf = 0; 523 sdl.running = false; 524 525 return 0; 526 527err: 528 for (i = 0; i < 2; i++) 529 free(sdl.buf[i].data); 530 return -1; 531} 532 533int sandbox_sdl_sound_play(const void *data, uint size) 534{ 535 struct buf_info *buf; 536 537 if (!sdl.audio_active) 538 return 0; 539 540 buf = &sdl.buf[0]; 541 if (buf->size) 542 buf = &sdl.buf[1]; 543 while (buf->size) 544 usleep(1000); 545 546 if (size > buf->alloced) 547 return -E2BIG; 548 549 memcpy(buf->data, data, size); 550 buf->size = size; 551 buf->pos = 0; 552 if (!sdl.running) { 553 SDL_PauseAudio(0); 554 sdl.running = true; 555 sdl.stopping = false; 556 } 557 558 return 0; 559} 560 561int sandbox_sdl_sound_stop(void) 562{ 563 if (sdl.running) { 564 while (!sdl.stopping) 565 SDL_Delay(100); 566 567 SDL_PauseAudio(1); 568 sdl.running = 0; 569 sdl.stopping = false; 570 } 571 572 return 0; 573} 574