dedf00ed3581110fc9e7c5dc5c0277f40f3029cc
[oweals/u-boot.git] / arch / sandbox / cpu / sdl.c
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 <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  */
21 struct 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  * @screen: Surface used to draw on the screen
32  * @width: Width of simulated LCD display
33  * @height: Height of simulated LCD display
34  * @depth: Depth of the display in bits per pixel (16 or 32)
35  * @pitch: Number of bytes per line of the display
36  * @sample_rate: Current sample rate for audio
37  * @audio_active: true if audio can be used
38  * @inited: true if this module is initialised
39  * @cur_buf: Current audio buffer being used by sandbox_sdl_fill_audio (0 or 1)
40  * @buf: The two available audio buffers. SDL can be reading from one while we
41  *      are setting up the next
42  * @running: true if audio is running
43  * @stopping: true if audio will stop once it runs out of data
44  */
45 static struct sdl_info {
46         SDL_Surface *screen;
47         int width;
48         int height;
49         int depth;
50         int pitch;
51         uint sample_rate;
52         bool audio_active;
53         bool inited;
54         int cur_buf;
55         struct buf_info buf[2];
56         bool running;
57         bool stopping;
58 } sdl;
59
60 static void sandbox_sdl_poll_events(void)
61 {
62         /*
63          * We don't want to include common.h in this file since it uses
64          * system headers. So add a declation here.
65          */
66         extern void reset_cpu(unsigned long addr);
67         SDL_Event event;
68
69         while (SDL_PollEvent(&event)) {
70                 switch (event.type) {
71                 case SDL_QUIT:
72                         puts("LCD window closed - quitting\n");
73                         reset_cpu(1);
74                         break;
75                 }
76         }
77 }
78
79 static int sandbox_sdl_ensure_init(void)
80 {
81         if (!sdl.inited) {
82                 if (SDL_Init(0) < 0) {
83                         printf("Unable to initialise SDL: %s\n",
84                                SDL_GetError());
85                         return -EIO;
86                 }
87
88                 atexit(SDL_Quit);
89
90                 sdl.inited = true;
91         }
92         return 0;
93 }
94
95 int sandbox_sdl_init_display(int width, int height, int log2_bpp)
96 {
97         struct sandbox_state *state = state_get_current();
98         int err;
99
100         if (!width || !state->show_lcd)
101                 return 0;
102         err = sandbox_sdl_ensure_init();
103         if (err)
104                 return err;
105         if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
106                 printf("Unable to initialise SDL LCD: %s\n", SDL_GetError());
107                 return -EPERM;
108         }
109         SDL_WM_SetCaption("U-Boot", "U-Boot");
110
111         sdl.width = width;
112         sdl.height = height;
113         sdl.depth = 1 << log2_bpp;
114         sdl.pitch = sdl.width * sdl.depth / 8;
115         sdl.screen = SDL_SetVideoMode(width, height, 0, 0);
116         sandbox_sdl_poll_events();
117
118         return 0;
119 }
120
121 int sandbox_sdl_sync(void *lcd_base)
122 {
123         SDL_Surface *frame;
124
125         frame = SDL_CreateRGBSurfaceFrom(lcd_base, sdl.width, sdl.height,
126                         sdl.depth, sdl.pitch,
127                         0x1f << 11, 0x3f << 5, 0x1f << 0, 0);
128         SDL_BlitSurface(frame, NULL, sdl.screen, NULL);
129         SDL_FreeSurface(frame);
130         SDL_UpdateRect(sdl.screen, 0, 0, 0, 0);
131         sandbox_sdl_poll_events();
132
133         return 0;
134 }
135
136 #define NONE (-1)
137 #define NUM_SDL_CODES   (SDLK_UNDO + 1)
138
139 static int16_t sdl_to_keycode[NUM_SDL_CODES] = {
140         /* 0 */
141         NONE, NONE, NONE, NONE, NONE,
142         NONE, NONE, NONE, KEY_BACKSPACE, KEY_TAB,
143         NONE, NONE, NONE, KEY_ENTER, NONE,
144         NONE, NONE, NONE, NONE, KEY_POWER,      /* use PAUSE as POWER */
145
146         /* 20 */
147         NONE, NONE, NONE, NONE, NONE,
148         NONE, NONE, KEY_ESC, NONE, NONE,
149         NONE, NONE, KEY_SPACE, NONE, NONE,
150         NONE, NONE, NONE, NONE, NONE,
151
152         /* 40 */
153         NONE, NONE, NONE, NONE, KEY_COMMA,
154         KEY_MINUS, KEY_DOT, KEY_SLASH, KEY_0, KEY_1,
155         KEY_2, KEY_3, KEY_4, KEY_5, KEY_6,
156         KEY_7, KEY_8, KEY_9, NONE, KEY_SEMICOLON,
157
158         /* 60 */
159         NONE, KEY_EQUAL, NONE, NONE, NONE,
160         NONE, NONE, NONE, NONE, NONE,
161         NONE, NONE, NONE, NONE, NONE,
162         NONE, NONE, NONE, NONE, NONE,
163
164         /* 80 */
165         NONE, NONE, NONE, NONE, NONE,
166         NONE, NONE, NONE, NONE, NONE,
167         NONE, NONE, KEY_BACKSLASH, NONE, NONE,
168         NONE, KEY_GRAVE, KEY_A, KEY_B, KEY_C,
169
170         /* 100 */
171         KEY_D, KEY_E, KEY_F, KEY_G, KEY_H,
172         KEY_I, KEY_J, KEY_K, KEY_L, KEY_M,
173         KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R,
174         KEY_S, KEY_T, KEY_U, KEY_V, KEY_W,
175
176         /* 120 */
177         KEY_X, KEY_Y, KEY_Z, NONE, NONE,
178         NONE, NONE, KEY_DELETE, NONE, NONE,
179         NONE, NONE, NONE, NONE, NONE,
180         NONE, NONE, NONE, NONE, NONE,
181
182         /* 140 */
183         NONE, NONE, NONE, NONE, NONE,
184         NONE, NONE, NONE, NONE, NONE,
185         NONE, NONE, NONE, NONE, NONE,
186         NONE, NONE, NONE, NONE, NONE,
187
188         /* 160 */
189         NONE, NONE, NONE, NONE, NONE,
190         NONE, NONE, NONE, NONE, NONE,
191         NONE, NONE, NONE, NONE, NONE,
192         NONE, NONE, NONE, NONE, NONE,
193
194         /* 180 */
195         NONE, NONE, NONE, NONE, NONE,
196         NONE, NONE, NONE, NONE, NONE,
197         NONE, NONE, NONE, NONE, NONE,
198         NONE, NONE, NONE, NONE, NONE,
199
200         /* 200 */
201         NONE, NONE, NONE, NONE, NONE,
202         NONE, NONE, NONE, NONE, NONE,
203         NONE, NONE, NONE, NONE, NONE,
204         NONE, NONE, NONE, NONE, NONE,
205
206         /* 220 */
207         NONE, NONE, NONE, NONE, NONE,
208         NONE, NONE, NONE, NONE, NONE,
209         NONE, NONE, NONE, NONE, NONE,
210         NONE, NONE, NONE, NONE, NONE,
211
212         /* 240 */
213         NONE, NONE, NONE, NONE, NONE,
214         NONE, NONE, NONE, NONE, NONE,
215         NONE, NONE, NONE, NONE, NONE,
216         NONE, KEY_KP0, KEY_KP1, KEY_KP2, KEY_KP3,
217
218         /* 260 */
219         KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7, KEY_KP8,
220         KEY_KP9, KEY_KPDOT, KEY_KPSLASH, KEY_KPASTERISK, KEY_KPMINUS,
221         KEY_KPPLUS, KEY_KPENTER, KEY_KPEQUAL, KEY_UP, KEY_DOWN,
222         KEY_RIGHT, KEY_LEFT, KEY_INSERT, KEY_HOME, KEY_END,
223
224         /* 280 */
225         KEY_PAGEUP, KEY_PAGEDOWN, KEY_F1, KEY_F2, KEY_F3,
226         KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8,
227         KEY_F9, KEY_F10, KEY_F11, KEY_F12, NONE,
228         NONE, NONE, NONE, NONE, NONE,
229
230         /* 300 */
231         KEY_NUMLOCK, KEY_CAPSLOCK, KEY_SCROLLLOCK, KEY_RIGHTSHIFT,
232                 KEY_LEFTSHIFT,
233         KEY_RIGHTCTRL, KEY_LEFTCTRL, KEY_RIGHTALT, KEY_LEFTALT, KEY_RIGHTMETA,
234         KEY_LEFTMETA, NONE, KEY_FN, NONE, KEY_COMPOSE,
235         NONE, KEY_PRINT, KEY_SYSRQ, KEY_PAUSE, NONE,
236
237         /* 320 */
238         NONE, NONE, NONE,
239 };
240
241 int sandbox_sdl_scan_keys(int key[], int max_keys)
242 {
243         Uint8 *keystate;
244         int i, count;
245
246         sandbox_sdl_poll_events();
247         keystate = SDL_GetKeyState(NULL);
248         for (i = count = 0; i < NUM_SDL_CODES; i++) {
249                 if (count >= max_keys)
250                         break;
251                 else if (keystate[i])
252                         key[count++] = sdl_to_keycode[i];
253         }
254
255         return count;
256 }
257
258 int sandbox_sdl_key_pressed(int keycode)
259 {
260         int key[8];     /* allow up to 8 keys to be pressed at once */
261         int count;
262         int i;
263
264         count = sandbox_sdl_scan_keys(key, sizeof(key) / sizeof(key[0]));
265         for (i = 0; i < count; i++) {
266                 if (key[i] == keycode)
267                         return 0;
268         }
269
270         return -ENOENT;
271 }
272
273 void sandbox_sdl_fill_audio(void *udata, Uint8 *stream, int len)
274 {
275         struct buf_info *buf;
276         int avail;
277         bool have_data = false;
278         int i;
279
280         for (i = 0; i < 2; i++) {
281                 buf = &sdl.buf[sdl.cur_buf];
282                 avail = buf->size - buf->pos;
283                 if (avail <= 0) {
284                         sdl.cur_buf = 1 - sdl.cur_buf;
285                         continue;
286                 }
287                 if (avail > len)
288                         avail = len;
289                 have_data = true;
290
291                 SDL_MixAudio(stream, buf->data + buf->pos, avail,
292                              SDL_MIX_MAXVOLUME);
293                 buf->pos += avail;
294                 len -= avail;
295
296                 /* Move to next buffer if we are at the end */
297                 if (buf->pos == buf->size)
298                         buf->size = 0;
299                 else
300                         break;
301         }
302         sdl.stopping = !have_data;
303 }
304
305 int sandbox_sdl_sound_init(int rate, int channels)
306 {
307         SDL_AudioSpec wanted, have;
308         int i;
309
310         if (sandbox_sdl_ensure_init())
311                 return -1;
312
313         if (sdl.audio_active)
314                 return 0;
315
316         /* Set the audio format */
317         wanted.freq = rate;
318         wanted.format = AUDIO_S16;
319         wanted.channels = channels;
320         wanted.samples = 1024;  /* Good low-latency value for callback */
321         wanted.callback = sandbox_sdl_fill_audio;
322         wanted.userdata = NULL;
323
324         for (i = 0; i < 2; i++) {
325                 struct buf_info *buf = &sdl.buf[i];
326
327                 buf->alloced = sizeof(uint16_t) * wanted.freq * wanted.channels;
328                 buf->data = malloc(buf->alloced);
329                 if (!buf->data) {
330                         printf("%s: Out of memory\n", __func__);
331                         if (i == 1)
332                                 free(sdl.buf[0].data);
333                         return -1;
334                 }
335                 buf->pos = 0;
336                 buf->size = 0;
337         }
338
339         if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
340                 printf("Unable to initialise SDL audio: %s\n", SDL_GetError());
341                 goto err;
342         }
343
344         /* Open the audio device, forcing the desired format */
345         if (SDL_OpenAudio(&wanted, &have) < 0) {
346                 printf("Couldn't open audio: %s\n", SDL_GetError());
347                 goto err;
348         }
349         if (have.format != wanted.format) {
350                 printf("Couldn't select required audio format\n");
351                 goto err;
352         }
353         sdl.audio_active = true;
354         sdl.sample_rate = wanted.freq;
355         sdl.cur_buf = 0;
356         sdl.running = false;
357
358         return 0;
359
360 err:
361         for (i = 0; i < 2; i++)
362                 free(sdl.buf[i].data);
363         return -1;
364 }
365
366 int sandbox_sdl_sound_play(const void *data, uint size)
367 {
368         struct buf_info *buf;
369
370         if (!sdl.audio_active)
371                 return 0;
372
373         buf = &sdl.buf[0];
374         if (buf->size)
375                 buf = &sdl.buf[1];
376         while (buf->size)
377                 usleep(1000);
378
379         if (size > buf->alloced)
380                 return -E2BIG;
381
382         memcpy(buf->data, data, size);
383         buf->size = size;
384         buf->pos = 0;
385         if (!sdl.running) {
386                 SDL_PauseAudio(0);
387                 sdl.running = true;
388                 sdl.stopping = false;
389         }
390
391         return 0;
392 }
393
394 int sandbox_sdl_sound_stop(void)
395 {
396         if (sdl.running) {
397                 while (!sdl.stopping)
398                         SDL_Delay(100);
399
400                 SDL_PauseAudio(1);
401                 sdl.running = 0;
402                 sdl.stopping = false;
403         }
404
405         return 0;
406 }