X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=target%2Flinux%2Fbrcm2708%2Fpatches-4.19%2F950-0583-Pulled-in-the-multi-frame-buffer-support-from-the-Pi.patch;fp=target%2Flinux%2Fbrcm2708%2Fpatches-4.19%2F950-0583-Pulled-in-the-multi-frame-buffer-support-from-the-Pi.patch;h=0000000000000000000000000000000000000000;hb=c2308a7e4adbb2acc8ff149f91d1ca46801c135e;hp=883d947c2239b91a65b1b22d8986ecc7ebb0ce09;hpb=67dcc43f3a22dc3a7ac07a7065971b426feeb043;p=openwrt%2Fopenwrt.git diff --git a/target/linux/brcm2708/patches-4.19/950-0583-Pulled-in-the-multi-frame-buffer-support-from-the-Pi.patch b/target/linux/brcm2708/patches-4.19/950-0583-Pulled-in-the-multi-frame-buffer-support-from-the-Pi.patch deleted file mode 100644 index 883d947c22..0000000000 --- a/target/linux/brcm2708/patches-4.19/950-0583-Pulled-in-the-multi-frame-buffer-support-from-the-Pi.patch +++ /dev/null @@ -1,925 +0,0 @@ -From 3e33fb46eb8791ba39fe4781f278487bcc2c3356 Mon Sep 17 00:00:00 2001 -From: James Hughes -Date: Thu, 14 Mar 2019 13:27:54 +0000 -Subject: [PATCH] Pulled in the multi frame buffer support from the Pi3 - repo - ---- - drivers/video/fbdev/bcm2708_fb.c | 580 +++++++++++++++------ - include/soc/bcm2835/raspberrypi-firmware.h | 4 + - 2 files changed, 432 insertions(+), 152 deletions(-) - ---- a/drivers/video/fbdev/bcm2708_fb.c -+++ b/drivers/video/fbdev/bcm2708_fb.c -@@ -2,6 +2,7 @@ - * linux/drivers/video/bcm2708_fb.c - * - * Copyright (C) 2010 Broadcom -+ * Copyright (C) 2018 Raspberry Pi (Trading) Ltd - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive -@@ -13,6 +14,7 @@ - * Copyright 1999-2001 Jeff Garzik - * - */ -+ - #include - #include - #include -@@ -36,6 +38,7 @@ - #include - #include - #include -+#include - - //#define BCM2708_FB_DEBUG - #define MODULE_NAME "bcm2708_fb" -@@ -82,62 +85,139 @@ struct bcm2708_fb_stats { - u32 dma_irqs; - }; - -+struct vc4_display_settings_t { -+ u32 display_num; -+ u32 width; -+ u32 height; -+ u32 pitch; -+ u32 depth; -+ u32 virtual_width; -+ u32 virtual_height; -+ u32 virtual_width_offset; -+ u32 virtual_height_offset; -+ unsigned long fb_bus_address; -+}; -+ -+struct bcm2708_fb_dev; -+ - struct bcm2708_fb { - struct fb_info fb; - struct platform_device *dev; -- struct rpi_firmware *fw; - u32 cmap[16]; - u32 gpu_cmap[256]; -+ struct dentry *debugfs_dir; -+ struct dentry *debugfs_subdir; -+ unsigned long fb_bus_address; -+ struct { u32 base, length; } gpu; -+ struct vc4_display_settings_t display_settings; -+ struct debugfs_regset32 screeninfo_regset; -+ struct bcm2708_fb_dev *fbdev; -+ unsigned int image_size; -+ dma_addr_t dma_addr; -+ void *cpuaddr; -+}; -+ -+#define MAX_FRAMEBUFFERS 3 -+ -+struct bcm2708_fb_dev { -+ int firmware_supports_multifb; -+ /* Protects the DMA system from multiple FB access */ -+ struct mutex dma_mutex; - int dma_chan; - int dma_irq; - void __iomem *dma_chan_base; -- void *cb_base; /* DMA control blocks */ -- dma_addr_t cb_handle; -- struct dentry *debugfs_dir; - wait_queue_head_t dma_waitq; -- struct bcm2708_fb_stats stats; -- unsigned long fb_bus_address; -- struct { u32 base, length; } gpu; -+ bool disable_arm_alloc; -+ struct bcm2708_fb_stats dma_stats; -+ void *cb_base; /* DMA control blocks */ -+ dma_addr_t cb_handle; -+ int instance_count; -+ int num_displays; -+ struct rpi_firmware *fw; -+ struct bcm2708_fb displays[MAX_FRAMEBUFFERS]; - }; - - #define to_bcm2708(info) container_of(info, struct bcm2708_fb, fb) - - static void bcm2708_fb_debugfs_deinit(struct bcm2708_fb *fb) - { -- debugfs_remove_recursive(fb->debugfs_dir); -- fb->debugfs_dir = NULL; -+ debugfs_remove_recursive(fb->debugfs_subdir); -+ fb->debugfs_subdir = NULL; -+ -+ fb->fbdev->instance_count--; -+ -+ if (!fb->fbdev->instance_count) { -+ debugfs_remove_recursive(fb->debugfs_dir); -+ fb->debugfs_dir = NULL; -+ } - } - - static int bcm2708_fb_debugfs_init(struct bcm2708_fb *fb) - { -+ char buf[3]; -+ struct bcm2708_fb_dev *fbdev = fb->fbdev; -+ - static struct debugfs_reg32 stats_registers[] = { -- { -- "dma_copies", -- offsetof(struct bcm2708_fb_stats, dma_copies) -- }, -- { -- "dma_irqs", -- offsetof(struct bcm2708_fb_stats, dma_irqs) -- }, -+ {"dma_copies", offsetof(struct bcm2708_fb_stats, dma_copies)}, -+ {"dma_irqs", offsetof(struct bcm2708_fb_stats, dma_irqs)}, - }; - -- fb->debugfs_dir = debugfs_create_dir(DRIVER_NAME, NULL); -+ static struct debugfs_reg32 screeninfo[] = { -+ {"width", offsetof(struct fb_var_screeninfo, xres)}, -+ {"height", offsetof(struct fb_var_screeninfo, yres)}, -+ {"bpp", offsetof(struct fb_var_screeninfo, bits_per_pixel)}, -+ {"xres_virtual", offsetof(struct fb_var_screeninfo, xres_virtual)}, -+ {"yres_virtual", offsetof(struct fb_var_screeninfo, yres_virtual)}, -+ {"xoffset", offsetof(struct fb_var_screeninfo, xoffset)}, -+ {"yoffset", offsetof(struct fb_var_screeninfo, yoffset)}, -+ }; -+ -+ fb->debugfs_dir = debugfs_lookup(DRIVER_NAME, NULL); -+ -+ if (!fb->debugfs_dir) -+ fb->debugfs_dir = debugfs_create_dir(DRIVER_NAME, NULL); -+ - if (!fb->debugfs_dir) { -- pr_warn("%s: could not create debugfs entry\n", -- __func__); -+ dev_warn(fb->fb.dev, "%s: could not create debugfs folder\n", -+ __func__); -+ return -EFAULT; -+ } -+ -+ snprintf(buf, sizeof(buf), "%u", fb->display_settings.display_num); -+ -+ fb->debugfs_subdir = debugfs_create_dir(buf, fb->debugfs_dir); -+ -+ if (!fb->debugfs_subdir) { -+ dev_warn(fb->fb.dev, "%s: could not create debugfs entry %u\n", -+ __func__, fb->display_settings.display_num); - return -EFAULT; - } - -- fb->stats.regset.regs = stats_registers; -- fb->stats.regset.nregs = ARRAY_SIZE(stats_registers); -- fb->stats.regset.base = &fb->stats; -- -- if (!debugfs_create_regset32("stats", 0444, fb->debugfs_dir, -- &fb->stats.regset)) { -- pr_warn("%s: could not create statistics registers\n", -- __func__); -+ fbdev->dma_stats.regset.regs = stats_registers; -+ fbdev->dma_stats.regset.nregs = ARRAY_SIZE(stats_registers); -+ fbdev->dma_stats.regset.base = &fbdev->dma_stats; -+ -+ if (!debugfs_create_regset32("dma_stats", 0444, fb->debugfs_subdir, -+ &fbdev->dma_stats.regset)) { -+ dev_warn(fb->fb.dev, "%s: could not create statistics registers\n", -+ __func__); -+ goto fail; -+ } -+ -+ fb->screeninfo_regset.regs = screeninfo; -+ fb->screeninfo_regset.nregs = ARRAY_SIZE(screeninfo); -+ fb->screeninfo_regset.base = &fb->fb.var; -+ -+ if (!debugfs_create_regset32("screeninfo", 0444, fb->debugfs_subdir, -+ &fb->screeninfo_regset)) { -+ dev_warn(fb->fb.dev, -+ "%s: could not create dimensions registers\n", -+ __func__); - goto fail; - } -+ -+ fbdev->instance_count++; -+ - return 0; - - fail: -@@ -145,6 +225,20 @@ fail: - return -EFAULT; - } - -+static void set_display_num(struct bcm2708_fb *fb) -+{ -+ if (fb && fb->fbdev && fb->fbdev->firmware_supports_multifb) { -+ u32 tmp = fb->display_settings.display_num; -+ -+ if (rpi_firmware_property(fb->fbdev->fw, -+ RPI_FIRMWARE_FRAMEBUFFER_SET_DISPLAY_NUM, -+ &tmp, -+ sizeof(tmp))) -+ dev_warn_once(fb->fb.dev, -+ "Set display number call failed. Old GPU firmware?"); -+ } -+} -+ - static int bcm2708_fb_set_bitfields(struct fb_var_screeninfo *var) - { - int ret = 0; -@@ -222,11 +316,11 @@ static int bcm2708_fb_check_var(struct f - struct fb_info *info) - { - /* info input, var output */ -- print_debug("%s(%p) %dx%d (%dx%d), %d, %d\n", -+ print_debug("%s(%p) %ux%u (%ux%u), %ul, %u\n", - __func__, info, info->var.xres, info->var.yres, - info->var.xres_virtual, info->var.yres_virtual, -- (int)info->screen_size, info->var.bits_per_pixel); -- print_debug("%s(%p) %dx%d (%dx%d), %d\n", __func__, var, var->xres, -+ info->screen_size, info->var.bits_per_pixel); -+ print_debug("%s(%p) %ux%u (%ux%u), %u\n", __func__, var, var->xres, - var->yres, var->xres_virtual, var->yres_virtual, - var->bits_per_pixel); - -@@ -283,23 +377,96 @@ static int bcm2708_fb_set_par(struct fb_ - .xoffset = info->var.xoffset, - .yoffset = info->var.yoffset, - .tag5 = { RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE, 8, 0 }, -- .base = 0, -- .screen_size = 0, -- .tag6 = { RPI_FIRMWARE_FRAMEBUFFER_GET_PITCH, 4, 0 }, -- .pitch = 0, -+ /* base and screen_size will be initialised later */ -+ .tag6 = { RPI_FIRMWARE_FRAMEBUFFER_SET_PITCH, 4, 0 }, -+ /* pitch will be initialised later */ - }; -- int ret; -+ int ret, image_size; -+ - -- print_debug("%s(%p) %dx%d (%dx%d), %d, %d\n", __func__, info, -+ print_debug("%s(%p) %dx%d (%dx%d), %d, %d (display %d)\n", __func__, -+ info, - info->var.xres, info->var.yres, info->var.xres_virtual, - info->var.yres_virtual, (int)info->screen_size, -- info->var.bits_per_pixel); -+ info->var.bits_per_pixel, value); -+ -+ /* Need to set the display number to act on first -+ * Cannot do it in the tag list because on older firmware the call -+ * will fail and stop the rest of the list being executed. -+ * We can ignore this call failing as the default at other end is 0 -+ */ -+ set_display_num(fb); -+ -+ /* Try allocating our own buffer. We can specify all the parameters */ -+ image_size = ((info->var.xres * info->var.yres) * -+ info->var.bits_per_pixel) >> 3; -+ -+ if (!fb->fbdev->disable_arm_alloc && -+ (image_size != fb->image_size || !fb->dma_addr)) { -+ if (fb->dma_addr) { -+ dma_free_coherent(info->device, fb->image_size, -+ fb->cpuaddr, fb->dma_addr); -+ fb->image_size = 0; -+ fb->cpuaddr = NULL; -+ fb->dma_addr = 0; -+ } -+ -+ fb->cpuaddr = dma_alloc_coherent(info->device, image_size, -+ &fb->dma_addr, GFP_KERNEL); -+ -+ if (!fb->cpuaddr) { -+ fb->dma_addr = 0; -+ fb->fbdev->disable_arm_alloc = true; -+ } else { -+ fb->image_size = image_size; -+ } -+ } -+ -+ if (fb->cpuaddr) { -+ fbinfo.base = fb->dma_addr; -+ fbinfo.screen_size = image_size; -+ fbinfo.pitch = (info->var.xres * info->var.bits_per_pixel) >> 3; -+ -+ ret = rpi_firmware_property_list(fb->fbdev->fw, &fbinfo, -+ sizeof(fbinfo)); -+ if (ret || fbinfo.base != fb->dma_addr) { -+ /* Firmware either failed, or assigned a different base -+ * address (ie it doesn't support being passed an FB -+ * allocation). -+ * Destroy the allocation, and don't try again. -+ */ -+ dma_free_coherent(info->device, fb->image_size, -+ fb->cpuaddr, fb->dma_addr); -+ fb->image_size = 0; -+ fb->cpuaddr = NULL; -+ fb->dma_addr = 0; -+ fb->fbdev->disable_arm_alloc = true; -+ } -+ } else { -+ /* Our allocation failed - drop into the old scheme of -+ * allocation by the VPU. -+ */ -+ ret = -ENOMEM; -+ } - -- ret = rpi_firmware_property_list(fb->fw, &fbinfo, sizeof(fbinfo)); - if (ret) { -- dev_err(info->device, -- "Failed to allocate GPU framebuffer (%d)\n", ret); -- return ret; -+ /* Old scheme: -+ * - FRAMEBUFFER_ALLOCATE passes 0 for base and screen_size. -+ * - GET_PITCH instead of SET_PITCH. -+ */ -+ fbinfo.base = 0; -+ fbinfo.screen_size = 0; -+ fbinfo.tag6.tag = RPI_FIRMWARE_FRAMEBUFFER_GET_PITCH; -+ fbinfo.pitch = 0; -+ -+ ret = rpi_firmware_property_list(fb->fbdev->fw, &fbinfo, -+ sizeof(fbinfo)); -+ if (ret) { -+ dev_err(info->device, -+ "Failed to allocate GPU framebuffer (%d)\n", -+ ret); -+ return ret; -+ } - } - - if (info->var.bits_per_pixel <= 8) -@@ -314,9 +481,17 @@ static int bcm2708_fb_set_par(struct fb_ - fb->fb.fix.smem_start = fbinfo.base; - fb->fb.fix.smem_len = fbinfo.pitch * fbinfo.yres_virtual; - fb->fb.screen_size = fbinfo.screen_size; -- if (fb->fb.screen_base) -- iounmap(fb->fb.screen_base); -- fb->fb.screen_base = ioremap_wc(fbinfo.base, fb->fb.screen_size); -+ -+ if (!fb->dma_addr) { -+ if (fb->fb.screen_base) -+ iounmap(fb->fb.screen_base); -+ -+ fb->fb.screen_base = ioremap_wc(fbinfo.base, -+ fb->fb.screen_size); -+ } else { -+ fb->fb.screen_base = fb->cpuaddr; -+ } -+ - if (!fb->fb.screen_base) { - /* the console may currently be locked */ - console_trylock(); -@@ -374,7 +549,10 @@ static int bcm2708_fb_setcolreg(unsigned - packet->length = regno + 1; - memcpy(packet->cmap, fb->gpu_cmap, - sizeof(packet->cmap)); -- ret = rpi_firmware_property(fb->fw, -+ -+ set_display_num(fb); -+ -+ ret = rpi_firmware_property(fb->fbdev->fw, - RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE, - packet, - (2 + packet->length) * sizeof(u32)); -@@ -413,8 +591,11 @@ static int bcm2708_fb_blank(int blank_mo - return -EINVAL; - } - -- ret = rpi_firmware_property(fb->fw, RPI_FIRMWARE_FRAMEBUFFER_BLANK, -+ set_display_num(fb); -+ -+ ret = rpi_firmware_property(fb->fbdev->fw, RPI_FIRMWARE_FRAMEBUFFER_BLANK, - &value, sizeof(value)); -+ - if (ret) - dev_err(info->device, "%s(%d) failed: %d\n", __func__, - blank_mode, ret); -@@ -431,7 +612,7 @@ static int bcm2708_fb_pan_display(struct - info->var.yoffset = var->yoffset; - result = bcm2708_fb_set_par(info); - if (result != 0) -- pr_err("%s(%d,%d) returns=%d\n", __func__, var->xoffset, -+ pr_err("%s(%u,%u) returns=%d\n", __func__, var->xoffset, - var->yoffset, result); - return result; - } -@@ -439,8 +620,9 @@ static int bcm2708_fb_pan_display(struct - static void dma_memcpy(struct bcm2708_fb *fb, dma_addr_t dst, dma_addr_t src, - int size) - { -- int burst_size = (fb->dma_chan == 0) ? 8 : 2; -- struct bcm2708_dma_cb *cb = fb->cb_base; -+ struct bcm2708_fb_dev *fbdev = fb->fbdev; -+ struct bcm2708_dma_cb *cb = fbdev->cb_base; -+ int burst_size = (fbdev->dma_chan == 0) ? 8 : 2; - - cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH | - BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH | -@@ -453,21 +635,27 @@ static void dma_memcpy(struct bcm2708_fb - cb->pad[1] = 0; - cb->next = 0; - -+ // Not sure what to do if this gets a signal whilst waiting -+ if (mutex_lock_interruptible(&fbdev->dma_mutex)) -+ return; -+ - if (size < dma_busy_wait_threshold) { -- bcm_dma_start(fb->dma_chan_base, fb->cb_handle); -- bcm_dma_wait_idle(fb->dma_chan_base); -+ bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle); -+ bcm_dma_wait_idle(fbdev->dma_chan_base); - } else { -- void __iomem *dma_chan = fb->dma_chan_base; -+ void __iomem *local_dma_chan = fbdev->dma_chan_base; - - cb->info |= BCM2708_DMA_INT_EN; -- bcm_dma_start(fb->dma_chan_base, fb->cb_handle); -- while (bcm_dma_is_busy(dma_chan)) { -- wait_event_interruptible(fb->dma_waitq, -- !bcm_dma_is_busy(dma_chan)); -+ bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle); -+ while (bcm_dma_is_busy(local_dma_chan)) { -+ wait_event_interruptible(fbdev->dma_waitq, -+ !bcm_dma_is_busy(local_dma_chan)); - } -- fb->stats.dma_irqs++; -+ fbdev->dma_stats.dma_irqs++; - } -- fb->stats.dma_copies++; -+ fbdev->dma_stats.dma_copies++; -+ -+ mutex_unlock(&fbdev->dma_mutex); - } - - /* address with no aliases */ -@@ -542,10 +730,13 @@ static int bcm2708_ioctl(struct fb_info - - switch (cmd) { - case FBIO_WAITFORVSYNC: -- ret = rpi_firmware_property(fb->fw, -+ set_display_num(fb); -+ -+ ret = rpi_firmware_property(fb->fbdev->fw, - RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC, - &dummy, sizeof(dummy)); - break; -+ - case FBIODMACOPY: - { - struct fb_dmacopy ioparam; -@@ -615,23 +806,22 @@ static int bcm2708_compat_ioctl(struct f - static void bcm2708_fb_fillrect(struct fb_info *info, - const struct fb_fillrect *rect) - { -- /* (is called) print_debug("bcm2708_fb_fillrect\n"); */ - cfb_fillrect(info, rect); - } - - /* A helper function for configuring dma control block */ - static void set_dma_cb(struct bcm2708_dma_cb *cb, -- int burst_size, -- dma_addr_t dst, -- int dst_stride, -- dma_addr_t src, -- int src_stride, -- int w, -- int h) -+ int burst_size, -+ dma_addr_t dst, -+ int dst_stride, -+ dma_addr_t src, -+ int src_stride, -+ int w, -+ int h) - { - cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH | -- BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH | -- BCM2708_DMA_D_INC | BCM2708_DMA_TDMODE; -+ BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH | -+ BCM2708_DMA_D_INC | BCM2708_DMA_TDMODE; - cb->dst = dst; - cb->src = src; - /* -@@ -649,15 +839,19 @@ static void bcm2708_fb_copyarea(struct f - const struct fb_copyarea *region) - { - struct bcm2708_fb *fb = to_bcm2708(info); -- struct bcm2708_dma_cb *cb = fb->cb_base; -+ struct bcm2708_fb_dev *fbdev = fb->fbdev; -+ struct bcm2708_dma_cb *cb = fbdev->cb_base; - int bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3; - - /* Channel 0 supports larger bursts and is a bit faster */ -- int burst_size = (fb->dma_chan == 0) ? 8 : 2; -+ int burst_size = (fbdev->dma_chan == 0) ? 8 : 2; - int pixels = region->width * region->height; - -- /* Fallback to cfb_copyarea() if we don't like something */ -- if (bytes_per_pixel > 4 || -+ /* If DMA is currently in use (ie being used on another FB), then -+ * rather than wait for it to finish, just use the cfb_copyarea -+ */ -+ if (!mutex_trylock(&fbdev->dma_mutex) || -+ bytes_per_pixel > 4 || - info->var.xres * info->var.yres > 1920 * 1200 || - region->width <= 0 || region->width > info->var.xres || - region->height <= 0 || region->height > info->var.yres || -@@ -684,8 +878,8 @@ static void bcm2708_fb_copyarea(struct f - * 1920x1200 resolution at 32bpp pixel depth. - */ - int y; -- dma_addr_t control_block_pa = fb->cb_handle; -- dma_addr_t scratchbuf = fb->cb_handle + 16 * 1024; -+ dma_addr_t control_block_pa = fbdev->cb_handle; -+ dma_addr_t scratchbuf = fbdev->cb_handle + 16 * 1024; - int scanline_size = bytes_per_pixel * region->width; - int scanlines_per_cb = (64 * 1024 - 16 * 1024) / scanline_size; - -@@ -735,10 +929,10 @@ static void bcm2708_fb_copyarea(struct f - } - set_dma_cb(cb, burst_size, - fb->fb_bus_address + dy * fb->fb.fix.line_length + -- bytes_per_pixel * region->dx, -+ bytes_per_pixel * region->dx, - stride, - fb->fb_bus_address + sy * fb->fb.fix.line_length + -- bytes_per_pixel * region->sx, -+ bytes_per_pixel * region->sx, - stride, - region->width * bytes_per_pixel, - region->height); -@@ -748,32 +942,33 @@ static void bcm2708_fb_copyarea(struct f - cb->next = 0; - - if (pixels < dma_busy_wait_threshold) { -- bcm_dma_start(fb->dma_chan_base, fb->cb_handle); -- bcm_dma_wait_idle(fb->dma_chan_base); -+ bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle); -+ bcm_dma_wait_idle(fbdev->dma_chan_base); - } else { -- void __iomem *dma_chan = fb->dma_chan_base; -+ void __iomem *local_dma_chan = fbdev->dma_chan_base; - - cb->info |= BCM2708_DMA_INT_EN; -- bcm_dma_start(fb->dma_chan_base, fb->cb_handle); -- while (bcm_dma_is_busy(dma_chan)) { -- wait_event_interruptible(fb->dma_waitq, -- !bcm_dma_is_busy(dma_chan)); -+ bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle); -+ while (bcm_dma_is_busy(local_dma_chan)) { -+ wait_event_interruptible(fbdev->dma_waitq, -+ !bcm_dma_is_busy(local_dma_chan)); - } -- fb->stats.dma_irqs++; -+ fbdev->dma_stats.dma_irqs++; - } -- fb->stats.dma_copies++; -+ fbdev->dma_stats.dma_copies++; -+ -+ mutex_unlock(&fbdev->dma_mutex); - } - - static void bcm2708_fb_imageblit(struct fb_info *info, - const struct fb_image *image) - { -- /* (is called) print_debug("bcm2708_fb_imageblit\n"); */ - cfb_imageblit(info, image); - } - - static irqreturn_t bcm2708_fb_dma_irq(int irq, void *cxt) - { -- struct bcm2708_fb *fb = cxt; -+ struct bcm2708_fb_dev *fbdev = cxt; - - /* FIXME: should read status register to check if this is - * actually interrupting us or not, in case this interrupt -@@ -783,9 +978,9 @@ static irqreturn_t bcm2708_fb_dma_irq(in - */ - - /* acknowledge the interrupt */ -- writel(BCM2708_DMA_INT, fb->dma_chan_base + BCM2708_DMA_CS); -+ writel(BCM2708_DMA_INT, fbdev->dma_chan_base + BCM2708_DMA_CS); - -- wake_up(&fb->dma_waitq); -+ wake_up(&fbdev->dma_waitq); - return IRQ_HANDLED; - } - -@@ -821,11 +1016,23 @@ static int bcm2708_fb_register(struct bc - fb->fb.fix.ywrapstep = 0; - fb->fb.fix.accel = FB_ACCEL_NONE; - -- fb->fb.var.xres = fbwidth; -- fb->fb.var.yres = fbheight; -- fb->fb.var.xres_virtual = fbwidth; -- fb->fb.var.yres_virtual = fbheight; -- fb->fb.var.bits_per_pixel = fbdepth; -+ /* If we have data from the VC4 on FB's, use that, otherwise use the -+ * module parameters -+ */ -+ if (fb->display_settings.width) { -+ fb->fb.var.xres = fb->display_settings.width; -+ fb->fb.var.yres = fb->display_settings.height; -+ fb->fb.var.xres_virtual = fb->fb.var.xres; -+ fb->fb.var.yres_virtual = fb->fb.var.yres; -+ fb->fb.var.bits_per_pixel = fb->display_settings.depth; -+ } else { -+ fb->fb.var.xres = fbwidth; -+ fb->fb.var.yres = fbheight; -+ fb->fb.var.xres_virtual = fbwidth; -+ fb->fb.var.yres_virtual = fbheight; -+ fb->fb.var.bits_per_pixel = fbdepth; -+ } -+ - fb->fb.var.vmode = FB_VMODE_NONINTERLACED; - fb->fb.var.activate = FB_ACTIVATE_NOW; - fb->fb.var.nonstd = 0; -@@ -841,26 +1048,23 @@ static int bcm2708_fb_register(struct bc - fb->fb.monspecs.dclkmax = 100000000; - - bcm2708_fb_set_bitfields(&fb->fb.var); -- init_waitqueue_head(&fb->dma_waitq); - - /* - * Allocate colourmap. - */ -- - fb_set_var(&fb->fb, &fb->fb.var); -+ - ret = bcm2708_fb_set_par(&fb->fb); -+ - if (ret) - return ret; - -- print_debug("BCM2708FB: registering framebuffer (%dx%d@%d) (%d)\n", -- fbwidth, fbheight, fbdepth, fbswap); -- - ret = register_framebuffer(&fb->fb); -- print_debug("BCM2708FB: register framebuffer (%d)\n", ret); -+ - if (ret == 0) - goto out; - -- print_debug("BCM2708FB: cannot register framebuffer (%d)\n", ret); -+ dev_warn(fb->fb.dev, "Unable to register framebuffer (%d)\n", ret); - out: - return ret; - } -@@ -869,10 +1073,18 @@ static int bcm2708_fb_probe(struct platf - { - struct device_node *fw_np; - struct rpi_firmware *fw; -- struct bcm2708_fb *fb; -- int ret; -+ int ret, i; -+ u32 num_displays; -+ struct bcm2708_fb_dev *fbdev; -+ struct { u32 base, length; } gpu_mem; -+ -+ fbdev = devm_kzalloc(&dev->dev, sizeof(*fbdev), GFP_KERNEL); -+ -+ if (!fbdev) -+ return -ENOMEM; - - fw_np = of_parse_phandle(dev->dev.of_node, "firmware", 0); -+ - /* Remove comment when booting without Device Tree is no longer supported - * if (!fw_np) { - * dev_err(&dev->dev, "Missing firmware node\n"); -@@ -880,90 +1092,154 @@ static int bcm2708_fb_probe(struct platf - * } - */ - fw = rpi_firmware_get(fw_np); -+ fbdev->fw = fw; -+ - if (!fw) - return -EPROBE_DEFER; - -- fb = kzalloc(sizeof(*fb), GFP_KERNEL); -- if (!fb) { -- ret = -ENOMEM; -- goto free_region; -+ ret = rpi_firmware_property(fw, -+ RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS, -+ &num_displays, sizeof(u32)); -+ -+ /* If we fail to get the number of displays, or it returns 0, then -+ * assume old firmware that doesn't have the mailbox call, so just -+ * set one display -+ */ -+ if (ret || num_displays == 0) { -+ num_displays = 1; -+ dev_err(&dev->dev, -+ "Unable to determine number of FB's. Assuming 1\n"); -+ ret = 0; -+ } else { -+ fbdev->firmware_supports_multifb = 1; - } - -- fb->fw = fw; -- bcm2708_fb_debugfs_init(fb); -+ if (num_displays > MAX_FRAMEBUFFERS) { -+ dev_warn(&dev->dev, -+ "More displays reported from firmware than supported in driver (%u vs %u)", -+ num_displays, MAX_FRAMEBUFFERS); -+ num_displays = MAX_FRAMEBUFFERS; -+ } - -- fb->cb_base = dma_alloc_writecombine(&dev->dev, SZ_64K, -- &fb->cb_handle, GFP_KERNEL); -- if (!fb->cb_base) { -+ dev_info(&dev->dev, "FB found %d display(s)\n", num_displays); -+ -+ /* Set up the DMA information. Note we have just one set of DMA -+ * parameters to work with all the FB's so requires synchronising when -+ * being used -+ */ -+ -+ mutex_init(&fbdev->dma_mutex); -+ -+ fbdev->cb_base = dma_alloc_writecombine(&dev->dev, SZ_64K, -+ &fbdev->cb_handle, -+ GFP_KERNEL); -+ if (!fbdev->cb_base) { - dev_err(&dev->dev, "cannot allocate DMA CBs\n"); - ret = -ENOMEM; - goto free_fb; - } - -- pr_info("BCM2708FB: allocated DMA memory %pad\n", &fb->cb_handle); -- - ret = bcm_dma_chan_alloc(BCM_DMA_FEATURE_BULK, -- &fb->dma_chan_base, &fb->dma_irq); -+ &fbdev->dma_chan_base, -+ &fbdev->dma_irq); - if (ret < 0) { -- dev_err(&dev->dev, "couldn't allocate a DMA channel\n"); -+ dev_err(&dev->dev, "Couldn't allocate a DMA channel\n"); - goto free_cb; - } -- fb->dma_chan = ret; -+ fbdev->dma_chan = ret; - -- ret = request_irq(fb->dma_irq, bcm2708_fb_dma_irq, -- 0, "bcm2708_fb dma", fb); -+ ret = request_irq(fbdev->dma_irq, bcm2708_fb_dma_irq, -+ 0, "bcm2708_fb DMA", fbdev); - if (ret) { -- pr_err("%s: failed to request DMA irq\n", __func__); -+ dev_err(&dev->dev, -+ "Failed to request DMA irq\n"); - goto free_dma_chan; - } - -- pr_info("BCM2708FB: allocated DMA channel %d\n", fb->dma_chan); -+ rpi_firmware_property(fbdev->fw, -+ RPI_FIRMWARE_GET_VC_MEMORY, -+ &gpu_mem, sizeof(gpu_mem)); -+ -+ for (i = 0; i < num_displays; i++) { -+ struct bcm2708_fb *fb = &fbdev->displays[i]; -+ -+ fb->display_settings.display_num = i; -+ fb->dev = dev; -+ fb->fb.device = &dev->dev; -+ fb->fbdev = fbdev; -+ -+ fb->gpu.base = gpu_mem.base; -+ fb->gpu.length = gpu_mem.length; -+ -+ if (fbdev->firmware_supports_multifb) { -+ ret = rpi_firmware_property(fw, -+ RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_SETTINGS, -+ &fb->display_settings, -+ GET_DISPLAY_SETTINGS_PAYLOAD_SIZE); -+ } else { -+ memset(&fb->display_settings, 0, -+ sizeof(fb->display_settings)); -+ } -+ -+ ret = bcm2708_fb_register(fb); - -- fb->dev = dev; -- fb->fb.device = &dev->dev; -+ if (ret == 0) { -+ bcm2708_fb_debugfs_init(fb); - -- /* failure here isn't fatal, but we'll fail in vc_mem_copy if -- * fb->gpu is not valid -- */ -- rpi_firmware_property(fb->fw, RPI_FIRMWARE_GET_VC_MEMORY, &fb->gpu, -- sizeof(fb->gpu)); -+ fbdev->num_displays++; - -- ret = bcm2708_fb_register(fb); -- if (ret == 0) { -- platform_set_drvdata(dev, fb); -- goto out; -+ dev_info(&dev->dev, -+ "Registered framebuffer for display %u, size %ux%u\n", -+ fb->display_settings.display_num, -+ fb->fb.var.xres, -+ fb->fb.var.yres); -+ } else { -+ // Use this to flag if this FB entry is in use. -+ fb->fbdev = NULL; -+ } -+ } -+ -+ // Did we actually successfully create any FB's? -+ if (fbdev->num_displays) { -+ init_waitqueue_head(&fbdev->dma_waitq); -+ platform_set_drvdata(dev, fbdev); -+ return ret; - } - - free_dma_chan: -- bcm_dma_chan_free(fb->dma_chan); -+ bcm_dma_chan_free(fbdev->dma_chan); - free_cb: -- dma_free_writecombine(&dev->dev, SZ_64K, fb->cb_base, fb->cb_handle); -+ dma_free_writecombine(&dev->dev, SZ_64K, fbdev->cb_base, -+ fbdev->cb_handle); - free_fb: -- kfree(fb); --free_region: - dev_err(&dev->dev, "probe failed, err %d\n", ret); --out: -+ - return ret; - } - - static int bcm2708_fb_remove(struct platform_device *dev) - { -- struct bcm2708_fb *fb = platform_get_drvdata(dev); -+ struct bcm2708_fb_dev *fbdev = platform_get_drvdata(dev); -+ int i; - - platform_set_drvdata(dev, NULL); - -- if (fb->fb.screen_base) -- iounmap(fb->fb.screen_base); -- unregister_framebuffer(&fb->fb); -- -- dma_free_writecombine(&dev->dev, SZ_64K, fb->cb_base, fb->cb_handle); -- bcm_dma_chan_free(fb->dma_chan); -- -- bcm2708_fb_debugfs_deinit(fb); -+ for (i = 0; i < fbdev->num_displays; i++) { -+ if (fbdev->displays[i].fb.screen_base) -+ iounmap(fbdev->displays[i].fb.screen_base); -+ -+ if (fbdev->displays[i].fbdev) { -+ unregister_framebuffer(&fbdev->displays[i].fb); -+ bcm2708_fb_debugfs_deinit(&fbdev->displays[i]); -+ } -+ } - -- free_irq(fb->dma_irq, fb); -+ dma_free_writecombine(&dev->dev, SZ_64K, fbdev->cb_base, -+ fbdev->cb_handle); -+ bcm_dma_chan_free(fbdev->dma_chan); -+ free_irq(fbdev->dma_irq, fbdev); - -- kfree(fb); -+ mutex_destroy(&fbdev->dma_mutex); - - return 0; - } -@@ -978,10 +1254,10 @@ static struct platform_driver bcm2708_fb - .probe = bcm2708_fb_probe, - .remove = bcm2708_fb_remove, - .driver = { -- .name = DRIVER_NAME, -- .owner = THIS_MODULE, -- .of_match_table = bcm2708_fb_of_match_table, -- }, -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = bcm2708_fb_of_match_table, -+ }, - }; - - static int __init bcm2708_fb_init(void) ---- a/include/soc/bcm2835/raspberrypi-firmware.h -+++ b/include/soc/bcm2835/raspberrypi-firmware.h -@@ -138,9 +138,11 @@ enum rpi_firmware_property_tag { - RPI_FIRMWARE_FRAMEBUFFER_SET_DEPTH = 0x00048005, - RPI_FIRMWARE_FRAMEBUFFER_SET_PIXEL_ORDER = 0x00048006, - RPI_FIRMWARE_FRAMEBUFFER_SET_ALPHA_MODE = 0x00048007, -+ RPI_FIRMWARE_FRAMEBUFFER_SET_PITCH = 0x00048008, - RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET = 0x00048009, - RPI_FIRMWARE_FRAMEBUFFER_SET_OVERSCAN = 0x0004800a, - RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE = 0x0004800b, -+ - RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF = 0x0004801f, - RPI_FIRMWARE_FRAMEBUFFER_SET_GPIOVIRTBUF = 0x00048020, - RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC = 0x0004800e, -@@ -159,6 +161,8 @@ enum rpi_firmware_property_tag { - RPI_FIRMWARE_GET_DMA_CHANNELS = 0x00060001, - }; - -+#define GET_DISPLAY_SETTINGS_PAYLOAD_SIZE 64 -+ - #if IS_ENABLED(CONFIG_RASPBERRYPI_FIRMWARE) - int rpi_firmware_property(struct rpi_firmware *fw, - u32 tag, void *data, size_t len);