xref: /openbmc/u-boot/drivers/video/video-uclass.c (revision 9649c5343fb1e105109f8aac499134dd4bd723ca)
1  // SPDX-License-Identifier: GPL-2.0+
2  /*
3   * Copyright (c) 2015 Google, Inc
4   */
5  
6  #include <common.h>
7  #include <dm.h>
8  #include <mapmem.h>
9  #include <stdio_dev.h>
10  #include <video.h>
11  #include <video_console.h>
12  #include <dm/lists.h>
13  #include <dm/device-internal.h>
14  #include <dm/uclass-internal.h>
15  #ifdef CONFIG_SANDBOX
16  #include <asm/sdl.h>
17  #endif
18  
19  /*
20   * Theory of operation:
21   *
22   * Before relocation each device is bound. The driver for each device must
23   * set the @align and @size values in struct video_uc_platdata. This
24   * information represents the requires size and alignment of the frame buffer
25   * for the device. The values can be an over-estimate but cannot be too
26   * small. The actual values will be suppled (in the same manner) by the bind()
27   * method after relocation.
28   *
29   * This information is then picked up by video_reserve() which works out how
30   * much memory is needed for all devices. This is allocated between
31   * gd->video_bottom and gd->video_top.
32   *
33   * After relocation the same process occurs. The driver supplies the same
34   * @size and @align information and this time video_post_bind() checks that
35   * the drivers does not overflow the allocated memory.
36   *
37   * The frame buffer address is actually set (to plat->base) in
38   * video_post_probe(). This function also clears the frame buffer and
39   * allocates a suitable text console device. This can then be used to write
40   * text to the video device.
41   */
42  DECLARE_GLOBAL_DATA_PTR;
43  
video_set_flush_dcache(struct udevice * dev,bool flush)44  void video_set_flush_dcache(struct udevice *dev, bool flush)
45  {
46  	struct video_priv *priv = dev_get_uclass_priv(dev);
47  
48  	priv->flush_dcache = flush;
49  }
50  
alloc_fb(struct udevice * dev,ulong * addrp)51  static ulong alloc_fb(struct udevice *dev, ulong *addrp)
52  {
53  	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
54  	ulong base, align, size;
55  
56  	if (!plat->size)
57  		return 0;
58  
59  	align = plat->align ? plat->align : 1 << 20;
60  	base = *addrp - plat->size;
61  	base &= ~(align - 1);
62  	plat->base = base;
63  	size = *addrp - base;
64  	*addrp = base;
65  
66  	return size;
67  }
68  
video_reserve(ulong * addrp)69  int video_reserve(ulong *addrp)
70  {
71  	struct udevice *dev;
72  	ulong size;
73  
74  	gd->video_top = *addrp;
75  	for (uclass_find_first_device(UCLASS_VIDEO, &dev);
76  	     dev;
77  	     uclass_find_next_device(&dev)) {
78  		size = alloc_fb(dev, addrp);
79  		debug("%s: Reserving %lx bytes at %lx for video device '%s'\n",
80  		      __func__, size, *addrp, dev->name);
81  	}
82  	gd->video_bottom = *addrp;
83  	debug("Video frame buffers from %lx to %lx\n", gd->video_bottom,
84  	      gd->video_top);
85  
86  	return 0;
87  }
88  
video_clear(struct udevice * dev)89  int video_clear(struct udevice *dev)
90  {
91  	struct video_priv *priv = dev_get_uclass_priv(dev);
92  
93  	switch (priv->bpix) {
94  	case VIDEO_BPP16: {
95  		u16 *ppix = priv->fb;
96  		u16 *end = priv->fb + priv->fb_size;
97  
98  		while (ppix < end)
99  			*ppix++ = priv->colour_bg;
100  		break;
101  	}
102  	case VIDEO_BPP32: {
103  		u32 *ppix = priv->fb;
104  		u32 *end = priv->fb + priv->fb_size;
105  
106  		while (ppix < end)
107  			*ppix++ = priv->colour_bg;
108  		break;
109  	}
110  	default:
111  		memset(priv->fb, priv->colour_bg, priv->fb_size);
112  		break;
113  	}
114  
115  	return 0;
116  }
117  
video_set_default_colors(struct udevice * dev,bool invert)118  void video_set_default_colors(struct udevice *dev, bool invert)
119  {
120  	struct video_priv *priv = dev_get_uclass_priv(dev);
121  	int fore, back;
122  
123  #ifdef CONFIG_SYS_WHITE_ON_BLACK
124  	/* White is used when switching to bold, use light gray here */
125  	fore = VID_LIGHT_GRAY;
126  	back = VID_BLACK;
127  #else
128  	fore = VID_BLACK;
129  	back = VID_WHITE;
130  #endif
131  	if (invert) {
132  		int temp;
133  
134  		temp = fore;
135  		fore = back;
136  		back = temp;
137  	}
138  	priv->fg_col_idx = fore;
139  	priv->colour_fg = vid_console_color(priv, fore);
140  	priv->colour_bg = vid_console_color(priv, back);
141  }
142  
143  /* Flush video activity to the caches */
video_sync(struct udevice * vid,bool force)144  void video_sync(struct udevice *vid, bool force)
145  {
146  	/*
147  	 * flush_dcache_range() is declared in common.h but it seems that some
148  	 * architectures do not actually implement it. Is there a way to find
149  	 * out whether it exists? For now, ARM is safe.
150  	 */
151  #if defined(CONFIG_ARM) && !defined(CONFIG_SYS_DCACHE_OFF)
152  	struct video_priv *priv = dev_get_uclass_priv(vid);
153  
154  	if (priv->flush_dcache) {
155  		flush_dcache_range((ulong)priv->fb,
156  				   ALIGN((ulong)priv->fb + priv->fb_size,
157  					 CONFIG_SYS_CACHELINE_SIZE));
158  	}
159  #elif defined(CONFIG_VIDEO_SANDBOX_SDL)
160  	struct video_priv *priv = dev_get_uclass_priv(vid);
161  	static ulong last_sync;
162  
163  	if (force || get_timer(last_sync) > 10) {
164  		sandbox_sdl_sync(priv->fb);
165  		last_sync = get_timer(0);
166  	}
167  #endif
168  }
169  
video_sync_all(void)170  void video_sync_all(void)
171  {
172  	struct udevice *dev;
173  
174  	for (uclass_find_first_device(UCLASS_VIDEO, &dev);
175  	     dev;
176  	     uclass_find_next_device(&dev)) {
177  		if (device_active(dev))
178  			video_sync(dev, true);
179  	}
180  }
181  
video_get_xsize(struct udevice * dev)182  int video_get_xsize(struct udevice *dev)
183  {
184  	struct video_priv *priv = dev_get_uclass_priv(dev);
185  
186  	return priv->xsize;
187  }
188  
video_get_ysize(struct udevice * dev)189  int video_get_ysize(struct udevice *dev)
190  {
191  	struct video_priv *priv = dev_get_uclass_priv(dev);
192  
193  	return priv->ysize;
194  }
195  
196  /* Set up the colour map */
video_pre_probe(struct udevice * dev)197  static int video_pre_probe(struct udevice *dev)
198  {
199  	struct video_priv *priv = dev_get_uclass_priv(dev);
200  
201  	priv->cmap = calloc(256, sizeof(ushort));
202  	if (!priv->cmap)
203  		return -ENOMEM;
204  
205  	return 0;
206  }
207  
video_pre_remove(struct udevice * dev)208  static int video_pre_remove(struct udevice *dev)
209  {
210  	struct video_priv *priv = dev_get_uclass_priv(dev);
211  
212  	free(priv->cmap);
213  
214  	return 0;
215  }
216  
217  /* Set up the display ready for use */
video_post_probe(struct udevice * dev)218  static int video_post_probe(struct udevice *dev)
219  {
220  	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
221  	struct video_priv *priv = dev_get_uclass_priv(dev);
222  	char name[30], drv[15], *str;
223  	const char *drv_name = drv;
224  	struct udevice *cons;
225  	int ret;
226  
227  	/* Set up the line and display size */
228  	priv->fb = map_sysmem(plat->base, plat->size);
229  	if (!priv->line_length)
230  		priv->line_length = priv->xsize * VNBYTES(priv->bpix);
231  
232  	priv->fb_size = priv->line_length * priv->ysize;
233  
234  	/* Set up colors  */
235  	video_set_default_colors(dev, false);
236  
237  	if (!CONFIG_IS_ENABLED(NO_FB_CLEAR))
238  		video_clear(dev);
239  
240  	/*
241  	 * Create a text console device. For now we always do this, although
242  	 * it might be useful to support only bitmap drawing on the device
243  	 * for boards that don't need to display text. We create a TrueType
244  	 * console if enabled, a rotated console if the video driver requests
245  	 * it, otherwise a normal console.
246  	 *
247  	 * The console can be override by setting vidconsole_drv_name before
248  	 * probing this video driver, or in the probe() method.
249  	 *
250  	 * TrueType does not support rotation at present so fall back to the
251  	 * rotated console in that case.
252  	 */
253  	if (!priv->rot && IS_ENABLED(CONFIG_CONSOLE_TRUETYPE)) {
254  		snprintf(name, sizeof(name), "%s.vidconsole_tt", dev->name);
255  		strcpy(drv, "vidconsole_tt");
256  	} else {
257  		snprintf(name, sizeof(name), "%s.vidconsole%d", dev->name,
258  			 priv->rot);
259  		snprintf(drv, sizeof(drv), "vidconsole%d", priv->rot);
260  	}
261  
262  	str = strdup(name);
263  	if (!str)
264  		return -ENOMEM;
265  	if (priv->vidconsole_drv_name)
266  		drv_name = priv->vidconsole_drv_name;
267  	ret = device_bind_driver(dev, drv_name, str, &cons);
268  	if (ret) {
269  		debug("%s: Cannot bind console driver\n", __func__);
270  		return ret;
271  	}
272  
273  	ret = device_probe(cons);
274  	if (ret) {
275  		debug("%s: Cannot probe console driver\n", __func__);
276  		return ret;
277  	}
278  
279  	return 0;
280  };
281  
282  /* Post-relocation, allocate memory for the frame buffer */
video_post_bind(struct udevice * dev)283  static int video_post_bind(struct udevice *dev)
284  {
285  	ulong addr = gd->video_top;
286  	ulong size;
287  
288  	/* Before relocation there is nothing to do here */
289  	if (!(gd->flags & GD_FLG_RELOC))
290  		return 0;
291  	size = alloc_fb(dev, &addr);
292  	if (addr < gd->video_bottom) {
293  		/* Device tree node may need the 'u-boot,dm-pre-reloc' tag */
294  		printf("Video device '%s' cannot allocate frame buffer memory -ensure the device is set up before relocation\n",
295  		       dev->name);
296  		return -ENOSPC;
297  	}
298  	debug("%s: Claiming %lx bytes at %lx for video device '%s'\n",
299  	      __func__, size, addr, dev->name);
300  	gd->video_bottom = addr;
301  
302  	return 0;
303  }
304  
305  UCLASS_DRIVER(video) = {
306  	.id		= UCLASS_VIDEO,
307  	.name		= "video",
308  	.flags		= DM_UC_FLAG_SEQ_ALIAS,
309  	.post_bind	= video_post_bind,
310  	.pre_probe	= video_pre_probe,
311  	.post_probe	= video_post_probe,
312  	.pre_remove	= video_pre_remove,
313  	.per_device_auto_alloc_size	= sizeof(struct video_priv),
314  	.per_device_platdata_auto_alloc_size = sizeof(struct video_uc_platdata),
315  };
316