Linux Audio

Check our new training course

Linux debugging, profiling, tracing and performance analysis training

Mar 24-27, 2025, special US time zones
Register
Loading...
Note: File does not exist in v3.15.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * framebuffer-coreboot.c
  4 *
  5 * Memory based framebuffer accessed through coreboot table.
  6 *
  7 * Copyright 2012-2013 David Herrmann <dh.herrmann@gmail.com>
  8 * Copyright 2017 Google Inc.
  9 * Copyright 2017 Samuel Holland <samuel@sholland.org>
 10 */
 11
 12#include <linux/device.h>
 13#include <linux/kernel.h>
 14#include <linux/mm.h>
 15#include <linux/module.h>
 16#include <linux/platform_data/simplefb.h>
 17#include <linux/platform_device.h>
 18#include <linux/sysfb.h>
 19
 20#include "coreboot_table.h"
 21
 22#define CB_TAG_FRAMEBUFFER 0x12
 23
 24static const struct simplefb_format formats[] = SIMPLEFB_FORMATS;
 25
 26static int framebuffer_probe(struct coreboot_device *dev)
 27{
 28	int i;
 29	u32 length;
 30	struct lb_framebuffer *fb = &dev->framebuffer;
 31	struct platform_device *pdev;
 32	struct resource res;
 33	struct simplefb_platform_data pdata = {
 34		.width = fb->x_resolution,
 35		.height = fb->y_resolution,
 36		.stride = fb->bytes_per_line,
 37		.format = NULL,
 38	};
 39
 40	/*
 41	 * On coreboot systems, the advertised LB_TAG_FRAMEBUFFER entry
 42	 * in the coreboot table should only be used if the payload did
 43	 * not pass a framebuffer information to the Linux kernel.
 44	 *
 45	 * If the global screen_info data has been filled, the Generic
 46	 * System Framebuffers (sysfb) will already register a platform
 47	 * device and pass that screen_info as platform_data to a driver
 48	 * that can scan-out using the system provided framebuffer.
 49	 */
 50	if (sysfb_handles_screen_info())
 51		return -ENODEV;
 52
 53	if (!fb->physical_address)
 54		return -ENODEV;
 55
 56	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
 57		if (fb->bits_per_pixel     == formats[i].bits_per_pixel &&
 58		    fb->red_mask_pos       == formats[i].red.offset &&
 59		    fb->red_mask_size      == formats[i].red.length &&
 60		    fb->green_mask_pos     == formats[i].green.offset &&
 61		    fb->green_mask_size    == formats[i].green.length &&
 62		    fb->blue_mask_pos      == formats[i].blue.offset &&
 63		    fb->blue_mask_size     == formats[i].blue.length)
 64			pdata.format = formats[i].name;
 65	}
 66	if (!pdata.format)
 67		return -ENODEV;
 68
 69	memset(&res, 0, sizeof(res));
 70	res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
 71	res.name = "Coreboot Framebuffer";
 72	res.start = fb->physical_address;
 73	length = PAGE_ALIGN(fb->y_resolution * fb->bytes_per_line);
 74	res.end = res.start + length - 1;
 75	if (res.end <= res.start)
 76		return -EINVAL;
 77
 78	pdev = platform_device_register_resndata(&dev->dev,
 79						 "simple-framebuffer", 0,
 80						 &res, 1, &pdata,
 81						 sizeof(pdata));
 82	if (IS_ERR(pdev))
 83		pr_warn("coreboot: could not register framebuffer\n");
 84	else
 85		dev_set_drvdata(&dev->dev, pdev);
 86
 87	return PTR_ERR_OR_ZERO(pdev);
 88}
 89
 90static void framebuffer_remove(struct coreboot_device *dev)
 91{
 92	struct platform_device *pdev = dev_get_drvdata(&dev->dev);
 93
 94	platform_device_unregister(pdev);
 95}
 96
 97static const struct coreboot_device_id framebuffer_ids[] = {
 98	{ .tag = CB_TAG_FRAMEBUFFER },
 99	{ /* sentinel */ }
100};
101MODULE_DEVICE_TABLE(coreboot, framebuffer_ids);
102
103static struct coreboot_driver framebuffer_driver = {
104	.probe = framebuffer_probe,
105	.remove = framebuffer_remove,
106	.drv = {
107		.name = "framebuffer",
108	},
109	.id_table = framebuffer_ids,
110};
111module_coreboot_driver(framebuffer_driver);
112
113MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
114MODULE_DESCRIPTION("Memory based framebuffer accessed through coreboot table");
115MODULE_LICENSE("GPL");