diff options
| -rw-r--r-- | drivers/firmware/google/framebuffer-coreboot.c | 82 |
1 files changed, 77 insertions, 5 deletions
diff --git a/drivers/firmware/google/framebuffer-coreboot.c b/drivers/firmware/google/framebuffer-coreboot.c index 767515a30a52..07e9c7be94fa 100644 --- a/drivers/firmware/google/framebuffer-coreboot.c +++ b/drivers/firmware/google/framebuffer-coreboot.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/module.h> +#include <linux/pci.h> #include <linux/platform_data/simplefb.h> #include <linux/platform_device.h> #include <linux/sysfb.h> @@ -21,14 +22,71 @@ #define CB_TAG_FRAMEBUFFER 0x12 +#if defined(CONFIG_PCI) +static bool framebuffer_pci_dev_is_enabled(struct pci_dev *pdev) +{ + /* + * TODO: Try to integrate this code into the PCI subsystem + */ + int ret; + u16 command; + + ret = pci_read_config_word(pdev, PCI_COMMAND, &command); + if (ret != PCIBIOS_SUCCESSFUL) + return false; + if (!(command & PCI_COMMAND_MEMORY)) + return false; + return true; +} + +static struct pci_dev *framebuffer_parent_pci_dev(struct resource *res) +{ + struct pci_dev *pdev = NULL; + const struct resource *r = NULL; + + while (!r && (pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) + r = pci_find_resource(pdev, res); + + if (!r || !pdev) + return NULL; /* not found; not an error */ + + if (!framebuffer_pci_dev_is_enabled(pdev)) { + pci_dev_put(pdev); + return ERR_PTR(-ENODEV); + } + + return pdev; +} +#else +static struct pci_dev *framebuffer_parent_pci_dev(struct resource *res) +{ + return NULL; +} +#endif + +static struct device *framebuffer_parent_dev(struct resource *res) +{ + struct pci_dev *pdev; + + pdev = framebuffer_parent_pci_dev(res); + if (IS_ERR(pdev)) + return ERR_CAST(pdev); + else if (pdev) + return &pdev->dev; + + return NULL; +} + static const struct simplefb_format formats[] = SIMPLEFB_FORMATS; static int framebuffer_probe(struct coreboot_device *dev) { int i; struct lb_framebuffer *fb = &dev->framebuffer; + struct device *parent; struct platform_device *pdev; struct resource res; + int ret; struct simplefb_platform_data pdata = { .width = fb->x_resolution, .height = fb->y_resolution, @@ -57,6 +115,10 @@ static int framebuffer_probe(struct coreboot_device *dev) if (res.end <= res.start) return -EINVAL; + parent = framebuffer_parent_dev(&res); + if (IS_ERR(parent)) + return PTR_ERR(parent); + for (i = 0; i < ARRAY_SIZE(formats); ++i) { if (fb->bits_per_pixel == formats[i].bits_per_pixel && fb->red_mask_pos == formats[i].red.offset && @@ -67,17 +129,27 @@ static int framebuffer_probe(struct coreboot_device *dev) fb->blue_mask_size == formats[i].blue.length) pdata.format = formats[i].name; } - if (!pdata.format) - return -ENODEV; + if (!pdata.format) { + ret = -ENODEV; + goto out_put_device_parent; + } - pdev = platform_device_register_resndata(&dev->dev, + pdev = platform_device_register_resndata(parent, "simple-framebuffer", 0, &res, 1, &pdata, sizeof(pdata)); - if (IS_ERR(pdev)) + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); pr_warn("coreboot: could not register framebuffer\n"); + goto out_put_device_parent; + } + + ret = 0; - return PTR_ERR_OR_ZERO(pdev); +out_put_device_parent: + if (parent) + put_device(parent); + return ret; } static const struct coreboot_device_id framebuffer_ids[] = { |
