blob: 53641fc28b6cf1c767e9f4dfcddc78581537d19f [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass623d28f2016-01-18 19:52:15 -07002/*
3 * Copyright (c) 2015 Google, Inc
Simon Glass623d28f2016-01-18 19:52:15 -07004 */
5
Patrick Delaunay81313352021-04-27 11:02:19 +02006#define LOG_CATEGORY UCLASS_VIDEO
7
Nikhil M Jainf7ec5312023-07-18 14:27:31 +05308#include <bloblist.h>
Simon Glass73c9c372020-07-02 21:12:20 -06009#include <console.h>
Simon Glass63334482019-11-14 12:57:39 -070010#include <cpu_func.h>
Simon Glasse0337a52024-07-31 08:44:10 -060011#include <cyclic.h>
Simon Glass623d28f2016-01-18 19:52:15 -070012#include <dm.h>
Simon Glass0f2af882020-05-10 11:40:05 -060013#include <log.h>
Simon Glass9bc15642020-02-03 07:36:16 -070014#include <malloc.h>
Simon Glass623d28f2016-01-18 19:52:15 -070015#include <mapmem.h>
Nikhil M Jainf7ec5312023-07-18 14:27:31 +053016#include <spl.h>
Simon Glass623d28f2016-01-18 19:52:15 -070017#include <stdio_dev.h>
18#include <video.h>
19#include <video_console.h>
Simon Glass274e0b02020-05-10 11:39:56 -060020#include <asm/cache.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060021#include <asm/global_data.h>
Simon Glass623d28f2016-01-18 19:52:15 -070022#include <dm/lists.h>
Michal Simek632e3d42020-12-14 08:47:52 +010023#include <dm/device_compat.h>
Simon Glass623d28f2016-01-18 19:52:15 -070024#include <dm/device-internal.h>
25#include <dm/uclass-internal.h>
26#ifdef CONFIG_SANDBOX
27#include <asm/sdl.h>
28#endif
Simon Glassaef8c792025-04-02 06:29:43 +130029#include "vidconsole_internal.h"
Simon Glass623d28f2016-01-18 19:52:15 -070030
31/*
32 * Theory of operation:
33 *
34 * Before relocation each device is bound. The driver for each device must
Simon Glassb75b15b2020-12-03 16:55:23 -070035 * set the @align and @size values in struct video_uc_plat. This
Simon Glass623d28f2016-01-18 19:52:15 -070036 * information represents the requires size and alignment of the frame buffer
37 * for the device. The values can be an over-estimate but cannot be too
38 * small. The actual values will be suppled (in the same manner) by the bind()
Pali Rohárf204fce2022-03-09 20:46:00 +010039 * method after relocation. Additionally driver can allocate frame buffer
40 * itself by setting plat->base.
Simon Glass623d28f2016-01-18 19:52:15 -070041 *
42 * This information is then picked up by video_reserve() which works out how
43 * much memory is needed for all devices. This is allocated between
44 * gd->video_bottom and gd->video_top.
45 *
46 * After relocation the same process occurs. The driver supplies the same
47 * @size and @align information and this time video_post_bind() checks that
48 * the drivers does not overflow the allocated memory.
49 *
50 * The frame buffer address is actually set (to plat->base) in
51 * video_post_probe(). This function also clears the frame buffer and
52 * allocates a suitable text console device. This can then be used to write
53 * text to the video device.
54 */
55DECLARE_GLOBAL_DATA_PTR;
56
Simon Glasse0337a52024-07-31 08:44:10 -060057struct cyclic_info;
58
Simon Glass951255d2020-07-02 21:12:32 -060059/**
60 * struct video_uc_priv - Information for the video uclass
61 *
62 * @video_ptr: Current allocation position of the video framebuffer pointer.
63 * While binding devices after relocation, this points to the next
64 * available address to use for a device's framebuffer. It starts at
65 * gd->video_top and works downwards, running out of space when it hits
66 * gd->video_bottom.
Simon Glasse0337a52024-07-31 08:44:10 -060067 * @cyc: handle for cyclic-execution function, or NULL if none
Simon Glass951255d2020-07-02 21:12:32 -060068 */
69struct video_uc_priv {
70 ulong video_ptr;
Simon Glasse0337a52024-07-31 08:44:10 -060071 bool cyc_active;
72 struct cyclic_info cyc;
Simon Glass951255d2020-07-02 21:12:32 -060073};
74
Simon Glass2a006332022-10-06 08:36:03 -060075/** struct vid_rgb - Describes a video colour */
76struct vid_rgb {
77 u32 r;
78 u32 g;
79 u32 b;
80};
81
Simon Glass64635382016-01-21 19:44:52 -070082void video_set_flush_dcache(struct udevice *dev, bool flush)
83{
84 struct video_priv *priv = dev_get_uclass_priv(dev);
85
86 priv->flush_dcache = flush;
87}
88
Simon Glassdf865d32023-03-10 12:47:17 -080089static ulong alloc_fb_(ulong align, ulong size, ulong *addrp)
90{
91 ulong base;
92
93 align = align ? align : 1 << 20;
94 base = *addrp - size;
95 base &= ~(align - 1);
96 size = *addrp - base;
97 *addrp = base;
98
99 return size;
100}
101
Simon Glass623d28f2016-01-18 19:52:15 -0700102static ulong alloc_fb(struct udevice *dev, ulong *addrp)
103{
Simon Glassb75b15b2020-12-03 16:55:23 -0700104 struct video_uc_plat *plat = dev_get_uclass_plat(dev);
Simon Glassdf865d32023-03-10 12:47:17 -0800105 ulong size;
Simon Glass623d28f2016-01-18 19:52:15 -0700106
Simon Glassdf865d32023-03-10 12:47:17 -0800107 if (!plat->size) {
108 if (IS_ENABLED(CONFIG_VIDEO_COPY) && plat->copy_size) {
109 size = alloc_fb_(plat->align, plat->copy_size, addrp);
110 plat->copy_base = *addrp;
111 return size;
112 }
113
Bin Meng755623d2016-10-09 04:14:17 -0700114 return 0;
Simon Glassdf865d32023-03-10 12:47:17 -0800115 }
Bin Meng755623d2016-10-09 04:14:17 -0700116
Pali Rohárf204fce2022-03-09 20:46:00 +0100117 /* Allow drivers to allocate the frame buffer themselves */
118 if (plat->base)
119 return 0;
120
Simon Glassdf865d32023-03-10 12:47:17 -0800121 size = alloc_fb_(plat->align, plat->size, addrp);
122 plat->base = *addrp;
Simon Glass623d28f2016-01-18 19:52:15 -0700123
124 return size;
125}
126
127int video_reserve(ulong *addrp)
128{
129 struct udevice *dev;
130 ulong size;
131
Simon Glassd4dce4a2024-09-29 19:49:36 -0600132 if (IS_ENABLED(CONFIG_SPL_VIDEO_HANDOFF) && xpl_phase() == PHASE_BOARD_F)
Devarsh Thakkar2febd462023-12-05 21:25:20 +0530133 return 0;
134
Simon Glass623d28f2016-01-18 19:52:15 -0700135 gd->video_top = *addrp;
136 for (uclass_find_first_device(UCLASS_VIDEO, &dev);
137 dev;
138 uclass_find_next_device(&dev)) {
139 size = alloc_fb(dev, addrp);
140 debug("%s: Reserving %lx bytes at %lx for video device '%s'\n",
141 __func__, size, *addrp, dev->name);
142 }
Simon Glassc3d2f352020-07-02 21:12:33 -0600143
144 /* Allocate space for PCI video devices in case there were not bound */
145 if (*addrp == gd->video_top)
Nikhil M Jain9e3301d2023-04-20 17:41:08 +0530146 *addrp -= CONFIG_VAL(VIDEO_PCI_DEFAULT_FB_SIZE);
Simon Glassc3d2f352020-07-02 21:12:33 -0600147
Simon Glass623d28f2016-01-18 19:52:15 -0700148 gd->video_bottom = *addrp;
149 debug("Video frame buffers from %lx to %lx\n", gd->video_bottom,
150 gd->video_top);
151
152 return 0;
153}
154
Simon Glass853e8d22024-08-21 10:18:55 -0600155ulong video_get_fb(void)
156{
157 struct udevice *dev;
158
159 uclass_find_first_device(UCLASS_VIDEO, &dev);
160 if (dev) {
161 const struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
162
163 return uc_plat->base;
164 }
165
166 return 0;
167}
168
Simon Glass062673b2023-06-01 10:22:33 -0600169int video_fill_part(struct udevice *dev, int xstart, int ystart, int xend,
170 int yend, u32 colour)
171{
172 struct video_priv *priv = dev_get_uclass_priv(dev);
173 void *start, *line;
174 int pixels = xend - xstart;
Alexander Graff88b6a22022-06-10 00:59:21 +0200175 int row, i;
Simon Glass062673b2023-06-01 10:22:33 -0600176
177 start = priv->fb + ystart * priv->line_length;
178 start += xstart * VNBYTES(priv->bpix);
179 line = start;
180 for (row = ystart; row < yend; row++) {
181 switch (priv->bpix) {
182 case VIDEO_BPP8: {
183 u8 *dst = line;
184
185 if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
186 for (i = 0; i < pixels; i++)
187 *dst++ = colour;
188 }
189 break;
190 }
191 case VIDEO_BPP16: {
192 u16 *dst = line;
193
194 if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
195 for (i = 0; i < pixels; i++)
196 *dst++ = colour;
197 }
198 break;
199 }
200 case VIDEO_BPP32: {
201 u32 *dst = line;
202
203 if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
204 for (i = 0; i < pixels; i++)
205 *dst++ = colour;
206 }
207 break;
208 }
209 default:
210 return -ENOSYS;
211 }
212 line += priv->line_length;
213 }
Simon Glass062673b2023-06-01 10:22:33 -0600214
Alexander Graf3b2cc822022-06-10 00:59:16 +0200215 video_damage(dev, xstart, ystart, xend - xstart, yend - ystart);
216
Simon Glass062673b2023-06-01 10:22:33 -0600217 return 0;
218}
219
Simon Glassaef8c792025-04-02 06:29:43 +1300220int video_draw_box(struct udevice *dev, int x0, int y0, int x1, int y1,
221 int width, u32 colour)
222{
223 struct video_priv *priv = dev_get_uclass_priv(dev);
224 int pbytes = VNBYTES(priv->bpix);
225 void *start, *line;
226 int pixels = x1 - x0;
227 int row;
228
229 start = priv->fb + y0 * priv->line_length;
230 start += x0 * pbytes;
231 line = start;
232 for (row = y0; row < y1; row++) {
233 void *ptr = line;
234 int i;
235
236 for (i = 0; i < width; i++)
237 fill_pixel_and_goto_next(&ptr, colour, pbytes, pbytes);
238 if (row < y0 + width || row >= y1 - width) {
239 for (i = 0; i < pixels - width * 2; i++)
240 fill_pixel_and_goto_next(&ptr, colour, pbytes,
241 pbytes);
242 } else {
243 ptr += (pixels - width * 2) * pbytes;
244 }
245 for (i = 0; i < width; i++)
246 fill_pixel_and_goto_next(&ptr, colour, pbytes, pbytes);
247 line += priv->line_length;
248 }
249 video_damage(dev, x0, y0, x1 - x0, y1 - y0);
250
251 return 0;
252}
253
Nikhil M Jain76833532023-07-18 14:27:30 +0530254int video_reserve_from_bloblist(struct video_handoff *ho)
255{
Devarsh Thakkar2febd462023-12-05 21:25:20 +0530256 if (!ho->fb || ho->size == 0)
257 return -ENOENT;
258
Nikhil M Jain76833532023-07-18 14:27:30 +0530259 gd->video_bottom = ho->fb;
Nikhil M Jain76833532023-07-18 14:27:30 +0530260 gd->video_top = ho->fb + ho->size;
Devarsh Thakkar2febd462023-12-05 21:25:20 +0530261 debug("%s: Reserving %lx bytes at %08x as per bloblist received\n",
262 __func__, (unsigned long)ho->size, (u32)ho->fb);
Nikhil M Jain76833532023-07-18 14:27:30 +0530263
264 return 0;
265}
266
Simon Glass2baa6f82022-10-06 08:36:08 -0600267int video_fill(struct udevice *dev, u32 colour)
Simon Glass623d28f2016-01-18 19:52:15 -0700268{
269 struct video_priv *priv = dev_get_uclass_priv(dev);
270
Heinrich Schuchardt5e4947e2018-02-08 21:47:10 +0100271 switch (priv->bpix) {
Simon Glass05c17d62019-12-20 18:10:37 -0700272 case VIDEO_BPP16:
Nikhil M Jain9e3301d2023-04-20 17:41:08 +0530273 if (CONFIG_IS_ENABLED(VIDEO_BPP16)) {
Simon Glass05c17d62019-12-20 18:10:37 -0700274 u16 *ppix = priv->fb;
275 u16 *end = priv->fb + priv->fb_size;
Heinrich Schuchardt5e4947e2018-02-08 21:47:10 +0100276
Simon Glass05c17d62019-12-20 18:10:37 -0700277 while (ppix < end)
Simon Glass2baa6f82022-10-06 08:36:08 -0600278 *ppix++ = colour;
Simon Glass05c17d62019-12-20 18:10:37 -0700279 break;
280 }
Andre Przywara0cff09f2025-03-27 15:33:05 +0000281 fallthrough;
Simon Glass05c17d62019-12-20 18:10:37 -0700282 case VIDEO_BPP32:
Nikhil M Jain9e3301d2023-04-20 17:41:08 +0530283 if (CONFIG_IS_ENABLED(VIDEO_BPP32)) {
Simon Glass05c17d62019-12-20 18:10:37 -0700284 u32 *ppix = priv->fb;
285 u32 *end = priv->fb + priv->fb_size;
Simon Glass623d28f2016-01-18 19:52:15 -0700286
Simon Glass05c17d62019-12-20 18:10:37 -0700287 while (ppix < end)
Simon Glass2baa6f82022-10-06 08:36:08 -0600288 *ppix++ = colour;
Simon Glass05c17d62019-12-20 18:10:37 -0700289 break;
290 }
Andre Przywara0cff09f2025-03-27 15:33:05 +0000291 fallthrough;
Heinrich Schuchardt5e4947e2018-02-08 21:47:10 +0100292 default:
Simon Glass2baa6f82022-10-06 08:36:08 -0600293 memset(priv->fb, colour, priv->fb_size);
Heinrich Schuchardt5e4947e2018-02-08 21:47:10 +0100294 break;
Simon Glass623d28f2016-01-18 19:52:15 -0700295 }
Simon Glass55343122018-10-01 12:22:26 -0600296
Alexander Graf3b2cc822022-06-10 00:59:16 +0200297 video_damage(dev, 0, 0, priv->xsize, priv->ysize);
298
Michal Simeka1e136d2020-12-15 15:12:09 +0100299 return video_sync(dev, false);
Simon Glass623d28f2016-01-18 19:52:15 -0700300}
301
Simon Glass2baa6f82022-10-06 08:36:08 -0600302int video_clear(struct udevice *dev)
303{
304 struct video_priv *priv = dev_get_uclass_priv(dev);
305 int ret;
306
307 ret = video_fill(dev, priv->colour_bg);
308 if (ret)
309 return ret;
310
311 return 0;
312}
313
Simon Glass2a006332022-10-06 08:36:03 -0600314static const struct vid_rgb colours[VID_COLOUR_COUNT] = {
315 { 0x00, 0x00, 0x00 }, /* black */
316 { 0xc0, 0x00, 0x00 }, /* red */
317 { 0x00, 0xc0, 0x00 }, /* green */
318 { 0xc0, 0x60, 0x00 }, /* brown */
319 { 0x00, 0x00, 0xc0 }, /* blue */
320 { 0xc0, 0x00, 0xc0 }, /* magenta */
321 { 0x00, 0xc0, 0xc0 }, /* cyan */
322 { 0xc0, 0xc0, 0xc0 }, /* light gray */
323 { 0x80, 0x80, 0x80 }, /* gray */
324 { 0xff, 0x00, 0x00 }, /* bright red */
325 { 0x00, 0xff, 0x00 }, /* bright green */
326 { 0xff, 0xff, 0x00 }, /* yellow */
327 { 0x00, 0x00, 0xff }, /* bright blue */
328 { 0xff, 0x00, 0xff }, /* bright magenta */
329 { 0x00, 0xff, 0xff }, /* bright cyan */
330 { 0xff, 0xff, 0xff }, /* white */
Simon Glassbda3adc2024-10-14 16:31:53 -0600331
332 /* an extra one for menus */
333 { 0x40, 0x40, 0x40 }, /* dark gray */
Simon Glass2a006332022-10-06 08:36:03 -0600334};
335
Simon Glassd17a6242023-06-01 10:22:48 -0600336u32 video_index_to_colour(struct video_priv *priv, enum colour_idx idx)
Simon Glass2a006332022-10-06 08:36:03 -0600337{
338 switch (priv->bpix) {
339 case VIDEO_BPP16:
Nikhil M Jain9e3301d2023-04-20 17:41:08 +0530340 if (CONFIG_IS_ENABLED(VIDEO_BPP16)) {
Simon Glass2a006332022-10-06 08:36:03 -0600341 return ((colours[idx].r >> 3) << 11) |
342 ((colours[idx].g >> 2) << 5) |
343 ((colours[idx].b >> 3) << 0);
344 }
345 break;
346 case VIDEO_BPP32:
Nikhil M Jain9e3301d2023-04-20 17:41:08 +0530347 if (CONFIG_IS_ENABLED(VIDEO_BPP32)) {
Michal Simek985eac82023-05-17 10:42:07 +0200348 switch (priv->format) {
349 case VIDEO_X2R10G10B10:
Simon Glass2a006332022-10-06 08:36:03 -0600350 return (colours[idx].r << 22) |
351 (colours[idx].g << 12) |
352 (colours[idx].b << 2);
Michal Simek985eac82023-05-17 10:42:07 +0200353 case VIDEO_RGBA8888:
354 return (colours[idx].r << 24) |
355 (colours[idx].g << 16) |
356 (colours[idx].b << 8) | 0xff;
357 default:
Simon Glass2a006332022-10-06 08:36:03 -0600358 return (colours[idx].r << 16) |
359 (colours[idx].g << 8) |
360 (colours[idx].b << 0);
Michal Simek985eac82023-05-17 10:42:07 +0200361 }
Simon Glass2a006332022-10-06 08:36:03 -0600362 }
363 break;
364 default:
365 break;
366 }
367
368 /*
369 * For unknown bit arrangements just support
370 * black and white.
371 */
372 if (idx)
373 return 0xffffff; /* white */
374
375 return 0x000000; /* black */
376}
377
Simon Glass2b063b82018-11-06 15:21:36 -0700378void video_set_default_colors(struct udevice *dev, bool invert)
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100379{
Simon Glass2b063b82018-11-06 15:21:36 -0700380 struct video_priv *priv = dev_get_uclass_priv(dev);
381 int fore, back;
382
Simon Glass21320da2025-04-02 06:29:33 +1300383 if (priv->white_on_black) {
Simon Glass05c17d62019-12-20 18:10:37 -0700384 /* White is used when switching to bold, use light gray here */
385 fore = VID_LIGHT_GRAY;
386 back = VID_BLACK;
387 } else {
388 fore = VID_BLACK;
389 back = VID_WHITE;
390 }
Simon Glass2b063b82018-11-06 15:21:36 -0700391 if (invert) {
392 int temp;
393
394 temp = fore;
395 fore = back;
396 back = temp;
397 }
398 priv->fg_col_idx = fore;
Andre Przywara4ed5bc82019-03-23 01:29:56 +0000399 priv->bg_col_idx = back;
Simon Glass2a006332022-10-06 08:36:03 -0600400 priv->colour_fg = video_index_to_colour(priv, fore);
401 priv->colour_bg = video_index_to_colour(priv, back);
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100402}
403
Alexander Grafdb757752022-06-10 00:59:15 +0200404/* Notify about changes in the frame buffer */
405#ifdef CONFIG_VIDEO_DAMAGE
406void video_damage(struct udevice *vid, int x, int y, int width, int height)
407{
408 struct video_priv *priv = dev_get_uclass_priv(vid);
409 int xend = x + width;
410 int yend = y + height;
411
412 if (x > priv->xsize)
413 return;
414
415 if (y > priv->ysize)
416 return;
417
418 if (xend > priv->xsize)
419 xend = priv->xsize;
420
421 if (yend > priv->ysize)
422 yend = priv->ysize;
423
424 /* Span a rectangle across all old and new damage */
425 priv->damage.xstart = min(x, priv->damage.xstart);
426 priv->damage.ystart = min(y, priv->damage.ystart);
427 priv->damage.xend = max(xend, priv->damage.xend);
428 priv->damage.yend = max(yend, priv->damage.yend);
429}
430#endif
431
Alexander Graf2b978f22022-06-10 00:59:20 +0200432static void video_flush_dcache(struct udevice *vid, bool use_copy)
433{
434 struct video_priv *priv = dev_get_uclass_priv(vid);
435 ulong fb = use_copy ? (ulong)priv->copy_fb : (ulong)priv->fb;
Alexander Grafa4b815e2023-01-03 22:50:03 +0100436 uint cacheline_size = 32;
437
438#ifdef CONFIG_SYS_CACHELINE_SIZE
439 cacheline_size = CONFIG_SYS_CACHELINE_SIZE;
440#endif
441
442 if (CONFIG_IS_ENABLED(SYS_DCACHE_OFF))
443 return;
Alexander Graf2b978f22022-06-10 00:59:20 +0200444
445 if (!priv->flush_dcache)
446 return;
447
448 if (!IS_ENABLED(CONFIG_VIDEO_DAMAGE)) {
449 flush_dcache_range(fb, ALIGN(fb + priv->fb_size,
Alexander Grafa4b815e2023-01-03 22:50:03 +0100450 cacheline_size));
Alexander Graf2b978f22022-06-10 00:59:20 +0200451
452 return;
453 }
454
455 if (priv->damage.xend && priv->damage.yend) {
456 int lstart = priv->damage.xstart * VNBYTES(priv->bpix);
457 int lend = priv->damage.xend * VNBYTES(priv->bpix);
458 int y;
459
460 for (y = priv->damage.ystart; y < priv->damage.yend; y++) {
461 ulong start = fb + (y * priv->line_length) + lstart;
462 ulong end = start + lend - lstart;
463
Alexander Grafa4b815e2023-01-03 22:50:03 +0100464 start = ALIGN_DOWN(start, cacheline_size);
465 end = ALIGN(end, cacheline_size);
Alexander Graf2b978f22022-06-10 00:59:20 +0200466
467 flush_dcache_range(start, end);
468 }
469 }
470}
Alexander Graf2b978f22022-06-10 00:59:20 +0200471
Alexander Graff88b6a22022-06-10 00:59:21 +0200472static void video_flush_copy(struct udevice *vid)
473{
474 struct video_priv *priv = dev_get_uclass_priv(vid);
475
476 if (!priv->copy_fb)
477 return;
478
479 if (priv->damage.xend && priv->damage.yend) {
480 int lstart = priv->damage.xstart * VNBYTES(priv->bpix);
481 int lend = priv->damage.xend * VNBYTES(priv->bpix);
482 int y;
483
484 for (y = priv->damage.ystart; y < priv->damage.yend; y++) {
485 ulong offset = (y * priv->line_length) + lstart;
486 ulong len = lend - lstart;
487
488 memcpy(priv->copy_fb + offset, priv->fb + offset, len);
489 }
490 }
491}
492
Simon Glass623d28f2016-01-18 19:52:15 -0700493/* Flush video activity to the caches */
Michal Simek632e3d42020-12-14 08:47:52 +0100494int video_sync(struct udevice *vid, bool force)
Simon Glass623d28f2016-01-18 19:52:15 -0700495{
Simon Glass7f6280f2024-07-31 08:44:09 -0600496 struct video_priv *priv = dev_get_uclass_priv(vid);
Michal Simek8ae95df2020-12-03 09:30:00 +0100497 struct video_ops *ops = video_get_ops(vid);
498 int ret;
499
Alexander Graff88b6a22022-06-10 00:59:21 +0200500 if (IS_ENABLED(CONFIG_VIDEO_COPY))
501 video_flush_copy(vid);
502
Michal Simek8ae95df2020-12-03 09:30:00 +0100503 if (ops && ops->video_sync) {
504 ret = ops->video_sync(vid);
505 if (ret)
506 return ret;
507 }
508
Simon Glasse0337a52024-07-31 08:44:10 -0600509 if (CONFIG_IS_ENABLED(CYCLIC) && !force &&
510 get_timer(priv->last_sync) < CONFIG_VIDEO_SYNC_MS)
511 return 0;
512
Alexander Graf2b978f22022-06-10 00:59:20 +0200513 video_flush_dcache(vid, false);
514
515 if (IS_ENABLED(CONFIG_VIDEO_COPY))
516 video_flush_dcache(vid, true);
Alexander Grafa4b815e2023-01-03 22:50:03 +0100517
518#if defined(CONFIG_VIDEO_SANDBOX_SDL)
Simon Glassaef8c792025-04-02 06:29:43 +1300519 /* to see the copy framebuffer, use priv->copy_fb */
Simon Glasse0337a52024-07-31 08:44:10 -0600520 sandbox_sdl_sync(priv->fb);
Simon Glass623d28f2016-01-18 19:52:15 -0700521#endif
Simon Glasse0337a52024-07-31 08:44:10 -0600522 priv->last_sync = get_timer(0);
523
Alexander Grafdb757752022-06-10 00:59:15 +0200524 if (IS_ENABLED(CONFIG_VIDEO_DAMAGE)) {
525 priv->damage.xstart = priv->xsize;
526 priv->damage.ystart = priv->ysize;
527 priv->damage.xend = 0;
528 priv->damage.yend = 0;
529 }
530
Michal Simek632e3d42020-12-14 08:47:52 +0100531 return 0;
Simon Glass623d28f2016-01-18 19:52:15 -0700532}
533
534void video_sync_all(void)
535{
536 struct udevice *dev;
Michal Simek632e3d42020-12-14 08:47:52 +0100537 int ret;
Simon Glass623d28f2016-01-18 19:52:15 -0700538
539 for (uclass_find_first_device(UCLASS_VIDEO, &dev);
540 dev;
541 uclass_find_next_device(&dev)) {
Michal Simek632e3d42020-12-14 08:47:52 +0100542 if (device_active(dev)) {
543 ret = video_sync(dev, true);
544 if (ret)
545 dev_dbg(dev, "Video sync failed\n");
546 }
Simon Glass623d28f2016-01-18 19:52:15 -0700547 }
548}
549
Patrick Delaunayefcc84b2021-11-15 16:32:20 +0100550bool video_is_active(void)
551{
552 struct udevice *dev;
553
Devarsh Thakkar42e41062024-02-22 18:38:09 +0530554 /* Assume video to be active if SPL passed video hand-off to U-boot */
Simon Glassd4dce4a2024-09-29 19:49:36 -0600555 if (IS_ENABLED(CONFIG_SPL_VIDEO_HANDOFF) && xpl_phase() > PHASE_SPL)
Devarsh Thakkar42e41062024-02-22 18:38:09 +0530556 return true;
557
Patrick Delaunayefcc84b2021-11-15 16:32:20 +0100558 for (uclass_find_first_device(UCLASS_VIDEO, &dev);
559 dev;
560 uclass_find_next_device(&dev)) {
561 if (device_active(dev))
562 return true;
563 }
564
565 return false;
566}
567
Simon Glass623d28f2016-01-18 19:52:15 -0700568int video_get_xsize(struct udevice *dev)
569{
570 struct video_priv *priv = dev_get_uclass_priv(dev);
571
572 return priv->xsize;
573}
574
575int video_get_ysize(struct udevice *dev)
576{
577 struct video_priv *priv = dev_get_uclass_priv(dev);
578
579 return priv->ysize;
580}
581
Simon Glass87a3cd72021-11-19 13:24:03 -0700582#define SPLASH_DECL(_name) \
583 extern u8 __splash_ ## _name ## _begin[]; \
584 extern u8 __splash_ ## _name ## _end[]
585
586#define SPLASH_START(_name) __splash_ ## _name ## _begin
587
588SPLASH_DECL(u_boot_logo);
589
Simon Glass22477422022-10-06 08:36:09 -0600590void *video_get_u_boot_logo(void)
591{
592 return SPLASH_START(u_boot_logo);
593}
594
Simon Glass87a3cd72021-11-19 13:24:03 -0700595static int show_splash(struct udevice *dev)
596{
597 u8 *data = SPLASH_START(u_boot_logo);
598 int ret;
599
600 ret = video_bmp_display(dev, map_to_sysmem(data), -4, 4, true);
601
602 return 0;
603}
604
Simon Glass70459902022-10-06 08:36:18 -0600605int video_default_font_height(struct udevice *dev)
606{
607 struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
608
609 if (IS_ENABLED(CONFIG_CONSOLE_TRUETYPE))
610 return IF_ENABLED_INT(CONFIG_CONSOLE_TRUETYPE,
611 CONFIG_CONSOLE_TRUETYPE_SIZE);
612
613 return vc_priv->y_charsize;
614}
615
Simon Glasse0337a52024-07-31 08:44:10 -0600616static void video_idle(struct cyclic_info *cyc)
617{
618 video_sync_all();
619}
620
Simon Glass21320da2025-04-02 06:29:33 +1300621void video_set_white_on_black(struct udevice *dev, bool white_on_black)
622{
623 struct video_priv *priv = dev_get_uclass_priv(dev);
624
625 if (priv->white_on_black != white_on_black) {
626 priv->white_on_black = white_on_black;
627 video_set_default_colors(dev, false);
628
629 video_clear(dev);
630 }
631}
632
Simon Glass623d28f2016-01-18 19:52:15 -0700633/* Set up the display ready for use */
634static int video_post_probe(struct udevice *dev)
635{
Simon Glassb75b15b2020-12-03 16:55:23 -0700636 struct video_uc_plat *plat = dev_get_uclass_plat(dev);
Simon Glasse0337a52024-07-31 08:44:10 -0600637 struct video_uc_priv *uc_priv = uclass_get_priv(dev->uclass);
Simon Glass623d28f2016-01-18 19:52:15 -0700638 struct video_priv *priv = dev_get_uclass_priv(dev);
639 char name[30], drv[15], *str;
Simon Glassb3a72b32016-01-14 18:10:48 -0700640 const char *drv_name = drv;
Simon Glass623d28f2016-01-18 19:52:15 -0700641 struct udevice *cons;
642 int ret;
643
644 /* Set up the line and display size */
645 priv->fb = map_sysmem(plat->base, plat->size);
Simon Glass7d186732018-11-29 15:08:52 -0700646 if (!priv->line_length)
647 priv->line_length = priv->xsize * VNBYTES(priv->bpix);
648
Simon Glass623d28f2016-01-18 19:52:15 -0700649 priv->fb_size = priv->line_length * priv->ysize;
650
Devarsh Thakkarf5254cc2023-12-05 21:25:21 +0530651 /*
652 * Set up video handoff fields for passing video blob to next stage
653 * NOTE:
654 * This assumes that reserved video memory only uses a single framebuffer
655 */
Simon Glassd4dce4a2024-09-29 19:49:36 -0600656 if (xpl_phase() == PHASE_SPL && CONFIG_IS_ENABLED(BLOBLIST)) {
Devarsh Thakkarf5254cc2023-12-05 21:25:21 +0530657 struct video_handoff *ho;
658
659 ho = bloblist_add(BLOBLISTT_U_BOOT_VIDEO, sizeof(*ho), 0);
660 if (!ho)
661 return log_msg_ret("blf", -ENOENT);
662 ho->fb = gd->video_bottom;
663 /* Fill aligned size here as calculated in video_reserve() */
664 ho->size = gd->video_top - gd->video_bottom;
665 ho->xsize = priv->xsize;
666 ho->ysize = priv->ysize;
667 ho->line_length = priv->line_length;
668 ho->bpix = priv->bpix;
Simon Glass30aaa9b2025-01-10 17:00:19 -0700669 ho->format = priv->format;
Devarsh Thakkarf5254cc2023-12-05 21:25:21 +0530670 }
671
Simon Glassb4928e52020-07-02 21:12:21 -0600672 if (IS_ENABLED(CONFIG_VIDEO_COPY) && plat->copy_base)
673 priv->copy_fb = map_sysmem(plat->copy_base, plat->size);
674
Simon Glass21320da2025-04-02 06:29:33 +1300675 priv->white_on_black = CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK);
676
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100677 /* Set up colors */
Simon Glass2b063b82018-11-06 15:21:36 -0700678 video_set_default_colors(dev, false);
Rob Clarkf1411882017-08-03 12:47:01 -0400679
680 if (!CONFIG_IS_ENABLED(NO_FB_CLEAR))
681 video_clear(dev);
Simon Glass623d28f2016-01-18 19:52:15 -0700682
Simon Glass84c7fb32016-01-18 19:52:17 -0700683 /*
Simon Glassb3a72b32016-01-14 18:10:48 -0700684 * Create a text console device. For now we always do this, although
Simon Glass84c7fb32016-01-18 19:52:17 -0700685 * it might be useful to support only bitmap drawing on the device
Simon Glassb3a72b32016-01-14 18:10:48 -0700686 * for boards that don't need to display text. We create a TrueType
687 * console if enabled, a rotated console if the video driver requests
688 * it, otherwise a normal console.
689 *
690 * The console can be override by setting vidconsole_drv_name before
691 * probing this video driver, or in the probe() method.
692 *
693 * TrueType does not support rotation at present so fall back to the
694 * rotated console in that case.
Simon Glass84c7fb32016-01-18 19:52:17 -0700695 */
Simon Glassb3a72b32016-01-14 18:10:48 -0700696 if (!priv->rot && IS_ENABLED(CONFIG_CONSOLE_TRUETYPE)) {
Simon Glass2ef353e2016-01-14 18:10:42 -0700697 snprintf(name, sizeof(name), "%s.vidconsole_tt", dev->name);
698 strcpy(drv, "vidconsole_tt");
699 } else {
700 snprintf(name, sizeof(name), "%s.vidconsole%d", dev->name,
701 priv->rot);
702 snprintf(drv, sizeof(drv), "vidconsole%d", priv->rot);
703 }
704
Simon Glass84c7fb32016-01-18 19:52:17 -0700705 str = strdup(name);
706 if (!str)
707 return -ENOMEM;
Simon Glassb3a72b32016-01-14 18:10:48 -0700708 if (priv->vidconsole_drv_name)
709 drv_name = priv->vidconsole_drv_name;
710 ret = device_bind_driver(dev, drv_name, str, &cons);
Simon Glass84c7fb32016-01-18 19:52:17 -0700711 if (ret) {
712 debug("%s: Cannot bind console driver\n", __func__);
713 return ret;
714 }
Simon Glassb3a72b32016-01-14 18:10:48 -0700715
Simon Glass84c7fb32016-01-18 19:52:17 -0700716 ret = device_probe(cons);
717 if (ret) {
718 debug("%s: Cannot probe console driver\n", __func__);
719 return ret;
720 }
721
Nikhil M Jain9e3301d2023-04-20 17:41:08 +0530722 if (CONFIG_IS_ENABLED(VIDEO_LOGO) &&
723 !CONFIG_IS_ENABLED(SPLASH_SCREEN) && !plat->hide_logo) {
Simon Glass87a3cd72021-11-19 13:24:03 -0700724 ret = show_splash(dev);
725 if (ret) {
726 log_debug("Cannot show splash screen\n");
727 return ret;
728 }
729 }
730
Simon Glasse0337a52024-07-31 08:44:10 -0600731 /* register cyclic as soon as the first video device is probed */
732 if (CONFIG_IS_ENABLED(CYCLIC) && (gd->flags && GD_FLG_RELOC) &&
733 !uc_priv->cyc_active) {
734 uint ms = CONFIG_IF_ENABLED_INT(CYCLIC, VIDEO_SYNC_CYCLIC_MS);
735
736 cyclic_register(&uc_priv->cyc, video_idle, ms * 1000,
737 "video_init");
738 uc_priv->cyc_active = true;
739 }
740
Simon Glass623d28f2016-01-18 19:52:15 -0700741 return 0;
742};
743
744/* Post-relocation, allocate memory for the frame buffer */
745static int video_post_bind(struct udevice *dev)
746{
Simon Glass951255d2020-07-02 21:12:32 -0600747 struct video_uc_priv *uc_priv;
748 ulong addr;
Simon Glass623d28f2016-01-18 19:52:15 -0700749 ulong size;
750
751 /* Before relocation there is nothing to do here */
Tom Rini6985a722018-04-22 09:47:48 -0400752 if (!(gd->flags & GD_FLG_RELOC))
Simon Glass623d28f2016-01-18 19:52:15 -0700753 return 0;
Simon Glass951255d2020-07-02 21:12:32 -0600754
755 /* Set up the video pointer, if this is the first device */
Simon Glass95588622020-12-22 19:30:28 -0700756 uc_priv = uclass_get_priv(dev->uclass);
Simon Glass951255d2020-07-02 21:12:32 -0600757 if (!uc_priv->video_ptr)
758 uc_priv->video_ptr = gd->video_top;
759
760 /* Allocate framebuffer space for this device */
761 addr = uc_priv->video_ptr;
Simon Glass623d28f2016-01-18 19:52:15 -0700762 size = alloc_fb(dev, &addr);
763 if (addr < gd->video_bottom) {
Bin Mengacbafdd2023-07-23 12:40:24 +0800764 /*
765 * Device tree node may need the 'bootph-all' or
Simon Glassfc1aa352023-02-13 08:56:34 -0700766 * 'bootph-some-ram' tag
Patrick Delaunayd1937182019-05-21 19:19:12 +0200767 */
Bin Mengacbafdd2023-07-23 12:40:24 +0800768 printf("Video device '%s' cannot allocate frame buffer memory "
769 "- ensure the device is set up before relocation\n",
Simon Glass623d28f2016-01-18 19:52:15 -0700770 dev->name);
771 return -ENOSPC;
772 }
773 debug("%s: Claiming %lx bytes at %lx for video device '%s'\n",
774 __func__, size, addr, dev->name);
Simon Glass951255d2020-07-02 21:12:32 -0600775 uc_priv->video_ptr = addr;
Simon Glass623d28f2016-01-18 19:52:15 -0700776
777 return 0;
778}
779
Simon Glasse0337a52024-07-31 08:44:10 -0600780__maybe_unused static int video_destroy(struct uclass *uc)
781{
782 struct video_uc_priv *uc_priv = uclass_get_priv(uc);
783
784 if (uc_priv->cyc_active) {
785 cyclic_unregister(&uc_priv->cyc);
786 uc_priv->cyc_active = false;
787 }
788
789 return 0;
790}
791
Simon Glass623d28f2016-01-18 19:52:15 -0700792UCLASS_DRIVER(video) = {
793 .id = UCLASS_VIDEO,
794 .name = "video",
795 .flags = DM_UC_FLAG_SEQ_ALIAS,
796 .post_bind = video_post_bind,
Simon Glass623d28f2016-01-18 19:52:15 -0700797 .post_probe = video_post_probe,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700798 .priv_auto = sizeof(struct video_uc_priv),
799 .per_device_auto = sizeof(struct video_priv),
Simon Glassb75b15b2020-12-03 16:55:23 -0700800 .per_device_plat_auto = sizeof(struct video_uc_plat),
Simon Glasse0337a52024-07-31 08:44:10 -0600801 CONFIG_IS_ENABLED(CYCLIC, (.destroy = video_destroy, ))
Simon Glass623d28f2016-01-18 19:52:15 -0700802};