summaryrefslogtreecommitdiff
path: root/drivers/video/screen_info_generic.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/screen_info_generic.c')
-rw-r--r--drivers/video/screen_info_generic.c91
1 files changed, 91 insertions, 0 deletions
diff --git a/drivers/video/screen_info_generic.c b/drivers/video/screen_info_generic.c
index 64117c6367ab..763adbba71cb 100644
--- a/drivers/video/screen_info_generic.c
+++ b/drivers/video/screen_info_generic.c
@@ -5,6 +5,8 @@
#include <linux/screen_info.h>
#include <linux/string.h>
+#include <video/pixel_format.h>
+
static void resource_init_named(struct resource *r,
resource_size_t start, resource_size_t size,
const char *name, unsigned int flags)
@@ -144,3 +146,92 @@ ssize_t screen_info_resources(const struct screen_info *si, struct resource *r,
return pos - r;
}
EXPORT_SYMBOL(screen_info_resources);
+
+/*
+ * The meaning of depth and bpp for direct-color formats is
+ * inconsistent:
+ *
+ * - DRM format info specifies depth as the number of color
+ * bits; including alpha, but not including filler bits.
+ * - Linux' EFI platform code computes lfb_depth from the
+ * individual color channels, including the reserved bits.
+ * - VBE 1.1 defines lfb_depth for XRGB1555 as 16, but later
+ * versions use 15.
+ * - On the kernel command line, 'bpp' of 32 is usually
+ * XRGB8888 including the filler bits, but 15 is XRGB1555
+ * not including the filler bit.
+ *
+ * It is not easily possible to fix this in struct screen_info,
+ * as this could break UAPI. The best solution is to compute
+ * bits_per_pixel from the color bits, reserved bits and
+ * reported lfb_depth, whichever is highest.
+ */
+
+u32 __screen_info_lfb_bits_per_pixel(const struct screen_info *si)
+{
+ u32 bits_per_pixel = si->lfb_depth;
+
+ if (bits_per_pixel > 8) {
+ bits_per_pixel = max(max3(si->red_size + si->red_pos,
+ si->green_size + si->green_pos,
+ si->blue_size + si->blue_pos),
+ si->rsvd_size + si->rsvd_pos);
+ bits_per_pixel = max_t(u32, bits_per_pixel, si->lfb_depth);
+ }
+
+ return bits_per_pixel;
+}
+EXPORT_SYMBOL(__screen_info_lfb_bits_per_pixel);
+
+static int __screen_info_lfb_pixel_format(const struct screen_info *si, struct pixel_format *f)
+{
+ u32 bits_per_pixel = __screen_info_lfb_bits_per_pixel(si);
+
+ if (bits_per_pixel > U8_MAX)
+ return -EINVAL;
+
+ f->bits_per_pixel = bits_per_pixel;
+
+ if (si->lfb_depth > 8) {
+ f->indexed = false;
+ f->alpha.offset = 0;
+ f->alpha.length = 0;
+ f->red.offset = si->red_pos;
+ f->red.length = si->red_size;
+ f->green.offset = si->green_pos;
+ f->green.length = si->green_size;
+ f->blue.offset = si->blue_pos;
+ f->blue.length = si->blue_size;
+ } else {
+ f->indexed = true;
+ f->index.offset = 0;
+ f->index.length = si->lfb_depth;
+ }
+
+ return 0;
+}
+
+/**
+ * screen_info_pixel_format - Returns the screen-info format as pixel-format description
+ *
+ * @si: the screen_info
+ * @f: pointer to return pixel-format description
+ *
+ * Returns:
+ * 0 on success, or a negative errno code otherwise.
+ */
+int screen_info_pixel_format(const struct screen_info *si, struct pixel_format *f)
+{
+ unsigned int type = screen_info_video_type(si);
+
+ /* TODO: Add support for additional types as needed. */
+ switch (type) {
+ case VIDEO_TYPE_VLFB:
+ case VIDEO_TYPE_EFI:
+ return __screen_info_lfb_pixel_format(si, f);
+ }
+
+ /* not supported */
+ return -EINVAL;
+}
+EXPORT_SYMBOL(screen_info_pixel_format);