diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index caf1efcbb3..946d958f9f 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -4,6 +4,46 @@ menu "Graphics support" +config DM_VIDEO + bool "Enable driver model support for LCD/video" + depends on DM + help + This enables driver model for LCD and video devices. These support + a bitmap display of various sizes and depths which can be drawn on + to display a command-line console or splash screen. Enabling this + option compiles in the video uclass and routes all LCD/video access + through this. + +config VIDEO_BPP8 + bool "Support 8-bit-per-pixel displays" + depends on DM_VIDEO + default y if DM_VIDEO + help + Support drawing text and bitmaps onto a 8-bit-per-pixel display. + Enabling this will include code to support this display. Without + this option, such displays will not be supported and console output + will be empty. + +config VIDEO_BPP16 + bool "Support 16-bit-per-pixel displays" + depends on DM_VIDEO + default y if DM_VIDEO + help + Support drawing text and bitmaps onto a 16-bit-per-pixel display. + Enabling this will include code to support this display. Without + this option, such displays will not be supported and console output + will be empty. + +config VIDEO_BPP32 + bool "Support 32-bit-per-pixel displays" + depends on DM_VIDEO + default y if DM_VIDEO + help + Support drawing text and bitmaps onto a 32-bit-per-pixel display. + Enabling this will include code to support this display. Without + this option, such displays will not be supported and console output + will be empty. + config VIDEO_VESA bool "Enable VESA video driver support" default n diff --git a/drivers/video/Makefile b/drivers/video/Makefile index e85fd8a677..71920130e4 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -7,6 +7,7 @@ ifdef CONFIG_DM obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o +obj-$(CONFIG_DM_VIDEO) += video-uclass.o endif obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c new file mode 100644 index 0000000000..1615889626 --- /dev/null +++ b/drivers/video/video-uclass.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SANDBOX +#include +#endif + +/* + * Theory of operation: + * + * Before relocation each device is bound. The driver for each device must + * set the @align and @size values in struct video_uc_platdata. This + * information represents the requires size and alignment of the frame buffer + * for the device. The values can be an over-estimate but cannot be too + * small. The actual values will be suppled (in the same manner) by the bind() + * method after relocation. + * + * This information is then picked up by video_reserve() which works out how + * much memory is needed for all devices. This is allocated between + * gd->video_bottom and gd->video_top. + * + * After relocation the same process occurs. The driver supplies the same + * @size and @align information and this time video_post_bind() checks that + * the drivers does not overflow the allocated memory. + * + * The frame buffer address is actually set (to plat->base) in + * video_post_probe(). This function also clears the frame buffer and + * allocates a suitable text console device. This can then be used to write + * text to the video device. + */ +DECLARE_GLOBAL_DATA_PTR; + +static ulong alloc_fb(struct udevice *dev, ulong *addrp) +{ + struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); + ulong base, align, size; + + align = plat->align ? plat->align : 1 << 20; + base = *addrp - plat->size; + base &= ~(align - 1); + plat->base = base; + size = *addrp - base; + *addrp = base; + + return size; +} + +int video_reserve(ulong *addrp) +{ + struct udevice *dev; + ulong size; + + gd->video_top = *addrp; + for (uclass_find_first_device(UCLASS_VIDEO, &dev); + dev; + uclass_find_next_device(&dev)) { + size = alloc_fb(dev, addrp); + debug("%s: Reserving %lx bytes at %lx for video device '%s'\n", + __func__, size, *addrp, dev->name); + } + gd->video_bottom = *addrp; + debug("Video frame buffers from %lx to %lx\n", gd->video_bottom, + gd->video_top); + + return 0; +} + +static int video_clear(struct udevice *dev) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + + if (priv->bpix == VIDEO_BPP32) { + u32 *ppix = priv->fb; + u32 *end = priv->fb + priv->fb_size; + + while (ppix < end) + *ppix++ = priv->colour_bg; + } else { + memset(priv->fb, priv->colour_bg, priv->fb_size); + } + + return 0; +} + +/* Flush video activity to the caches */ +void video_sync(struct udevice *vid) +{ + /* + * flush_dcache_range() is declared in common.h but it seems that some + * architectures do not actually implement it. Is there a way to find + * out whether it exists? For now, ARM is safe. + */ +#if defined(CONFIG_ARM) && !defined(CONFIG_SYS_DCACHE_OFF) + struct video_priv *priv = dev_get_uclass_priv(vid); + + if (priv->flush_dcache) { + flush_dcache_range((ulong)priv->fb, + (ulong)priv->fb + priv->fb_size); + } +#elif defined(CONFIG_VIDEO_SANDBOX_SDL) + struct video_priv *priv = dev_get_uclass_priv(vid); + static ulong last_sync; + + if (get_timer(last_sync) > 10) { + sandbox_sdl_sync(priv->fb); + last_sync = get_timer(0); + } +#endif +} + +void video_sync_all(void) +{ + struct udevice *dev; + + for (uclass_find_first_device(UCLASS_VIDEO, &dev); + dev; + uclass_find_next_device(&dev)) { + if (device_active(dev)) + video_sync(dev); + } +} + +int video_get_xsize(struct udevice *dev) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + + return priv->xsize; +} + +int video_get_ysize(struct udevice *dev) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + + return priv->ysize; +} + +/* Set up the colour map */ +static int video_pre_probe(struct udevice *dev) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + + priv->cmap = calloc(256, sizeof(ushort)); + if (!priv->cmap) + return -ENOMEM; + + return 0; +} + +static int video_pre_remove(struct udevice *dev) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + + free(priv->cmap); + + return 0; +} + +/* Set up the display ready for use */ +static int video_post_probe(struct udevice *dev) +{ + struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); + struct video_priv *priv = dev_get_uclass_priv(dev); + char name[30], drv[15], *str; + struct udevice *cons; + int ret; + + /* Set up the line and display size */ + priv->fb = map_sysmem(plat->base, plat->size); + priv->line_length = priv->xsize * VNBYTES(priv->bpix); + priv->fb_size = priv->line_length * priv->ysize; + + /* Set up colours - we could in future support other colours */ +#ifdef CONFIG_SYS_WHITE_ON_BLACK + priv->colour_fg = 0xffffff; +#else + priv->colour_bg = 0xffffff; +#endif + video_clear(dev); + + return 0; +}; + +/* Post-relocation, allocate memory for the frame buffer */ +static int video_post_bind(struct udevice *dev) +{ + ulong addr = gd->video_top; + ulong size; + + /* Before relocation there is nothing to do here */ + if ((!gd->flags & GD_FLG_RELOC)) + return 0; + size = alloc_fb(dev, &addr); + if (addr < gd->video_bottom) { + /* Device tree node may need the 'u-boot,dm-pre-reloc' tag */ + printf("Video device '%s' cannot allocate frame buffer memory -ensure the device is set up before relocation\n", + dev->name); + return -ENOSPC; + } + debug("%s: Claiming %lx bytes at %lx for video device '%s'\n", + __func__, size, addr, dev->name); + gd->video_bottom = addr; + + return 0; +} + +UCLASS_DRIVER(video) = { + .id = UCLASS_VIDEO, + .name = "video", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .post_bind = video_post_bind, + .pre_probe = video_pre_probe, + .post_probe = video_post_probe, + .pre_remove = video_pre_remove, + .per_device_auto_alloc_size = sizeof(struct video_priv), + .per_device_platdata_auto_alloc_size = sizeof(struct video_uc_platdata), +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 27fa0b68db..3934375fff 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -66,6 +66,7 @@ enum uclass_id { UCLASS_USB, /* USB bus */ UCLASS_USB_DEV_GENERIC, /* USB generic device */ UCLASS_USB_HUB, /* USB hub */ + UCLASS_VIDEO, /* Video or LCD device */ UCLASS_VIDEO_BRIDGE, /* Video bridge, e.g. DisplayPort to LVDS */ UCLASS_COUNT, diff --git a/include/video.h b/include/video.h index 65e4ec1e1a..b20f06f335 100644 --- a/include/video.h +++ b/include/video.h @@ -1,14 +1,167 @@ /* -** MPC823 Video Controller -** ======================= -** (C) 2000 by Paolo Scaffardi (arsenio@tin.it) -** AIRVENT SAM s.p.a - RIMINI(ITALY) -** -*/ + * Video uclass and legacy implementation + * + * Copyright (c) 2015 Google, Inc + * + * MPC823 Video Controller + * ======================= + * (C) 2000 by Paolo Scaffardi (arsenio@tin.it) + * AIRVENT SAM s.p.a - RIMINI(ITALY) + * + */ #ifndef _VIDEO_H_ #define _VIDEO_H_ +#ifdef CONFIG_DM_VIDEO + +#include + +struct video_uc_platdata { + uint align; + uint size; + ulong base; +}; + +/* + * Bits per pixel selector. Each value n is such that the bits-per-pixel is + * 2 ^ n + */ +enum video_log2_bpp { + VIDEO_BPP1 = 0, + VIDEO_BPP2, + VIDEO_BPP4, + VIDEO_BPP8, + VIDEO_BPP16, + VIDEO_BPP32, +}; + +/* + * Convert enum video_log2_bpp to bytes and bits. Note we omit the outer + * brackets to allow multiplication by fractional pixels. + */ +#define VNBYTES(bpix) (1 << (bpix)) / 8 + +#define VNBITS(bpix) (1 << (bpix)) + +/** + * struct video_priv - Device information used by the video uclass + * + * @xsize: Number of pixel columns (e.g. 1366) + * @ysize: Number of pixels rows (e.g.. 768) + * @tor: Display rotation (0=none, 1=90 degrees clockwise, etc.) + * @bpix: Encoded bits per pixel + * @fb: Frame buffer + * @fb_size: Frame buffer size + * @line_length: Length of each frame buffer line, in bytes + * @colour_fg: Foreground colour (pixel value) + * @colour_bg: Background colour (pixel value) + * @flush_dcache: true to enable flushing of the data cache after + * the LCD is updated + * @cmap: Colour map for 8-bit-per-pixel displays + */ +struct video_priv { + /* Things set up by the driver: */ + ushort xsize; + ushort ysize; + ushort rot; + enum video_log2_bpp bpix; + + /* + * Things that are private to the uclass: don't use these in the + * driver + */ + void *fb; + int fb_size; + int line_length; + int colour_fg; + int colour_bg; + bool flush_dcache; + ushort *cmap; +}; + +/* Placeholder - there are no video operations at present */ +struct video_ops { +}; + +#define video_get_ops(dev) ((struct video_ops *)(dev)->driver->ops) + +/** + * video_reserve() - Reserve frame-buffer memory for video devices + * + * Note: This function is for internal use. + * + * This uses the uclass platdata's @size and @align members to figure out + * a size and position for each frame buffer as part of the pre-relocation + * process of determining the post-relocation memory layout. + * + * gd->video_top is set to the initial value of *@addrp and gd->video_bottom + * is set to the final value. + * + * @addrp: On entry, the top of available memory. On exit, the new top, + * after allocating the required memory. + * @return 0 + */ +int video_reserve(ulong *addrp); + +/** + * video_sync() - Sync a device's frame buffer with its hardware + * + * Some frame buffers are cached or have a secondary frame buffer. This + * function syncs these up so that the current contents of the U-Boot frame + * buffer are displayed to the user. + * + * @dev: Device to sync + */ +void video_sync(struct udevice *vid); + +/** + * video_sync_all() - Sync all devices' frame buffers with there hardware + * + * This calls video_sync() on all active video devices. + */ +void video_sync_all(void); + +/** + * video_bmp_display() - Display a BMP file + * + * @dev: Device to display the bitmap on + * @bmp_image: Address of bitmap image to display + * @x: X position in pixels from the left + * @y: Y position in pixels from the top + * @align: true to adjust the coordinates to centre the image. If false + * the coordinates are used as is. If true: + * + * - if a coordinate is 0x7fff then the image will be centred in + * that direction + * - if a coordinate is -ve then it will be offset to the + * left/top of the centre by that many pixels + * - if a coordinate is positive it will be used unchnaged. + * @return 0 if OK, -ve on error + */ +int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, + bool align); + +/** + * video_get_xsize() - Get the width of the display in pixels + * + * @dev: Device to check + * @return device frame buffer width in pixels + */ +int video_get_xsize(struct udevice *dev); + +/** + * video_get_ysize() - Get the height of the display in pixels + * + * @dev: Device to check + * @return device frame buffer height in pixels + */ +int video_get_ysize(struct udevice *dev); + +#endif /* CONFIG_DM_VIDEO */ + +#ifndef CONFIG_DM_VIDEO + /* Video functions */ struct stdio_dev; @@ -73,4 +226,7 @@ int kwh043st20_f01_spi_startup(unsigned int bus, unsigned int cs, int lg4573_spi_startup(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int spi_mode); #endif + +#endif /* CONFIG_DM_VIDEO */ + #endif