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