configs: starfive: add starfive_visionfive2_defconfig
[platform/kernel/u-boot.git] / boot / scene.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Implementation of a scene, a collection of text/image/menu items in an expo
4  *
5  * Copyright 2022 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8
9 #include <common.h>
10 #include <dm.h>
11 #include <expo.h>
12 #include <malloc.h>
13 #include <mapmem.h>
14 #include <video.h>
15 #include <video_console.h>
16 #include <linux/input.h>
17 #include "scene_internal.h"
18
19 uint resolve_id(struct expo *exp, uint id)
20 {
21         if (!id)
22                 id = exp->next_id++;
23         else if (id >= exp->next_id)
24                 exp->next_id = id + 1;
25
26         return id;
27 }
28
29 int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp)
30 {
31         struct scene *scn;
32
33         scn = calloc(1, sizeof(struct scene));
34         if (!scn)
35                 return log_msg_ret("expo", -ENOMEM);
36         scn->name = strdup(name);
37         if (!scn->name) {
38                 free(scn);
39                 return log_msg_ret("name", -ENOMEM);
40         }
41
42         INIT_LIST_HEAD(&scn->obj_head);
43         scn->id = resolve_id(exp, id);
44         scn->expo = exp;
45         list_add_tail(&scn->sibling, &exp->scene_head);
46
47         *scnp = scn;
48
49         return scn->id;
50 }
51
52 void scene_obj_destroy(struct scene_obj *obj)
53 {
54         if (obj->type == SCENEOBJT_MENU)
55                 scene_menu_destroy((struct scene_obj_menu *)obj);
56         free(obj->name);
57         free(obj);
58 }
59
60 void scene_destroy(struct scene *scn)
61 {
62         struct scene_obj *obj, *next;
63
64         list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
65                 scene_obj_destroy(obj);
66
67         free(scn->name);
68         free(scn->title);
69         free(scn);
70 }
71
72 int scene_title_set(struct scene *scn, const char *title)
73 {
74         free(scn->title);
75         scn->title = strdup(title);
76         if (!scn->title)
77                 return log_msg_ret("tit", -ENOMEM);
78
79         return 0;
80 }
81
82 int scene_obj_count(struct scene *scn)
83 {
84         struct scene_obj *obj;
85         int count = 0;
86
87         list_for_each_entry(obj, &scn->obj_head, sibling)
88                 count++;
89
90         return count;
91 }
92
93 void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type)
94 {
95         struct scene_obj *obj;
96
97         list_for_each_entry(obj, &scn->obj_head, sibling) {
98                 if (obj->id == id &&
99                     (type == SCENEOBJT_NONE || obj->type == type))
100                         return obj;
101         }
102
103         return NULL;
104 }
105
106 int scene_obj_add(struct scene *scn, const char *name, uint id,
107                   enum scene_obj_t type, uint size, struct scene_obj **objp)
108 {
109         struct scene_obj *obj;
110
111         obj = calloc(1, size);
112         if (!obj)
113                 return log_msg_ret("obj", -ENOMEM);
114         obj->name = strdup(name);
115         if (!obj->name) {
116                 free(obj);
117                 return log_msg_ret("name", -ENOMEM);
118         }
119
120         obj->id = resolve_id(scn->expo, id);
121         obj->scene = scn;
122         obj->type = type;
123         list_add_tail(&obj->sibling, &scn->obj_head);
124         *objp = obj;
125
126         return obj->id;
127 }
128
129 int scene_img(struct scene *scn, const char *name, uint id, char *data,
130               struct scene_obj_img **imgp)
131 {
132         struct scene_obj_img *img;
133         int ret;
134
135         ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
136                             sizeof(struct scene_obj_img),
137                             (struct scene_obj **)&img);
138         if (ret < 0)
139                 return log_msg_ret("obj", -ENOMEM);
140
141         img->data = data;
142
143         if (imgp)
144                 *imgp = img;
145
146         return img->obj.id;
147 }
148
149 int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
150               struct scene_obj_txt **txtp)
151 {
152         struct scene_obj_txt *txt;
153         int ret;
154
155         ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
156                             sizeof(struct scene_obj_txt),
157                             (struct scene_obj **)&txt);
158         if (ret < 0)
159                 return log_msg_ret("obj", -ENOMEM);
160
161         txt->str_id = str_id;
162
163         if (txtp)
164                 *txtp = txt;
165
166         return txt->obj.id;
167 }
168
169 int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
170                   const char *str, struct scene_obj_txt **txtp)
171 {
172         struct scene_obj_txt *txt;
173         int ret;
174
175         ret = expo_str(scn->expo, name, str_id, str);
176         if (ret < 0)
177                 return log_msg_ret("str", ret);
178         else if (ret != str_id)
179                 return log_msg_ret("id", -EEXIST);
180
181         ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
182                             sizeof(struct scene_obj_txt),
183                             (struct scene_obj **)&txt);
184         if (ret < 0)
185                 return log_msg_ret("obj", -ENOMEM);
186
187         txt->str_id = str_id;
188
189         if (txtp)
190                 *txtp = txt;
191
192         return txt->obj.id;
193 }
194
195 int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
196                        uint font_size)
197 {
198         struct scene_obj_txt *txt;
199
200         txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
201         if (!txt)
202                 return log_msg_ret("find", -ENOENT);
203         txt->font_name = font_name;
204         txt->font_size = font_size;
205
206         return 0;
207 }
208
209 int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
210 {
211         struct scene_obj *obj;
212
213         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
214         if (!obj)
215                 return log_msg_ret("find", -ENOENT);
216         obj->x = x;
217         obj->y = y;
218         if (obj->type == SCENEOBJT_MENU)
219                 scene_menu_arrange(scn, (struct scene_obj_menu *)obj);
220
221         return 0;
222 }
223
224 int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
225 {
226         struct scene_obj *obj;
227
228         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
229         if (!obj)
230                 return log_msg_ret("find", -ENOENT);
231         obj->hide = hide;
232
233         return 0;
234 }
235
236 int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
237 {
238         struct scene_obj *obj;
239
240         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
241         if (!obj)
242                 return log_msg_ret("find", -ENOENT);
243
244         switch (obj->type) {
245         case SCENEOBJT_NONE:
246         case SCENEOBJT_MENU:
247                 break;
248         case SCENEOBJT_IMAGE: {
249                 struct scene_obj_img *img = (struct scene_obj_img *)obj;
250                 ulong width, height;
251                 uint bpix;
252
253                 video_bmp_get_info(img->data, &width, &height, &bpix);
254                 if (widthp)
255                         *widthp = width;
256                 return height;
257         }
258         case SCENEOBJT_TEXT: {
259                 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
260                 struct expo *exp = scn->expo;
261
262                 if (widthp)
263                         *widthp = 16; /* fake value for now */
264                 if (txt->font_size)
265                         return txt->font_size;
266                 if (exp->display)
267                         return video_default_font_height(exp->display);
268
269                 /* use a sensible default */
270                 return 16;
271         }
272         }
273
274         return 0;
275 }
276
277 /**
278  * scene_obj_render() - Render an object
279  *
280  */
281 static int scene_obj_render(struct scene_obj *obj, bool text_mode)
282 {
283         struct scene *scn = obj->scene;
284         struct expo *exp = scn->expo;
285         struct udevice *cons, *dev = exp->display;
286         int x, y, ret;
287
288         cons = NULL;
289         if (!text_mode) {
290                 ret = device_find_first_child_by_uclass(dev,
291                                                         UCLASS_VIDEO_CONSOLE,
292                                                         &cons);
293         }
294
295         x = obj->x;
296         y = obj->y;
297
298         switch (obj->type) {
299         case SCENEOBJT_NONE:
300                 break;
301         case SCENEOBJT_IMAGE: {
302                 struct scene_obj_img *img = (struct scene_obj_img *)obj;
303
304                 if (!cons)
305                         return -ENOTSUPP;
306                 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
307                                         true);
308                 if (ret < 0)
309                         return log_msg_ret("img", ret);
310                 break;
311         }
312         case SCENEOBJT_TEXT: {
313                 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
314                 const char *str;
315
316                 if (!cons)
317                         return -ENOTSUPP;
318
319                 if (txt->font_name || txt->font_size) {
320                         ret = vidconsole_select_font(cons,
321                                                      txt->font_name,
322                                                      txt->font_size);
323                 } else {
324                         ret = vidconsole_select_font(cons, NULL, 0);
325                 }
326                 if (ret && ret != -ENOSYS)
327                         return log_msg_ret("font", ret);
328                 vidconsole_set_cursor_pos(cons, x, y);
329                 str = expo_get_str(exp, txt->str_id);
330                 if (str)
331                         vidconsole_put_string(cons, str);
332                 break;
333         }
334         case SCENEOBJT_MENU: {
335                 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
336                 /*
337                  * With a vidconsole, the text and item pointer are rendered as
338                  * normal objects so we don't need to do anything here. The menu
339                  * simply controls where they are positioned.
340                  */
341                 if (cons)
342                         return -ENOTSUPP;
343
344                 ret = scene_menu_display(menu);
345                 if (ret < 0)
346                         return log_msg_ret("img", ret);
347
348                 break;
349         }
350         }
351
352         return 0;
353 }
354
355 int scene_arrange(struct scene *scn)
356 {
357         struct scene_obj *obj;
358         int ret;
359
360         list_for_each_entry(obj, &scn->obj_head, sibling) {
361                 if (obj->type == SCENEOBJT_MENU) {
362                         struct scene_obj_menu *menu;
363
364                         menu = (struct scene_obj_menu *)obj,
365                         ret = scene_menu_arrange(scn, menu);
366                         if (ret)
367                                 return log_msg_ret("arr", ret);
368                 }
369         }
370
371         return 0;
372 }
373
374 int scene_render(struct scene *scn)
375 {
376         struct expo *exp = scn->expo;
377         struct scene_obj *obj;
378         int ret;
379
380         list_for_each_entry(obj, &scn->obj_head, sibling) {
381                 if (!obj->hide) {
382                         ret = scene_obj_render(obj, exp->text_mode);
383                         if (ret && ret != -ENOTSUPP)
384                                 return log_msg_ret("ren", ret);
385                 }
386         }
387
388         return 0;
389 }
390
391 int scene_send_key(struct scene *scn, int key, struct expo_action *event)
392 {
393         struct scene_obj *obj;
394         int ret;
395
396         list_for_each_entry(obj, &scn->obj_head, sibling) {
397                 if (obj->type == SCENEOBJT_MENU) {
398                         struct scene_obj_menu *menu;
399
400                         menu = (struct scene_obj_menu *)obj,
401                         ret = scene_menu_send_key(scn, menu, key, event);
402                         if (ret)
403                                 return log_msg_ret("key", ret);
404
405                         /* only allow one menu */
406                         ret = scene_menu_arrange(scn, menu);
407                         if (ret)
408                                 return log_msg_ret("arr", ret);
409                         break;
410                 }
411         }
412
413         return 0;
414 }