video: Add helpers for decoding screen_info
authorThomas Zimmermann <tzimmermann@suse.de>
Mon, 12 Feb 2024 09:06:09 +0000 (10:06 +0100)
committerThomas Zimmermann <tzimmermann@suse.de>
Wed, 14 Feb 2024 09:09:13 +0000 (10:09 +0100)
The plain values as stored in struct screen_info need to be decoded
before being used. Add helpers that decode the type of video output
and the framebuffer I/O aperture.

Old or non-x86 systems may not set the type of video directly, but
only indicate the presence by storing 0x01 in orig_video_isVGA. The
decoding logic in screen_info_video_type() takes this into account.
It then follows similar code in vgacon's vgacon_startup() to detect
the video type from the given values.

A call to screen_info_resources() returns all known resources of the
given screen_info. The resources' values have been taken from existing
code in vgacon and vga16fb. These drivers can later be converted to
use the new interfaces.

v2:
* return ssize_t from screen_info_resources()
* don't call __screen_info_has_lfb() unnecessarily

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240212090736.11464-2-tzimmermann@suse.de
drivers/firmware/Kconfig
drivers/video/Kconfig
drivers/video/Makefile
drivers/video/screen_info_generic.c [new file with mode: 0644]
include/linux/screen_info.h

index afd38539b92e51b36da4c54b1913001445e624e0..71d8b26c4103b9cdb668c2e8eb1b2e519d9823c8 100644 (file)
@@ -182,6 +182,7 @@ config MTK_ADSP_IPC
 config SYSFB
        bool
        select BOOT_VESA_SUPPORT
+       select SCREEN_INFO
 
 config SYSFB_SIMPLEFB
        bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
index 130ebccb833804588f5f54a5d4cb7f16e995d7f8..44c9ef1435a2d2413d3a65abfe9c2165fb26a4c5 100644 (file)
@@ -11,6 +11,10 @@ config APERTURE_HELPERS
          Support tracking and hand-over of aperture ownership. Required
          by graphics drivers for firmware-provided framebuffers.
 
+config SCREEN_INFO
+       bool
+       default n
+
 config STI_CORE
        bool
        depends on PARISC
index 9eb5557911de566fbcdae48b37748bda77ab4c4f..117cbdbb58c2cbc65c187b2aab471cc221fe4eaf 100644 (file)
@@ -1,11 +1,14 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_APERTURE_HELPERS)    += aperture.o
+obj-$(CONFIG_SCREEN_INFO)         += screen_info.o
 obj-$(CONFIG_STI_CORE)            += sticore.o
 obj-$(CONFIG_VGASTATE)            += vgastate.o
 obj-$(CONFIG_VIDEO)               += cmdline.o nomodeset.o
 obj-$(CONFIG_HDMI)                += hdmi.o
 
+screen_info-y                    := screen_info_generic.o
+
 obj-$(CONFIG_VT)                 += console/
 obj-$(CONFIG_FB_STI)             += console/
 obj-$(CONFIG_LOGO)               += logo/
diff --git a/drivers/video/screen_info_generic.c b/drivers/video/screen_info_generic.c
new file mode 100644 (file)
index 0000000..64117c6
--- /dev/null
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/export.h>
+#include <linux/ioport.h>
+#include <linux/screen_info.h>
+#include <linux/string.h>
+
+static void resource_init_named(struct resource *r,
+                               resource_size_t start, resource_size_t size,
+                               const char *name, unsigned int flags)
+{
+       memset(r, 0, sizeof(*r));
+
+       r->start = start;
+       r->end = start + size - 1;
+       r->name = name;
+       r->flags = flags;
+}
+
+static void resource_init_io_named(struct resource *r,
+                                  resource_size_t start, resource_size_t size,
+                                  const char *name)
+{
+       resource_init_named(r, start, size, name, IORESOURCE_IO);
+}
+
+static void resource_init_mem_named(struct resource *r,
+                                  resource_size_t start, resource_size_t size,
+                                  const char *name)
+{
+       resource_init_named(r, start, size, name, IORESOURCE_MEM);
+}
+
+static inline bool __screen_info_has_ega_gfx(unsigned int mode)
+{
+       switch (mode) {
+       case 0x0d:      /* 320x200-4 */
+       case 0x0e:      /* 640x200-4 */
+       case 0x0f:      /* 640x350-1 */
+       case 0x10:      /* 640x350-4 */
+               return true;
+       default:
+               return false;
+       }
+}
+
+static inline bool __screen_info_has_vga_gfx(unsigned int mode)
+{
+       switch (mode) {
+       case 0x10:      /* 640x480-1 */
+       case 0x12:      /* 640x480-4 */
+       case 0x13:      /* 320-200-8 */
+       case 0x6a:      /* 800x600-4 (VESA) */
+               return true;
+       default:
+               return __screen_info_has_ega_gfx(mode);
+       }
+}
+
+/**
+ * screen_info_resources() - Get resources from screen_info structure
+ * @si: the screen_info
+ * @r: pointer to an array of resource structures
+ * @num: number of elements in @r:
+ *
+ * Returns:
+ * The number of resources stored in @r on success, or a negative errno code otherwise.
+ *
+ * A call to screen_info_resources() returns the resources consumed by the
+ * screen_info's device or framebuffer. The result is stored in the caller-supplied
+ * array @r with up to @num elements. The function returns the number of
+ * initialized elements.
+ */
+ssize_t screen_info_resources(const struct screen_info *si, struct resource *r, size_t num)
+{
+       struct resource *pos = r;
+       unsigned int type = screen_info_video_type(si);
+       u64 base, size;
+
+       switch (type) {
+       case VIDEO_TYPE_MDA:
+               if (num > 0)
+                       resource_init_io_named(pos++, 0x3b0, 12, "mda");
+               if (num > 1)
+                       resource_init_io_named(pos++, 0x3bf, 0x01, "mda");
+               if (num > 2)
+                       resource_init_mem_named(pos++, 0xb0000, 0x2000, "mda");
+               break;
+       case VIDEO_TYPE_CGA:
+               if (num > 0)
+                       resource_init_io_named(pos++, 0x3d4, 0x02, "cga");
+               if (num > 1)
+                       resource_init_mem_named(pos++, 0xb8000, 0x2000, "cga");
+               break;
+       case VIDEO_TYPE_EGAM:
+               if (num > 0)
+                       resource_init_io_named(pos++, 0x3bf, 0x10, "ega");
+               if (num > 1)
+                       resource_init_mem_named(pos++, 0xb0000, 0x8000, "ega");
+               break;
+       case VIDEO_TYPE_EGAC:
+               if (num > 0)
+                       resource_init_io_named(pos++, 0x3c0, 0x20, "ega");
+               if (num > 1) {
+                       if (__screen_info_has_ega_gfx(si->orig_video_mode))
+                               resource_init_mem_named(pos++, 0xa0000, 0x10000, "ega");
+                       else
+                               resource_init_mem_named(pos++, 0xb8000, 0x8000, "ega");
+               }
+               break;
+       case VIDEO_TYPE_VGAC:
+               if (num > 0)
+                       resource_init_io_named(pos++, 0x3c0, 0x20, "vga+");
+               if (num > 1) {
+                       if (__screen_info_has_vga_gfx(si->orig_video_mode))
+                               resource_init_mem_named(pos++, 0xa0000, 0x10000, "vga+");
+                       else
+                               resource_init_mem_named(pos++, 0xb8000, 0x8000, "vga+");
+               }
+               break;
+       case VIDEO_TYPE_VLFB:
+       case VIDEO_TYPE_EFI:
+               base = __screen_info_lfb_base(si);
+               if (!base)
+                       break;
+               size = __screen_info_lfb_size(si, type);
+               if (!size)
+                       break;
+               if (num > 0)
+                       resource_init_mem_named(pos++, base, size, "lfb");
+               break;
+       case VIDEO_TYPE_PICA_S3:
+       case VIDEO_TYPE_MIPS_G364:
+       case VIDEO_TYPE_SGI:
+       case VIDEO_TYPE_TGAC:
+       case VIDEO_TYPE_SUN:
+       case VIDEO_TYPE_SUNPCI:
+       case VIDEO_TYPE_PMAC:
+       default:
+               /* not supported */
+               return -EINVAL;
+       }
+
+       return pos - r;
+}
+EXPORT_SYMBOL(screen_info_resources);
index eab7081392d502ca58e99aad2498cce9c6253ea6..e7a02c5609d1259411c0b1545665e001f7d16053 100644 (file)
@@ -4,6 +4,106 @@
 
 #include <uapi/linux/screen_info.h>
 
+/**
+ * SCREEN_INFO_MAX_RESOURCES - maximum number of resources per screen_info
+ */
+#define SCREEN_INFO_MAX_RESOURCES      3
+
+struct resource;
+
+static inline bool __screen_info_has_lfb(unsigned int type)
+{
+       return (type == VIDEO_TYPE_VLFB) || (type == VIDEO_TYPE_EFI);
+}
+
+static inline u64 __screen_info_lfb_base(const struct screen_info *si)
+{
+       u64 lfb_base = si->lfb_base;
+
+       if (si->capabilities & VIDEO_CAPABILITY_64BIT_BASE)
+               lfb_base |= (u64)si->ext_lfb_base << 32;
+
+       return lfb_base;
+}
+
+static inline u64 __screen_info_lfb_size(const struct screen_info *si, unsigned int type)
+{
+       u64 lfb_size = si->lfb_size;
+
+       if (type == VIDEO_TYPE_VLFB)
+               lfb_size <<= 16;
+       return lfb_size;
+}
+
+static inline unsigned int __screen_info_video_type(unsigned int type)
+{
+       switch (type) {
+       case VIDEO_TYPE_MDA:
+       case VIDEO_TYPE_CGA:
+       case VIDEO_TYPE_EGAM:
+       case VIDEO_TYPE_EGAC:
+       case VIDEO_TYPE_VGAC:
+       case VIDEO_TYPE_VLFB:
+       case VIDEO_TYPE_PICA_S3:
+       case VIDEO_TYPE_MIPS_G364:
+       case VIDEO_TYPE_SGI:
+       case VIDEO_TYPE_TGAC:
+       case VIDEO_TYPE_SUN:
+       case VIDEO_TYPE_SUNPCI:
+       case VIDEO_TYPE_PMAC:
+       case VIDEO_TYPE_EFI:
+               return type;
+       default:
+               return 0;
+       }
+}
+
+/**
+ * screen_info_video_type() - Decodes the video type from struct screen_info
+ * @si: an instance of struct screen_info
+ *
+ * Returns:
+ * A VIDEO_TYPE_ constant representing si's type of video display, or 0 otherwise.
+ */
+static inline unsigned int screen_info_video_type(const struct screen_info *si)
+{
+       unsigned int type;
+
+       // check if display output is on
+       if (!si->orig_video_isVGA)
+               return 0;
+
+       // check for a known VIDEO_TYPE_ constant
+       type = __screen_info_video_type(si->orig_video_isVGA);
+       if (type)
+               return si->orig_video_isVGA;
+
+       // check if text mode has been initialized
+       if (!si->orig_video_lines || !si->orig_video_cols)
+               return 0;
+
+       // 80x25 text, mono
+       if (si->orig_video_mode == 0x07) {
+               if ((si->orig_video_ega_bx & 0xff) != 0x10)
+                       return VIDEO_TYPE_EGAM;
+               else
+                       return VIDEO_TYPE_MDA;
+       }
+
+       // EGA/VGA, 16 colors
+       if ((si->orig_video_ega_bx & 0xff) != 0x10) {
+               if (si->orig_video_isVGA)
+                       return VIDEO_TYPE_VGAC;
+               else
+                       return VIDEO_TYPE_EGAC;
+       }
+
+       // the rest...
+       return VIDEO_TYPE_CGA;
+}
+
+ssize_t screen_info_resources(const struct screen_info *si, struct resource *r, size_t num);
+
 extern struct screen_info screen_info;
 
 #endif /* _SCREEN_INFO_H */