LCOV - code coverage report
Current view: top level - drivers/video/fbdev - bcm2708_fb.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_real_modules_combined.info Lines: 258 476 54.2 %
Date: 2020-09-30 20:25:40 Functions: 15 21 71.4 %
Branches: 85 241 35.3 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  *  linux/drivers/video/bcm2708_fb.c
       3                 :            :  *
       4                 :            :  * Copyright (C) 2010 Broadcom
       5                 :            :  * Copyright (C) 2018 Raspberry Pi (Trading) Ltd
       6                 :            :  *
       7                 :            :  * This file is subject to the terms and conditions of the GNU General Public
       8                 :            :  * License.  See the file COPYING in the main directory of this archive
       9                 :            :  * for more details.
      10                 :            :  *
      11                 :            :  * Broadcom simple framebuffer driver
      12                 :            :  *
      13                 :            :  * This file is derived from cirrusfb.c
      14                 :            :  * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
      15                 :            :  *
      16                 :            :  */
      17                 :            : 
      18                 :            : #include <linux/module.h>
      19                 :            : #include <linux/kernel.h>
      20                 :            : #include <linux/errno.h>
      21                 :            : #include <linux/string.h>
      22                 :            : #include <linux/slab.h>
      23                 :            : #include <linux/mm.h>
      24                 :            : #include <linux/fb.h>
      25                 :            : #include <linux/init.h>
      26                 :            : #include <linux/interrupt.h>
      27                 :            : #include <linux/ioport.h>
      28                 :            : #include <linux/list.h>
      29                 :            : #include <linux/platform_data/dma-bcm2708.h>
      30                 :            : #include <linux/platform_device.h>
      31                 :            : #include <linux/clk.h>
      32                 :            : #include <linux/printk.h>
      33                 :            : #include <linux/console.h>
      34                 :            : #include <linux/debugfs.h>
      35                 :            : #include <linux/uaccess.h>
      36                 :            : #include <linux/io.h>
      37                 :            : #include <linux/dma-mapping.h>
      38                 :            : #include <linux/cred.h>
      39                 :            : #include <soc/bcm2835/raspberrypi-firmware.h>
      40                 :            : #include <linux/mutex.h>
      41                 :            : 
      42                 :            : //#define BCM2708_FB_DEBUG
      43                 :            : #define MODULE_NAME "bcm2708_fb"
      44                 :            : 
      45                 :            : #ifdef BCM2708_FB_DEBUG
      46                 :            : #define print_debug(fmt, ...) pr_debug("%s:%s:%d: " fmt, \
      47                 :            :                         MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
      48                 :            : #else
      49                 :            : #define print_debug(fmt, ...)
      50                 :            : #endif
      51                 :            : 
      52                 :            : /* This is limited to 16 characters when displayed by X startup */
      53                 :            : static const char *bcm2708_name = "BCM2708 FB";
      54                 :            : 
      55                 :            : #define DRIVER_NAME "bcm2708_fb"
      56                 :            : 
      57                 :            : static int fbwidth = 800;       /* module parameter */
      58                 :            : static int fbheight = 480;      /* module parameter */
      59                 :            : static int fbdepth = 32;        /* module parameter */
      60                 :            : static int fbswap;              /* module parameter */
      61                 :            : 
      62                 :            : static u32 dma_busy_wait_threshold = 1 << 15;
      63                 :            : module_param(dma_busy_wait_threshold, int, 0644);
      64                 :            : MODULE_PARM_DESC(dma_busy_wait_threshold, "Busy-wait for DMA completion below this area");
      65                 :            : 
      66                 :            : struct fb_alloc_tags {
      67                 :            :         struct rpi_firmware_property_tag_header tag1;
      68                 :            :         u32 xres, yres;
      69                 :            :         struct rpi_firmware_property_tag_header tag2;
      70                 :            :         u32 xres_virtual, yres_virtual;
      71                 :            :         struct rpi_firmware_property_tag_header tag3;
      72                 :            :         u32 bpp;
      73                 :            :         struct rpi_firmware_property_tag_header tag4;
      74                 :            :         u32 xoffset, yoffset;
      75                 :            :         struct rpi_firmware_property_tag_header tag5;
      76                 :            :         u32 base, screen_size;
      77                 :            :         struct rpi_firmware_property_tag_header tag6;
      78                 :            :         u32 pitch;
      79                 :            : };
      80                 :            : 
      81                 :            : struct bcm2708_fb_stats {
      82                 :            :         struct debugfs_regset32 regset;
      83                 :            :         u32 dma_copies;
      84                 :            :         u32 dma_irqs;
      85                 :            : };
      86                 :            : 
      87                 :            : struct vc4_display_settings_t {
      88                 :            :         u32 display_num;
      89                 :            :         u32 width;
      90                 :            :         u32 height;
      91                 :            :         u32 depth;
      92                 :            :         u32 pitch;
      93                 :            :         u32 virtual_width;
      94                 :            :         u32 virtual_height;
      95                 :            :         u32 virtual_width_offset;
      96                 :            :         u32 virtual_height_offset;
      97                 :            :         unsigned long fb_bus_address;
      98                 :            : };
      99                 :            : 
     100                 :            : struct bcm2708_fb_dev;
     101                 :            : 
     102                 :            : struct bcm2708_fb {
     103                 :            :         struct fb_info fb;
     104                 :            :         struct platform_device *dev;
     105                 :            :         u32 cmap[16];
     106                 :            :         u32 gpu_cmap[256];
     107                 :            :         struct dentry *debugfs_dir;
     108                 :            :         struct dentry *debugfs_subdir;
     109                 :            :         unsigned long fb_bus_address;
     110                 :            :         struct { u32 base, length; } gpu;
     111                 :            :         struct vc4_display_settings_t display_settings;
     112                 :            :         struct debugfs_regset32 screeninfo_regset;
     113                 :            :         struct bcm2708_fb_dev *fbdev;
     114                 :            :         unsigned int image_size;
     115                 :            :         dma_addr_t dma_addr;
     116                 :            :         void *cpuaddr;
     117                 :            : };
     118                 :            : 
     119                 :            : #define MAX_FRAMEBUFFERS 3
     120                 :            : 
     121                 :            : struct bcm2708_fb_dev {
     122                 :            :         int firmware_supports_multifb;
     123                 :            :         /* Protects the DMA system from multiple FB access */
     124                 :            :         struct mutex dma_mutex;
     125                 :            :         int dma_chan;
     126                 :            :         int dma_irq;
     127                 :            :         void __iomem *dma_chan_base;
     128                 :            :         wait_queue_head_t dma_waitq;
     129                 :            :         bool disable_arm_alloc;
     130                 :            :         struct bcm2708_fb_stats dma_stats;
     131                 :            :         void *cb_base;  /* DMA control blocks */
     132                 :            :         dma_addr_t cb_handle;
     133                 :            :         int instance_count;
     134                 :            :         int num_displays;
     135                 :            :         struct rpi_firmware *fw;
     136                 :            :         struct bcm2708_fb displays[MAX_FRAMEBUFFERS];
     137                 :            : };
     138                 :            : 
     139                 :            : #define to_bcm2708(info)        container_of(info, struct bcm2708_fb, fb)
     140                 :            : 
     141                 :          0 : static void bcm2708_fb_debugfs_deinit(struct bcm2708_fb *fb)
     142                 :            : {
     143                 :          0 :         debugfs_remove_recursive(fb->debugfs_subdir);
     144                 :          0 :         fb->debugfs_subdir = NULL;
     145                 :            : 
     146                 :          0 :         fb->fbdev->instance_count--;
     147                 :            : 
     148         [ #  # ]:          0 :         if (!fb->fbdev->instance_count) {
     149                 :          0 :                 debugfs_remove_recursive(fb->debugfs_dir);
     150                 :          0 :                 fb->debugfs_dir = NULL;
     151                 :            :         }
     152                 :          0 : }
     153                 :            : 
     154                 :        207 : static int bcm2708_fb_debugfs_init(struct bcm2708_fb *fb)
     155                 :            : {
     156                 :            :         char buf[3];
     157                 :        207 :         struct bcm2708_fb_dev *fbdev = fb->fbdev;
     158                 :            : 
     159                 :            :         static struct debugfs_reg32 stats_registers[] = {
     160                 :            :         {"dma_copies", offsetof(struct bcm2708_fb_stats, dma_copies)},
     161                 :            :         {"dma_irqs",   offsetof(struct bcm2708_fb_stats, dma_irqs)},
     162                 :            :         };
     163                 :            : 
     164                 :            :         static struct debugfs_reg32 screeninfo[] = {
     165                 :            :         {"width",      offsetof(struct fb_var_screeninfo, xres)},
     166                 :            :         {"height",     offsetof(struct fb_var_screeninfo, yres)},
     167                 :            :         {"bpp",                offsetof(struct fb_var_screeninfo, bits_per_pixel)},
     168                 :            :         {"xres_virtual", offsetof(struct fb_var_screeninfo, xres_virtual)},
     169                 :            :         {"yres_virtual", offsetof(struct fb_var_screeninfo, yres_virtual)},
     170                 :            :         {"xoffset",    offsetof(struct fb_var_screeninfo, xoffset)},
     171                 :            :         {"yoffset",    offsetof(struct fb_var_screeninfo, yoffset)},
     172                 :            :         };
     173                 :            : 
     174                 :        207 :         fb->debugfs_dir = debugfs_lookup(DRIVER_NAME, NULL);
     175                 :            : 
     176         [ +  - ]:        207 :         if (!fb->debugfs_dir)
     177                 :        207 :                 fb->debugfs_dir = debugfs_create_dir(DRIVER_NAME, NULL);
     178                 :            : 
     179         [ -  + ]:        207 :         if (!fb->debugfs_dir) {
     180                 :          0 :                 dev_warn(fb->fb.dev, "%s: could not create debugfs folder\n",
     181                 :            :                          __func__);
     182                 :          0 :                 return -EFAULT;
     183                 :            :         }
     184                 :            : 
     185                 :        207 :         snprintf(buf, sizeof(buf), "%u", fb->display_settings.display_num);
     186                 :            : 
     187                 :        207 :         fb->debugfs_subdir = debugfs_create_dir(buf, fb->debugfs_dir);
     188                 :            : 
     189         [ -  + ]:        207 :         if (!fb->debugfs_subdir) {
     190                 :          0 :                 dev_warn(fb->fb.dev, "%s: could not create debugfs entry %u\n",
     191                 :            :                          __func__, fb->display_settings.display_num);
     192                 :          0 :                 return -EFAULT;
     193                 :            :         }
     194                 :            : 
     195                 :        207 :         fbdev->dma_stats.regset.regs = stats_registers;
     196                 :        207 :         fbdev->dma_stats.regset.nregs = ARRAY_SIZE(stats_registers);
     197                 :        207 :         fbdev->dma_stats.regset.base = &fbdev->dma_stats;
     198                 :            : 
     199         [ -  + ]:        207 :         if (!debugfs_create_regset32("dma_stats", 0444, fb->debugfs_subdir,
     200                 :            :                                      &fbdev->dma_stats.regset)) {
     201                 :          0 :                 dev_warn(fb->fb.dev, "%s: could not create statistics registers\n",
     202                 :            :                          __func__);
     203                 :          0 :                 goto fail;
     204                 :            :         }
     205                 :            : 
     206                 :        207 :         fb->screeninfo_regset.regs = screeninfo;
     207                 :        207 :         fb->screeninfo_regset.nregs = ARRAY_SIZE(screeninfo);
     208                 :        207 :         fb->screeninfo_regset.base = &fb->fb.var;
     209                 :            : 
     210         [ -  + ]:        207 :         if (!debugfs_create_regset32("screeninfo", 0444, fb->debugfs_subdir,
     211                 :            :                                      &fb->screeninfo_regset)) {
     212                 :          0 :                 dev_warn(fb->fb.dev,
     213                 :            :                          "%s: could not create dimensions registers\n",
     214                 :            :                          __func__);
     215                 :          0 :                 goto fail;
     216                 :            :         }
     217                 :            : 
     218                 :        207 :         fbdev->instance_count++;
     219                 :            : 
     220                 :        207 :         return 0;
     221                 :            : 
     222                 :            : fail:
     223                 :          0 :         bcm2708_fb_debugfs_deinit(fb);
     224                 :          0 :         return -EFAULT;
     225                 :            : }
     226                 :            : 
     227                 :       2070 : static void set_display_num(struct bcm2708_fb *fb)
     228                 :            : {
     229   [ +  -  +  -  :       2070 :         if (fb && fb->fbdev && fb->fbdev->firmware_supports_multifb) {
                   +  - ]
     230                 :       2070 :                 u32 tmp = fb->display_settings.display_num;
     231                 :            : 
     232         [ -  + ]:       2070 :                 if (rpi_firmware_property(fb->fbdev->fw,
     233                 :            :                                           RPI_FIRMWARE_FRAMEBUFFER_SET_DISPLAY_NUM,
     234                 :            :                                           &tmp,
     235                 :            :                                           sizeof(tmp)))
     236         [ #  # ]:          0 :                         dev_warn_once(fb->fb.dev,
     237                 :            :                                       "Set display number call failed. Old GPU firmware?");
     238                 :            :         }
     239                 :       2070 : }
     240                 :            : 
     241                 :        414 : static int bcm2708_fb_set_bitfields(struct fb_var_screeninfo *var)
     242                 :            : {
     243                 :            :         int ret = 0;
     244                 :            : 
     245                 :        414 :         memset(&var->transp, 0, sizeof(var->transp));
     246                 :            : 
     247                 :        414 :         var->red.msb_right = 0;
     248                 :        414 :         var->green.msb_right = 0;
     249                 :        414 :         var->blue.msb_right = 0;
     250                 :            : 
     251   [ -  -  -  +  :        414 :         switch (var->bits_per_pixel) {
                      - ]
     252                 :            :         case 1:
     253                 :            :         case 2:
     254                 :            :         case 4:
     255                 :            :         case 8:
     256                 :          0 :                 var->red.length = var->bits_per_pixel;
     257                 :          0 :                 var->red.offset = 0;
     258                 :          0 :                 var->green.length = var->bits_per_pixel;
     259                 :          0 :                 var->green.offset = 0;
     260                 :          0 :                 var->blue.length = var->bits_per_pixel;
     261                 :          0 :                 var->blue.offset = 0;
     262                 :          0 :                 break;
     263                 :            :         case 16:
     264                 :          0 :                 var->red.length = 5;
     265                 :          0 :                 var->blue.length = 5;
     266                 :            :                 /*
     267                 :            :                  * Green length can be 5 or 6 depending whether
     268                 :            :                  * we're operating in RGB555 or RGB565 mode.
     269                 :            :                  */
     270         [ #  # ]:          0 :                 if (var->green.length != 5 && var->green.length != 6)
     271                 :          0 :                         var->green.length = 6;
     272                 :            :                 break;
     273                 :            :         case 24:
     274                 :          0 :                 var->red.length = 8;
     275                 :          0 :                 var->blue.length = 8;
     276                 :          0 :                 var->green.length = 8;
     277                 :          0 :                 break;
     278                 :            :         case 32:
     279                 :        414 :                 var->red.length = 8;
     280                 :        414 :                 var->green.length = 8;
     281                 :        414 :                 var->blue.length = 8;
     282                 :        414 :                 var->transp.length = 8;
     283                 :        414 :                 break;
     284                 :            :         default:
     285                 :            :                 ret = -EINVAL;
     286                 :            :                 break;
     287                 :            :         }
     288                 :            : 
     289                 :            :         /*
     290                 :            :          * >= 16bpp displays have separate colour component bitfields
     291                 :            :          * encoded in the pixel data.  Calculate their position from
     292                 :            :          * the bitfield length defined above.
     293                 :            :          */
     294   [ +  -  +  -  :        414 :         if (ret == 0 && var->bits_per_pixel >= 24 && fbswap) {
                   +  - ]
     295                 :        414 :                 var->blue.offset = 0;
     296                 :        414 :                 var->green.offset = var->blue.offset + var->blue.length;
     297                 :        414 :                 var->red.offset = var->green.offset + var->green.length;
     298                 :        414 :                 var->transp.offset = var->red.offset + var->red.length;
     299   [ #  #  #  # ]:          0 :         } else if (ret == 0 && var->bits_per_pixel >= 24) {
     300                 :          0 :                 var->red.offset = 0;
     301                 :          0 :                 var->green.offset = var->red.offset + var->red.length;
     302                 :          0 :                 var->blue.offset = var->green.offset + var->green.length;
     303                 :          0 :                 var->transp.offset = var->blue.offset + var->blue.length;
     304   [ #  #  #  # ]:          0 :         } else if (ret == 0 && var->bits_per_pixel >= 16) {
     305                 :          0 :                 var->blue.offset = 0;
     306                 :          0 :                 var->green.offset = var->blue.offset + var->blue.length;
     307                 :          0 :                 var->red.offset = var->green.offset + var->green.length;
     308                 :          0 :                 var->transp.offset = var->red.offset + var->red.length;
     309                 :            :         }
     310                 :            : 
     311                 :        414 :         return ret;
     312                 :            : }
     313                 :            : 
     314                 :        207 : static int bcm2708_fb_check_var(struct fb_var_screeninfo *var,
     315                 :            :                                 struct fb_info *info)
     316                 :            : {
     317                 :            :         /* info input, var output */
     318                 :            :         print_debug("%s(%p) %ux%u (%ux%u), %ul, %u\n",
     319                 :            :                     __func__, info, info->var.xres, info->var.yres,
     320                 :            :                     info->var.xres_virtual, info->var.yres_virtual,
     321                 :            :                     info->screen_size, info->var.bits_per_pixel);
     322                 :            :         print_debug("%s(%p) %ux%u (%ux%u), %u\n", __func__, var, var->xres,
     323                 :            :                     var->yres, var->xres_virtual, var->yres_virtual,
     324                 :            :                     var->bits_per_pixel);
     325                 :            : 
     326         [ -  + ]:        207 :         if (!var->bits_per_pixel)
     327                 :          0 :                 var->bits_per_pixel = 16;
     328                 :            : 
     329         [ -  + ]:        207 :         if (bcm2708_fb_set_bitfields(var) != 0) {
     330                 :          0 :                 pr_err("%s: invalid bits_per_pixel %d\n", __func__,
     331                 :            :                        var->bits_per_pixel);
     332                 :          0 :                 return -EINVAL;
     333                 :            :         }
     334                 :            : 
     335         [ -  + ]:        207 :         if (var->xres_virtual < var->xres)
     336                 :          0 :                 var->xres_virtual = var->xres;
     337                 :            :         /* use highest possible virtual resolution */
     338         [ -  + ]:        207 :         if (var->yres_virtual == -1) {
     339                 :          0 :                 var->yres_virtual = 480;
     340                 :            : 
     341                 :          0 :                 pr_err("%s: virtual resolution set to maximum of %dx%d\n",
     342                 :            :                        __func__, var->xres_virtual, var->yres_virtual);
     343                 :            :         }
     344         [ -  + ]:        207 :         if (var->yres_virtual < var->yres)
     345                 :          0 :                 var->yres_virtual = var->yres;
     346                 :            : 
     347                 :            :         if (var->xoffset < 0)
     348                 :            :                 var->xoffset = 0;
     349                 :            :         if (var->yoffset < 0)
     350                 :            :                 var->yoffset = 0;
     351                 :            : 
     352                 :            :         /* truncate xoffset and yoffset to maximum if too high */
     353         [ -  + ]:        207 :         if (var->xoffset > var->xres_virtual - var->xres)
     354                 :          0 :                 var->xoffset = var->xres_virtual - var->xres - 1;
     355         [ -  + ]:        207 :         if (var->yoffset > var->yres_virtual - var->yres)
     356                 :          0 :                 var->yoffset = var->yres_virtual - var->yres - 1;
     357                 :            : 
     358                 :            :         return 0;
     359                 :            : }
     360                 :            : 
     361                 :       1656 : static int bcm2708_fb_set_par(struct fb_info *info)
     362                 :            : {
     363                 :            :         struct bcm2708_fb *fb = to_bcm2708(info);
     364                 :      13248 :         struct fb_alloc_tags fbinfo = {
     365                 :            :                 .tag1 = { RPI_FIRMWARE_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT,
     366                 :            :                           8, 0, },
     367                 :       1656 :                         .xres = info->var.xres,
     368                 :       1656 :                         .yres = info->var.yres,
     369                 :            :                 .tag2 = { RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT,
     370                 :            :                           8, 0, },
     371                 :       1656 :                         .xres_virtual = info->var.xres_virtual,
     372                 :       1656 :                         .yres_virtual = info->var.yres_virtual,
     373                 :            :                 .tag3 = { RPI_FIRMWARE_FRAMEBUFFER_SET_DEPTH, 4, 0 },
     374                 :       1656 :                         .bpp = info->var.bits_per_pixel,
     375                 :            :                 .tag4 = { RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET, 8, 0 },
     376                 :       1656 :                         .xoffset = info->var.xoffset,
     377                 :       1656 :                         .yoffset = info->var.yoffset,
     378                 :            :                 .tag5 = { RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE, 8, 0 },
     379                 :            :                         /* base and screen_size will be initialised later */
     380                 :            :                 .tag6 = { RPI_FIRMWARE_FRAMEBUFFER_SET_PITCH, 4, 0 },
     381                 :            :                         /* pitch will be initialised later */
     382                 :            :         };
     383                 :            :         int ret, image_size;
     384                 :            : 
     385                 :            :         print_debug("%s(%p) %dx%d (%dx%d), %d, %d (display %d)\n", __func__,
     386                 :            :                     info,
     387                 :            :                     info->var.xres, info->var.yres, info->var.xres_virtual,
     388                 :            :                     info->var.yres_virtual, (int)info->screen_size,
     389                 :            :                     info->var.bits_per_pixel, value);
     390                 :            : 
     391                 :            :         /* Need to set the display number to act on first
     392                 :            :          * Cannot do it in the tag list because on older firmware the call
     393                 :            :          * will fail and stop the rest of the list being executed.
     394                 :            :          * We can ignore this call failing as the default at other end is 0
     395                 :            :          */
     396                 :       1656 :         set_display_num(fb);
     397                 :            : 
     398                 :            :         /* Try allocating our own buffer. We can specify all the parameters */
     399                 :       3312 :         image_size = ((info->var.xres * info->var.yres) *
     400                 :       3312 :                       info->var.bits_per_pixel) >> 3;
     401                 :            : 
     402   [ +  +  -  + ]:       1863 :         if (!fb->fbdev->disable_arm_alloc &&
     403         [ #  # ]:        207 :             (image_size != fb->image_size || !fb->dma_addr)) {
     404         [ -  + ]:        207 :                 if (fb->dma_addr) {
     405                 :          0 :                         dma_free_coherent(info->device, fb->image_size,
     406                 :            :                                           fb->cpuaddr, fb->dma_addr);
     407                 :          0 :                         fb->image_size = 0;
     408                 :          0 :                         fb->cpuaddr = NULL;
     409                 :          0 :                         fb->dma_addr = 0;
     410                 :            :                 }
     411                 :            : 
     412                 :        414 :                 fb->cpuaddr = dma_alloc_coherent(info->device, image_size,
     413                 :            :                                                  &fb->dma_addr, GFP_KERNEL);
     414                 :            : 
     415         [ -  + ]:        207 :                 if (!fb->cpuaddr) {
     416                 :          0 :                         fb->dma_addr = 0;
     417                 :          0 :                         fb->fbdev->disable_arm_alloc = true;
     418                 :            :                 } else {
     419                 :        207 :                         fb->image_size = image_size;
     420                 :            :                 }
     421                 :            :         }
     422                 :            : 
     423         [ +  + ]:       1656 :         if (fb->cpuaddr) {
     424                 :        207 :                 fbinfo.base = fb->dma_addr;
     425                 :        207 :                 fbinfo.screen_size = image_size;
     426                 :        207 :                 fbinfo.pitch = (info->var.xres * info->var.bits_per_pixel) >> 3;
     427                 :            : 
     428                 :        207 :                 ret = rpi_firmware_property_list(fb->fbdev->fw, &fbinfo,
     429                 :            :                                                  sizeof(fbinfo));
     430   [ +  -  +  - ]:        207 :                 if (ret || fbinfo.base != fb->dma_addr) {
     431                 :            :                         /* Firmware either failed, or assigned a different base
     432                 :            :                          * address (ie it doesn't support being passed an FB
     433                 :            :                          * allocation).
     434                 :            :                          * Destroy the allocation, and don't try again.
     435                 :            :                          */
     436                 :        207 :                         dma_free_coherent(info->device, fb->image_size,
     437                 :            :                                           fb->cpuaddr, fb->dma_addr);
     438                 :        207 :                         fb->image_size = 0;
     439                 :        207 :                         fb->cpuaddr = NULL;
     440                 :        207 :                         fb->dma_addr = 0;
     441                 :        207 :                         fb->fbdev->disable_arm_alloc = true;
     442                 :            :                 }
     443                 :            :         } else {
     444                 :            :                 /* Our allocation failed - drop into the old scheme of
     445                 :            :                  * allocation by the VPU.
     446                 :            :                  */
     447                 :            :                 ret = -ENOMEM;
     448                 :            :         }
     449                 :            : 
     450         [ +  + ]:       1656 :         if (ret) {
     451                 :            :                 /* Old scheme:
     452                 :            :                  * - FRAMEBUFFER_ALLOCATE passes 0 for base and screen_size.
     453                 :            :                  * - GET_PITCH instead of SET_PITCH.
     454                 :            :                  */
     455                 :       1449 :                 fbinfo.base = 0;
     456                 :       1449 :                 fbinfo.screen_size = 0;
     457                 :       1449 :                 fbinfo.tag6.tag = RPI_FIRMWARE_FRAMEBUFFER_GET_PITCH;
     458                 :       1449 :                 fbinfo.pitch = 0;
     459                 :            : 
     460                 :       1449 :                 ret = rpi_firmware_property_list(fb->fbdev->fw, &fbinfo,
     461                 :            :                                                  sizeof(fbinfo));
     462         [ -  + ]:       1449 :                 if (ret) {
     463                 :          0 :                         dev_err(info->device,
     464                 :            :                                 "Failed to allocate GPU framebuffer (%d)\n",
     465                 :            :                                 ret);
     466                 :          0 :                         return ret;
     467                 :            :                 }
     468                 :            :         }
     469                 :            : 
     470         [ -  + ]:       1656 :         if (info->var.bits_per_pixel <= 8)
     471                 :          0 :                 fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
     472                 :            :         else
     473                 :       1656 :                 fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
     474                 :            : 
     475                 :       1656 :         fb->fb.fix.line_length = fbinfo.pitch;
     476                 :       1656 :         fbinfo.base |= 0x40000000;
     477                 :       1656 :         fb->fb_bus_address = fbinfo.base;
     478                 :       1656 :         fbinfo.base &= ~0xc0000000;
     479                 :       1656 :         fb->fb.fix.smem_start = fbinfo.base;
     480                 :       1656 :         fb->fb.fix.smem_len = fbinfo.pitch * fbinfo.yres_virtual;
     481                 :       1656 :         fb->fb.screen_size = fbinfo.screen_size;
     482                 :            : 
     483         [ +  - ]:       1656 :         if (!fb->dma_addr) {
     484         [ +  + ]:       1656 :                 if (fb->fb.screen_base)
     485                 :       1449 :                         iounmap(fb->fb.screen_base);
     486                 :            : 
     487                 :       3312 :                 fb->fb.screen_base = ioremap_wc(fbinfo.base,
     488                 :       1656 :                                                 fb->fb.screen_size);
     489                 :            :         } else {
     490                 :          0 :                 fb->fb.screen_base = fb->cpuaddr;
     491                 :            :         }
     492                 :            : 
     493         [ -  + ]:       1656 :         if (!fb->fb.screen_base) {
     494                 :            :                 /* the console may currently be locked */
     495                 :          0 :                 console_trylock();
     496                 :          0 :                 console_unlock();
     497                 :          0 :                 dev_err(info->device, "Failed to set screen_base\n");
     498                 :          0 :                 return -ENOMEM;
     499                 :            :         }
     500                 :            : 
     501                 :            :         print_debug("%s: start = %p,%p width=%d, height=%d, bpp=%d, pitch=%d size=%d\n",
     502                 :            :                     __func__, (void *)fb->fb.screen_base,
     503                 :            :                     (void *)fb->fb_bus_address, fbinfo.xres, fbinfo.yres,
     504                 :            :                     fbinfo.bpp, fbinfo.pitch, (int)fb->fb.screen_size);
     505                 :            : 
     506                 :            :         return 0;
     507                 :            : }
     508                 :            : 
     509                 :            : static inline u32 convert_bitfield(int val, struct fb_bitfield *bf)
     510                 :            : {
     511                 :     119232 :         unsigned int mask = (1 << bf->length) - 1;
     512                 :            : 
     513                 :     119232 :         return (val >> (16 - bf->length) & mask) << bf->offset;
     514                 :            : }
     515                 :            : 
     516                 :      79488 : static int bcm2708_fb_setcolreg(unsigned int regno, unsigned int red,
     517                 :            :                                 unsigned int green, unsigned int blue,
     518                 :            :                                 unsigned int transp, struct fb_info *info)
     519                 :            : {
     520                 :            :         struct bcm2708_fb *fb = to_bcm2708(info);
     521                 :            : 
     522         [ -  + ]:      79488 :         if (fb->fb.var.bits_per_pixel <= 8) {
     523         [ #  # ]:          0 :                 if (regno < 256) {
     524                 :            :                         /* blue [23:16], green [15:8], red [7:0] */
     525                 :          0 :                         fb->gpu_cmap[regno] = ((red   >> 8) & 0xff) << 0 |
     526                 :          0 :                                               ((green >> 8) & 0xff) << 8 |
     527                 :          0 :                                               ((blue  >> 8) & 0xff) << 16;
     528                 :            :                 }
     529                 :            :                 /* Hack: we need to tell GPU the palette has changed, but
     530                 :            :                  * currently bcm2708_fb_set_par takes noticeable time when
     531                 :            :                  * called for every (256) colour
     532                 :            :                  * So just call it for what looks like the last colour in a
     533                 :            :                  * list for now.
     534                 :            :                  */
     535         [ #  # ]:          0 :                 if (regno == 15 || regno == 255) {
     536                 :            :                         struct packet {
     537                 :            :                                 u32 offset;
     538                 :            :                                 u32 length;
     539                 :            :                                 u32 cmap[256];
     540                 :            :                         } *packet;
     541                 :            :                         int ret;
     542                 :            : 
     543                 :            :                         packet = kmalloc(sizeof(*packet), GFP_KERNEL);
     544         [ #  # ]:          0 :                         if (!packet)
     545                 :            :                                 return -ENOMEM;
     546                 :          0 :                         packet->offset = 0;
     547                 :          0 :                         packet->length = regno + 1;
     548                 :          0 :                         memcpy(packet->cmap, fb->gpu_cmap,
     549                 :            :                                sizeof(packet->cmap));
     550                 :            : 
     551                 :          0 :                         set_display_num(fb);
     552                 :            : 
     553                 :          0 :                         ret = rpi_firmware_property(fb->fbdev->fw,
     554                 :            :                                                     RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE,
     555                 :            :                                                     packet,
     556                 :          0 :                                                     (2 + packet->length) * sizeof(u32));
     557   [ #  #  #  # ]:          0 :                         if (ret || packet->offset)
     558                 :          0 :                                 dev_err(info->device,
     559                 :            :                                         "Failed to set palette (%d,%u)\n",
     560                 :            :                                         ret, packet->offset);
     561                 :          0 :                         kfree(packet);
     562                 :            :                 }
     563         [ +  + ]:      79488 :         } else if (regno < 16) {
     564                 :      89424 :                 fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) |
     565                 :      59616 :                                   convert_bitfield(blue, &fb->fb.var.blue) |
     566                 :      59616 :                                   convert_bitfield(green, &fb->fb.var.green) |
     567                 :      29808 :                                   convert_bitfield(red, &fb->fb.var.red);
     568                 :            :         }
     569                 :      79488 :         return regno > 255;
     570                 :            : }
     571                 :            : 
     572                 :        414 : static int bcm2708_fb_blank(int blank_mode, struct fb_info *info)
     573                 :            : {
     574                 :            :         struct bcm2708_fb *fb = to_bcm2708(info);
     575                 :            :         u32 value;
     576                 :            :         int ret;
     577                 :            : 
     578      [ +  +  - ]:        414 :         switch (blank_mode) {
     579                 :            :         case FB_BLANK_UNBLANK:
     580                 :        207 :                 value = 0;
     581                 :        207 :                 break;
     582                 :            :         case FB_BLANK_NORMAL:
     583                 :            :         case FB_BLANK_VSYNC_SUSPEND:
     584                 :            :         case FB_BLANK_HSYNC_SUSPEND:
     585                 :            :         case FB_BLANK_POWERDOWN:
     586                 :        207 :                 value = 1;
     587                 :        207 :                 break;
     588                 :            :         default:
     589                 :            :                 return -EINVAL;
     590                 :            :         }
     591                 :            : 
     592                 :        414 :         set_display_num(fb);
     593                 :            : 
     594                 :        414 :         ret = rpi_firmware_property(fb->fbdev->fw, RPI_FIRMWARE_FRAMEBUFFER_BLANK,
     595                 :            :                                     &value, sizeof(value));
     596                 :            : 
     597         [ -  + ]:        414 :         if (ret)
     598                 :          0 :                 dev_err(info->device, "%s(%d) failed: %d\n", __func__,
     599                 :            :                         blank_mode, ret);
     600                 :            : 
     601                 :        414 :         return ret;
     602                 :            : }
     603                 :            : 
     604                 :       1035 : static int bcm2708_fb_pan_display(struct fb_var_screeninfo *var,
     605                 :            :                                   struct fb_info *info)
     606                 :            : {
     607                 :            :         s32 result;
     608                 :            : 
     609                 :       1035 :         info->var.xoffset = var->xoffset;
     610                 :       1035 :         info->var.yoffset = var->yoffset;
     611                 :       1035 :         result = bcm2708_fb_set_par(info);
     612         [ -  + ]:       1035 :         if (result != 0)
     613                 :          0 :                 pr_err("%s(%u,%u) returns=%d\n", __func__, var->xoffset,
     614                 :            :                        var->yoffset, result);
     615                 :       1035 :         return result;
     616                 :            : }
     617                 :            : 
     618                 :          0 : static void dma_memcpy(struct bcm2708_fb *fb, dma_addr_t dst, dma_addr_t src,
     619                 :            :                        int size)
     620                 :            : {
     621                 :          0 :         struct bcm2708_fb_dev *fbdev = fb->fbdev;
     622                 :          0 :         struct bcm2708_dma_cb *cb = fbdev->cb_base;
     623         [ #  # ]:          0 :         int burst_size = (fbdev->dma_chan == 0) ? 8 : 2;
     624                 :            : 
     625                 :          0 :         cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
     626                 :          0 :                    BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
     627                 :            :                    BCM2708_DMA_D_INC;
     628                 :          0 :         cb->dst = dst;
     629                 :          0 :         cb->src = src;
     630                 :          0 :         cb->length = size;
     631                 :          0 :         cb->stride = 0;
     632                 :          0 :         cb->pad[0] = 0;
     633                 :          0 :         cb->pad[1] = 0;
     634                 :          0 :         cb->next = 0;
     635                 :            : 
     636                 :            :         // Not sure what to do if this gets a signal whilst waiting
     637         [ #  # ]:          0 :         if (mutex_lock_interruptible(&fbdev->dma_mutex))
     638                 :          0 :                 return;
     639                 :            : 
     640         [ #  # ]:          0 :         if (size < dma_busy_wait_threshold) {
     641                 :          0 :                 bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
     642                 :          0 :                 bcm_dma_wait_idle(fbdev->dma_chan_base);
     643                 :            :         } else {
     644                 :          0 :                 void __iomem *local_dma_chan = fbdev->dma_chan_base;
     645                 :            : 
     646                 :          0 :                 cb->info |= BCM2708_DMA_INT_EN;
     647                 :          0 :                 bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
     648         [ #  # ]:          0 :                 while (bcm_dma_is_busy(local_dma_chan)) {
     649   [ #  #  #  #  :          0 :                         wait_event_interruptible(fbdev->dma_waitq,
                   #  # ]
     650                 :            :                                                  !bcm_dma_is_busy(local_dma_chan));
     651                 :            :                 }
     652                 :          0 :                 fbdev->dma_stats.dma_irqs++;
     653                 :            :         }
     654                 :          0 :         fbdev->dma_stats.dma_copies++;
     655                 :            : 
     656                 :          0 :         mutex_unlock(&fbdev->dma_mutex);
     657                 :            : }
     658                 :            : 
     659                 :            : /* address with no aliases */
     660                 :            : #define INTALIAS_NORMAL(x) ((x) & ~0xc0000000)
     661                 :            : /* cache coherent but non-allocating in L1 and L2 */
     662                 :            : #define INTALIAS_L1L2_NONALLOCATING(x) (((x) & ~0xc0000000) | 0x80000000)
     663                 :            : 
     664                 :          0 : static long vc_mem_copy(struct bcm2708_fb *fb, struct fb_dmacopy *ioparam)
     665                 :            : {
     666                 :            :         size_t size = PAGE_SIZE;
     667                 :            :         u32 *buf = NULL;
     668                 :            :         dma_addr_t bus_addr;
     669                 :            :         long rc = 0;
     670                 :            :         size_t offset;
     671                 :            : 
     672                 :            :         /* restrict this to root user */
     673         [ #  # ]:          0 :         if (!uid_eq(current_euid(), GLOBAL_ROOT_UID)) {
     674                 :            :                 rc = -EFAULT;
     675                 :            :                 goto out;
     676                 :            :         }
     677                 :            : 
     678   [ #  #  #  # ]:          0 :         if (!fb->gpu.base || !fb->gpu.length) {
     679                 :          0 :                 pr_err("[%s]: Unable to determine gpu memory (%x,%x)\n",
     680                 :            :                        __func__, fb->gpu.base, fb->gpu.length);
     681                 :          0 :                 return -EFAULT;
     682                 :            :         }
     683                 :            : 
     684   [ #  #  #  # ]:          0 :         if (INTALIAS_NORMAL(ioparam->src) < fb->gpu.base ||
     685                 :          0 :             INTALIAS_NORMAL(ioparam->src) >= fb->gpu.base + fb->gpu.length) {
     686                 :          0 :                 pr_err("[%s]: Invalid memory access %x (%x-%x)", __func__,
     687                 :            :                        INTALIAS_NORMAL(ioparam->src), fb->gpu.base,
     688                 :            :                        fb->gpu.base + fb->gpu.length);
     689                 :          0 :                 return -EFAULT;
     690                 :            :         }
     691                 :            : 
     692                 :          0 :         buf = dma_alloc_coherent(fb->fb.device, PAGE_ALIGN(size), &bus_addr,
     693                 :            :                                  GFP_ATOMIC);
     694         [ #  # ]:          0 :         if (!buf) {
     695                 :          0 :                 pr_err("[%s]: failed to dma_alloc_coherent(%zd)\n", __func__,
     696                 :            :                        size);
     697                 :            :                 rc = -ENOMEM;
     698                 :          0 :                 goto out;
     699                 :            :         }
     700                 :            : 
     701         [ #  # ]:          0 :         for (offset = 0; offset < ioparam->length; offset += size) {
     702                 :          0 :                 size_t remaining = ioparam->length - offset;
     703                 :          0 :                 size_t s = min(size, remaining);
     704                 :          0 :                 u8 *p = (u8 *)((uintptr_t)ioparam->src + offset);
     705                 :          0 :                 u8 *q = (u8 *)ioparam->dst + offset;
     706                 :            : 
     707                 :          0 :                 dma_memcpy(fb, bus_addr,
     708                 :          0 :                            INTALIAS_L1L2_NONALLOCATING((dma_addr_t)p), size);
     709         [ #  # ]:          0 :                 if (copy_to_user(q, buf, s) != 0) {
     710                 :          0 :                         pr_err("[%s]: failed to copy-to-user\n", __func__);
     711                 :            :                         rc = -EFAULT;
     712                 :          0 :                         goto out;
     713                 :            :                 }
     714                 :            :         }
     715                 :            : out:
     716         [ #  # ]:          0 :         if (buf)
     717                 :          0 :                 dma_free_coherent(fb->fb.device, PAGE_ALIGN(size), buf,
     718                 :            :                                   bus_addr);
     719                 :          0 :         return rc;
     720                 :            : }
     721                 :            : 
     722                 :        207 : static int bcm2708_ioctl(struct fb_info *info, unsigned int cmd,
     723                 :            :                          unsigned long arg)
     724                 :            : {
     725                 :            :         struct bcm2708_fb *fb = to_bcm2708(info);
     726                 :        207 :         u32 dummy = 0;
     727                 :            :         int ret;
     728                 :            : 
     729      [ -  -  + ]:        207 :         switch (cmd) {
     730                 :            :         case FBIO_WAITFORVSYNC:
     731                 :          0 :                 set_display_num(fb);
     732                 :            : 
     733                 :          0 :                 ret = rpi_firmware_property(fb->fbdev->fw,
     734                 :            :                                             RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC,
     735                 :            :                                             &dummy, sizeof(dummy));
     736                 :          0 :                 break;
     737                 :            : 
     738                 :            :         case FBIODMACOPY:
     739                 :            :         {
     740                 :            :                 struct fb_dmacopy ioparam;
     741                 :            :                 /* Get the parameter data.
     742                 :            :                  */
     743         [ #  # ]:          0 :                 if (copy_from_user
     744                 :            :                     (&ioparam, (void *)arg, sizeof(ioparam))) {
     745                 :          0 :                         pr_err("[%s]: failed to copy-from-user\n", __func__);
     746                 :            :                         ret = -EFAULT;
     747                 :          0 :                         break;
     748                 :            :                 }
     749                 :          0 :                 ret = vc_mem_copy(fb, &ioparam);
     750                 :          0 :                 break;
     751                 :            :         }
     752                 :            :         default:
     753                 :            :                 dev_dbg(info->device, "Unknown ioctl 0x%x\n", cmd);
     754                 :            :                 return -ENOTTY;
     755                 :            :         }
     756                 :            : 
     757         [ #  # ]:          0 :         if (ret)
     758                 :          0 :                 dev_err(info->device, "ioctl 0x%x failed (%d)\n", cmd, ret);
     759                 :            : 
     760                 :          0 :         return ret;
     761                 :            : }
     762                 :            : 
     763                 :            : #ifdef CONFIG_COMPAT
     764                 :            : struct fb_dmacopy32 {
     765                 :            :         compat_uptr_t dst;
     766                 :            :         __u32 src;
     767                 :            :         __u32 length;
     768                 :            : };
     769                 :            : 
     770                 :            : #define FBIODMACOPY32           _IOW('z', 0x22, struct fb_dmacopy32)
     771                 :            : 
     772                 :            : static int bcm2708_compat_ioctl(struct fb_info *info, unsigned int cmd,
     773                 :            :                                 unsigned long arg)
     774                 :            : {
     775                 :            :         struct bcm2708_fb *fb = to_bcm2708(info);
     776                 :            :         int ret;
     777                 :            : 
     778                 :            :         switch (cmd) {
     779                 :            :         case FBIODMACOPY32:
     780                 :            :         {
     781                 :            :                 struct fb_dmacopy32 param32;
     782                 :            :                 struct fb_dmacopy param;
     783                 :            :                 /* Get the parameter data.
     784                 :            :                  */
     785                 :            :                 if (copy_from_user(&param32, (void *)arg, sizeof(param32))) {
     786                 :            :                         pr_err("[%s]: failed to copy-from-user\n", __func__);
     787                 :            :                         ret = -EFAULT;
     788                 :            :                         break;
     789                 :            :                 }
     790                 :            :                 param.dst = compat_ptr(param32.dst);
     791                 :            :                 param.src = param32.src;
     792                 :            :                 param.length = param32.length;
     793                 :            :                 ret = vc_mem_copy(fb, &param);
     794                 :            :                 break;
     795                 :            :         }
     796                 :            :         default:
     797                 :            :                 ret = bcm2708_ioctl(info, cmd, arg);
     798                 :            :                 break;
     799                 :            :         }
     800                 :            :         return ret;
     801                 :            : }
     802                 :            : #endif
     803                 :            : 
     804                 :        621 : static void bcm2708_fb_fillrect(struct fb_info *info,
     805                 :            :                                 const struct fb_fillrect *rect)
     806                 :            : {
     807                 :        621 :         cfb_fillrect(info, rect);
     808                 :        621 : }
     809                 :            : 
     810                 :            : /* A helper function for configuring dma control block */
     811                 :            : static void set_dma_cb(struct bcm2708_dma_cb *cb,
     812                 :            :                 int        burst_size,
     813                 :            :                 dma_addr_t dst,
     814                 :            :                 int        dst_stride,
     815                 :            :                 dma_addr_t src,
     816                 :            :                 int        src_stride,
     817                 :            :                 int        w,
     818                 :            :                 int        h)
     819                 :            : {
     820                 :        414 :         cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
     821                 :            :                 BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
     822                 :        207 :                 BCM2708_DMA_D_INC | BCM2708_DMA_TDMODE;
     823                 :        207 :         cb->dst = dst;
     824                 :        207 :         cb->src = src;
     825                 :            :         /*
     826                 :            :          * This is not really obvious from the DMA documentation,
     827                 :            :          * but the top 16 bits must be programmmed to "height -1"
     828                 :            :          * and not "height" in 2D mode.
     829                 :            :          */
     830                 :        207 :         cb->length = ((h - 1) << 16) | w;
     831                 :        207 :         cb->stride = ((dst_stride - w) << 16) | (u16)(src_stride - w);
     832                 :        207 :         cb->pad[0] = 0;
     833                 :        207 :         cb->pad[1] = 0;
     834                 :            : }
     835                 :            : 
     836                 :        207 : static void bcm2708_fb_copyarea(struct fb_info *info,
     837                 :            :                                 const struct fb_copyarea *region)
     838                 :            : {
     839                 :            :         struct bcm2708_fb *fb = to_bcm2708(info);
     840                 :        207 :         struct bcm2708_fb_dev *fbdev = fb->fbdev;
     841                 :        207 :         struct bcm2708_dma_cb *cb = fbdev->cb_base;
     842                 :        207 :         int bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3;
     843                 :            : 
     844                 :            :         /* Channel 0 supports larger bursts and is a bit faster */
     845         [ -  + ]:        207 :         int burst_size = (fbdev->dma_chan == 0) ? 8 : 2;
     846                 :        207 :         int pixels = region->width * region->height;
     847                 :            : 
     848                 :            :         /* If DMA is currently in use (ie being used on another FB), then
     849                 :            :          * rather than wait for it to finish, just use the cfb_copyarea
     850                 :            :          */
     851   [ +  -  +  - ]:        621 :         if (!mutex_trylock(&fbdev->dma_mutex) ||
     852         [ +  - ]:        207 :             bytes_per_pixel > 4 ||
     853         [ +  - ]:        414 :             info->var.xres * info->var.yres > 1920 * 1200 ||
     854   [ +  -  +  - ]:        621 :             region->width <= 0 || region->width > info->var.xres ||
     855   [ +  -  +  - ]:        621 :             region->height <= 0 || region->height > info->var.yres ||
     856   [ +  -  +  - ]:        207 :             region->sx < 0 || region->sx >= info->var.xres ||
     857   [ +  -  +  - ]:        207 :             region->sy < 0 || region->sy >= info->var.yres ||
     858   [ +  -  +  - ]:        207 :             region->dx < 0 || region->dx >= info->var.xres ||
     859   [ +  -  +  - ]:        414 :             region->dy < 0 || region->dy >= info->var.yres ||
     860         [ +  - ]:        414 :             region->sx + region->width > info->var.xres ||
     861         [ +  - ]:        414 :             region->dx + region->width > info->var.xres ||
     862         [ -  + ]:        414 :             region->sy + region->height > info->var.yres ||
     863                 :        207 :             region->dy + region->height > info->var.yres) {
     864                 :          0 :                 cfb_copyarea(info, region);
     865                 :        207 :                 return;
     866                 :            :         }
     867                 :            : 
     868   [ +  -  -  + ]:        207 :         if (region->dy == region->sy && region->dx > region->sx) {
     869                 :            :                 /*
     870                 :            :                  * A difficult case of overlapped copy. Because DMA can't
     871                 :            :                  * copy individual scanlines in backwards direction, we need
     872                 :            :                  * two-pass processing. We do it by programming a chain of dma
     873                 :            :                  * control blocks in the first 16K part of the buffer and use
     874                 :            :                  * the remaining 48K as the intermediate temporary scratch
     875                 :            :                  * buffer. The buffer size is sufficient to handle up to
     876                 :            :                  * 1920x1200 resolution at 32bpp pixel depth.
     877                 :            :                  */
     878                 :            :                 int y;
     879                 :          0 :                 dma_addr_t control_block_pa = fbdev->cb_handle;
     880                 :          0 :                 dma_addr_t scratchbuf = fbdev->cb_handle + 16 * 1024;
     881                 :          0 :                 int scanline_size = bytes_per_pixel * region->width;
     882                 :          0 :                 int scanlines_per_cb = (64 * 1024 - 16 * 1024) / scanline_size;
     883                 :            : 
     884         [ #  # ]:          0 :                 for (y = 0; y < region->height; y += scanlines_per_cb) {
     885                 :          0 :                         dma_addr_t src =
     886                 :          0 :                                 fb->fb_bus_address +
     887                 :          0 :                                 bytes_per_pixel * region->sx +
     888                 :          0 :                                 (region->sy + y) * fb->fb.fix.line_length;
     889                 :          0 :                         dma_addr_t dst =
     890                 :            :                                 fb->fb_bus_address +
     891                 :          0 :                                 bytes_per_pixel * region->dx +
     892                 :          0 :                                 (region->dy + y) * fb->fb.fix.line_length;
     893                 :            : 
     894         [ #  # ]:          0 :                         if (region->height - y < scanlines_per_cb)
     895                 :          0 :                                 scanlines_per_cb = region->height - y;
     896                 :            : 
     897                 :            :                         set_dma_cb(cb, burst_size, scratchbuf, scanline_size,
     898                 :            :                                    src, fb->fb.fix.line_length,
     899                 :            :                                    scanline_size, scanlines_per_cb);
     900                 :          0 :                         control_block_pa += sizeof(struct bcm2708_dma_cb);
     901                 :          0 :                         cb->next = control_block_pa;
     902                 :            :                         cb++;
     903                 :            : 
     904                 :          0 :                         set_dma_cb(cb, burst_size, dst, fb->fb.fix.line_length,
     905                 :            :                                    scratchbuf, scanline_size,
     906                 :            :                                    scanline_size, scanlines_per_cb);
     907                 :          0 :                         control_block_pa += sizeof(struct bcm2708_dma_cb);
     908                 :          0 :                         cb->next = control_block_pa;
     909                 :          0 :                         cb++;
     910                 :            :                 }
     911                 :            :                 /* move the pointer back to the last dma control block */
     912                 :          0 :                 cb--;
     913                 :            :         } else {
     914                 :            :                 /* A single dma control block is enough. */
     915                 :            :                 int sy, dy, stride;
     916                 :            : 
     917         [ +  - ]:        207 :                 if (region->dy <= region->sy) {
     918                 :            :                         /* processing from top to bottom */
     919                 :        207 :                         dy = region->dy;
     920                 :        207 :                         sy = region->sy;
     921                 :        207 :                         stride = fb->fb.fix.line_length;
     922                 :            :                 } else {
     923                 :            :                         /* processing from bottom to top */
     924                 :          0 :                         dy = region->dy + region->height - 1;
     925                 :          0 :                         sy = region->sy + region->height - 1;
     926                 :          0 :                         stride = -fb->fb.fix.line_length;
     927                 :            :                 }
     928                 :        207 :                 set_dma_cb(cb, burst_size,
     929                 :        414 :                            fb->fb_bus_address + dy * fb->fb.fix.line_length +
     930                 :        207 :                            bytes_per_pixel * region->dx,
     931                 :            :                            stride,
     932                 :        207 :                            fb->fb_bus_address + sy * fb->fb.fix.line_length +
     933                 :        207 :                            bytes_per_pixel * region->sx,
     934                 :            :                            stride,
     935                 :        207 :                            region->width * bytes_per_pixel,
     936                 :            :                            region->height);
     937                 :            :         }
     938                 :            : 
     939                 :            :         /* end of dma control blocks chain */
     940                 :        207 :         cb->next = 0;
     941                 :            : 
     942         [ +  - ]:        207 :         if (pixels < dma_busy_wait_threshold) {
     943                 :        207 :                 bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
     944                 :        207 :                 bcm_dma_wait_idle(fbdev->dma_chan_base);
     945                 :            :         } else {
     946                 :          0 :                 void __iomem *local_dma_chan = fbdev->dma_chan_base;
     947                 :            : 
     948                 :          0 :                 cb->info |= BCM2708_DMA_INT_EN;
     949                 :          0 :                 bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
     950         [ #  # ]:          0 :                 while (bcm_dma_is_busy(local_dma_chan)) {
     951   [ #  #  #  #  :          0 :                         wait_event_interruptible(fbdev->dma_waitq,
                   #  # ]
     952                 :            :                                                  !bcm_dma_is_busy(local_dma_chan));
     953                 :            :                 }
     954                 :          0 :                 fbdev->dma_stats.dma_irqs++;
     955                 :            :         }
     956                 :        207 :         fbdev->dma_stats.dma_copies++;
     957                 :            : 
     958                 :        207 :         mutex_unlock(&fbdev->dma_mutex);
     959                 :            : }
     960                 :            : 
     961                 :      53862 : static void bcm2708_fb_imageblit(struct fb_info *info,
     962                 :            :                                  const struct fb_image *image)
     963                 :            : {
     964                 :      53862 :         cfb_imageblit(info, image);
     965                 :      53862 : }
     966                 :            : 
     967                 :          0 : static irqreturn_t bcm2708_fb_dma_irq(int irq, void *cxt)
     968                 :            : {
     969                 :            :         struct bcm2708_fb_dev *fbdev = cxt;
     970                 :            : 
     971                 :            :         /* FIXME: should read status register to check if this is
     972                 :            :          * actually interrupting us or not, in case this interrupt
     973                 :            :          * ever becomes shared amongst several DMA channels
     974                 :            :          *
     975                 :            :          * readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_IRQ;
     976                 :            :          */
     977                 :            : 
     978                 :            :         /* acknowledge the interrupt */
     979                 :          0 :         writel(BCM2708_DMA_INT, fbdev->dma_chan_base + BCM2708_DMA_CS);
     980                 :            : 
     981                 :          0 :         wake_up(&fbdev->dma_waitq);
     982                 :          0 :         return IRQ_HANDLED;
     983                 :            : }
     984                 :            : 
     985                 :            : static struct fb_ops bcm2708_fb_ops = {
     986                 :            :         .owner = THIS_MODULE,
     987                 :            :         .fb_check_var = bcm2708_fb_check_var,
     988                 :            :         .fb_set_par = bcm2708_fb_set_par,
     989                 :            :         .fb_setcolreg = bcm2708_fb_setcolreg,
     990                 :            :         .fb_blank = bcm2708_fb_blank,
     991                 :            :         .fb_fillrect = bcm2708_fb_fillrect,
     992                 :            :         .fb_copyarea = bcm2708_fb_copyarea,
     993                 :            :         .fb_imageblit = bcm2708_fb_imageblit,
     994                 :            :         .fb_pan_display = bcm2708_fb_pan_display,
     995                 :            :         .fb_ioctl = bcm2708_ioctl,
     996                 :            : #ifdef CONFIG_COMPAT
     997                 :            :         .fb_compat_ioctl = bcm2708_compat_ioctl,
     998                 :            : #endif
     999                 :            : };
    1000                 :            : 
    1001                 :        207 : static int bcm2708_fb_register(struct bcm2708_fb *fb)
    1002                 :            : {
    1003                 :            :         int ret;
    1004                 :            : 
    1005                 :        207 :         fb->fb.fbops = &bcm2708_fb_ops;
    1006                 :        207 :         fb->fb.flags = FBINFO_FLAG_DEFAULT | FBINFO_HWACCEL_COPYAREA;
    1007                 :        207 :         fb->fb.pseudo_palette = fb->cmap;
    1008                 :            : 
    1009                 :        207 :         strncpy(fb->fb.fix.id, bcm2708_name, sizeof(fb->fb.fix.id));
    1010                 :        207 :         fb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
    1011                 :        207 :         fb->fb.fix.type_aux = 0;
    1012                 :        207 :         fb->fb.fix.xpanstep = 1;
    1013                 :        207 :         fb->fb.fix.ypanstep = 1;
    1014                 :        207 :         fb->fb.fix.ywrapstep = 0;
    1015                 :        207 :         fb->fb.fix.accel = FB_ACCEL_NONE;
    1016                 :            : 
    1017                 :            :         /* If we have data from the VC4 on FB's, use that, otherwise use the
    1018                 :            :          * module parameters
    1019                 :            :          */
    1020         [ +  - ]:        207 :         if (fb->display_settings.width) {
    1021                 :        207 :                 fb->fb.var.xres = fb->display_settings.width;
    1022                 :        207 :                 fb->fb.var.yres = fb->display_settings.height;
    1023                 :        207 :                 fb->fb.var.xres_virtual = fb->fb.var.xres;
    1024                 :        207 :                 fb->fb.var.yres_virtual = fb->fb.var.yres;
    1025                 :        207 :                 fb->fb.var.bits_per_pixel = fb->display_settings.depth;
    1026                 :            :         } else {
    1027                 :          0 :                 fb->fb.var.xres = fbwidth;
    1028                 :          0 :                 fb->fb.var.yres = fbheight;
    1029                 :          0 :                 fb->fb.var.xres_virtual = fbwidth;
    1030                 :          0 :                 fb->fb.var.yres_virtual = fbheight;
    1031                 :          0 :                 fb->fb.var.bits_per_pixel = fbdepth;
    1032                 :            :         }
    1033                 :            : 
    1034                 :        207 :         fb->fb.var.vmode = FB_VMODE_NONINTERLACED;
    1035                 :        207 :         fb->fb.var.activate = FB_ACTIVATE_NOW;
    1036                 :        207 :         fb->fb.var.nonstd = 0;
    1037                 :        207 :         fb->fb.var.height = -1;              /* height of picture in mm    */
    1038                 :        207 :         fb->fb.var.width = -1;               /* width of picture in mm    */
    1039                 :        207 :         fb->fb.var.accel_flags = 0;
    1040                 :            : 
    1041                 :        207 :         fb->fb.monspecs.hfmin = 0;
    1042                 :        207 :         fb->fb.monspecs.hfmax = 100000;
    1043                 :        207 :         fb->fb.monspecs.vfmin = 0;
    1044                 :        207 :         fb->fb.monspecs.vfmax = 400;
    1045                 :        207 :         fb->fb.monspecs.dclkmin = 1000000;
    1046                 :        207 :         fb->fb.monspecs.dclkmax = 100000000;
    1047                 :            : 
    1048                 :        207 :         bcm2708_fb_set_bitfields(&fb->fb.var);
    1049                 :            : 
    1050                 :            :         /*
    1051                 :            :          * Allocate colourmap.
    1052                 :            :          */
    1053                 :        207 :         fb_set_var(&fb->fb, &fb->fb.var);
    1054                 :            : 
    1055                 :        207 :         ret = bcm2708_fb_set_par(&fb->fb);
    1056                 :            : 
    1057         [ +  - ]:        207 :         if (ret)
    1058                 :            :                 return ret;
    1059                 :            : 
    1060                 :        207 :         ret = register_framebuffer(&fb->fb);
    1061                 :            : 
    1062         [ -  + ]:        207 :         if (ret == 0)
    1063                 :            :                 goto out;
    1064                 :            : 
    1065                 :          0 :         dev_warn(fb->fb.dev, "Unable to register framebuffer (%d)\n", ret);
    1066                 :            : out:
    1067                 :        207 :         return ret;
    1068                 :            : }
    1069                 :            : 
    1070                 :        207 : static int bcm2708_fb_probe(struct platform_device *dev)
    1071                 :            : {
    1072                 :            :         struct device_node *fw_np;
    1073                 :            :         struct rpi_firmware *fw;
    1074                 :            :         int ret, i;
    1075                 :            :         u32 num_displays;
    1076                 :            :         struct bcm2708_fb_dev *fbdev;
    1077                 :            :         struct { u32 base, length; } gpu_mem;
    1078                 :            : 
    1079                 :        207 :         fbdev = devm_kzalloc(&dev->dev, sizeof(*fbdev), GFP_KERNEL);
    1080                 :            : 
    1081         [ +  - ]:        207 :         if (!fbdev)
    1082                 :            :                 return -ENOMEM;
    1083                 :            : 
    1084                 :        207 :         fw_np = of_parse_phandle(dev->dev.of_node, "firmware", 0);
    1085                 :            : 
    1086                 :            : /* Remove comment when booting without Device Tree is no longer supported
    1087                 :            :  *      if (!fw_np) {
    1088                 :            :  *              dev_err(&dev->dev, "Missing firmware node\n");
    1089                 :            :  *              return -ENOENT;
    1090                 :            :  *      }
    1091                 :            :  */
    1092                 :        207 :         fw = rpi_firmware_get(fw_np);
    1093                 :        207 :         fbdev->fw = fw;
    1094                 :            : 
    1095         [ +  - ]:        207 :         if (!fw)
    1096                 :            :                 return -EPROBE_DEFER;
    1097                 :            : 
    1098                 :        207 :         ret = rpi_firmware_property(fw,
    1099                 :            :                                     RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS,
    1100                 :            :                                     &num_displays, sizeof(u32));
    1101                 :            : 
    1102                 :            :         /* If we fail to get the number of displays, or it returns 0, then
    1103                 :            :          * assume old firmware that doesn't have the mailbox call, so just
    1104                 :            :          * set one display
    1105                 :            :          */
    1106   [ +  -  -  + ]:        207 :         if (ret || num_displays == 0) {
    1107                 :          0 :                 dev_err(&dev->dev,
    1108                 :            :                         "Unable to determine number of FBs. Disabling driver.\n");
    1109                 :          0 :                 return -ENOENT;
    1110                 :            :         } else {
    1111                 :        207 :                 fbdev->firmware_supports_multifb = 1;
    1112                 :            :         }
    1113                 :            : 
    1114         [ -  + ]:        207 :         if (num_displays > MAX_FRAMEBUFFERS) {
    1115                 :          0 :                 dev_warn(&dev->dev,
    1116                 :            :                          "More displays reported from firmware than supported in driver (%u vs %u)",
    1117                 :            :                          num_displays, MAX_FRAMEBUFFERS);
    1118                 :          0 :                 num_displays = MAX_FRAMEBUFFERS;
    1119                 :            :         }
    1120                 :            : 
    1121                 :        207 :         dev_info(&dev->dev, "FB found %d display(s)\n", num_displays);
    1122                 :            : 
    1123                 :            :         /* Set up the DMA information. Note we have just one set of DMA
    1124                 :            :          * parameters to work with all the FB's so requires synchronising when
    1125                 :            :          * being used
    1126                 :            :          */
    1127                 :            : 
    1128                 :        207 :         mutex_init(&fbdev->dma_mutex);
    1129                 :            : 
    1130                 :        414 :         fbdev->cb_base = dma_alloc_wc(&dev->dev, SZ_64K,
    1131                 :            :                                                 &fbdev->cb_handle,
    1132                 :            :                                                 GFP_KERNEL);
    1133         [ -  + ]:        207 :         if (!fbdev->cb_base) {
    1134                 :          0 :                 dev_err(&dev->dev, "cannot allocate DMA CBs\n");
    1135                 :            :                 ret = -ENOMEM;
    1136                 :          0 :                 goto free_fb;
    1137                 :            :         }
    1138                 :            : 
    1139                 :        207 :         ret = bcm_dma_chan_alloc(BCM_DMA_FEATURE_BULK,
    1140                 :            :                                  &fbdev->dma_chan_base,
    1141                 :            :                                  &fbdev->dma_irq);
    1142         [ -  + ]:        207 :         if (ret < 0) {
    1143                 :          0 :                 dev_err(&dev->dev, "Couldn't allocate a DMA channel\n");
    1144                 :          0 :                 goto free_cb;
    1145                 :            :         }
    1146                 :        207 :         fbdev->dma_chan = ret;
    1147                 :            : 
    1148                 :        207 :         ret = request_irq(fbdev->dma_irq, bcm2708_fb_dma_irq,
    1149                 :            :                           0, "bcm2708_fb DMA", fbdev);
    1150         [ -  + ]:        207 :         if (ret) {
    1151                 :          0 :                 dev_err(&dev->dev,
    1152                 :            :                         "Failed to request DMA irq\n");
    1153                 :          0 :                 goto free_dma_chan;
    1154                 :            :         }
    1155                 :            : 
    1156                 :        207 :         rpi_firmware_property(fbdev->fw,
    1157                 :            :                               RPI_FIRMWARE_GET_VC_MEMORY,
    1158                 :            :                               &gpu_mem, sizeof(gpu_mem));
    1159                 :            : 
    1160         [ +  + ]:        414 :         for (i = 0; i < num_displays; i++) {
    1161                 :        207 :                 struct bcm2708_fb *fb = &fbdev->displays[i];
    1162                 :            : 
    1163                 :        207 :                 fb->display_settings.display_num = i;
    1164                 :        207 :                 fb->dev = dev;
    1165                 :        207 :                 fb->fb.device = &dev->dev;
    1166                 :        207 :                 fb->fbdev = fbdev;
    1167                 :            : 
    1168                 :        207 :                 fb->gpu.base = gpu_mem.base;
    1169                 :        207 :                 fb->gpu.length = gpu_mem.length;
    1170                 :            : 
    1171         [ +  - ]:        207 :                 if (fbdev->firmware_supports_multifb) {
    1172                 :        207 :                         ret = rpi_firmware_property(fw,
    1173                 :            :                                                     RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_SETTINGS,
    1174                 :        207 :                                                     &fb->display_settings,
    1175                 :            :                                                     GET_DISPLAY_SETTINGS_PAYLOAD_SIZE);
    1176                 :            :                 } else {
    1177                 :          0 :                         memset(&fb->display_settings, 0,
    1178                 :            :                                sizeof(fb->display_settings));
    1179                 :            :                 }
    1180                 :            : 
    1181                 :        207 :                 ret = bcm2708_fb_register(fb);
    1182                 :            : 
    1183         [ +  - ]:        207 :                 if (ret == 0) {
    1184                 :        207 :                         bcm2708_fb_debugfs_init(fb);
    1185                 :            : 
    1186                 :        207 :                         fbdev->num_displays++;
    1187                 :            : 
    1188                 :        207 :                         dev_info(&dev->dev,
    1189                 :            :                                  "Registered framebuffer for display %u, size %ux%u\n",
    1190                 :            :                                  fb->display_settings.display_num,
    1191                 :            :                                  fb->fb.var.xres,
    1192                 :            :                                  fb->fb.var.yres);
    1193                 :            :                 } else {
    1194                 :            :                         // Use this to flag if this FB entry is in use.
    1195                 :          0 :                         fb->fbdev = NULL;
    1196                 :            :                 }
    1197                 :            :         }
    1198                 :            : 
    1199                 :            :         // Did we actually successfully create any FB's?
    1200         [ +  - ]:        207 :         if (fbdev->num_displays) {
    1201                 :        207 :                 init_waitqueue_head(&fbdev->dma_waitq);
    1202                 :            :                 platform_set_drvdata(dev, fbdev);
    1203                 :        207 :                 return ret;
    1204                 :            :         }
    1205                 :            : 
    1206                 :            : free_dma_chan:
    1207                 :          0 :         bcm_dma_chan_free(fbdev->dma_chan);
    1208                 :            : free_cb:
    1209                 :          0 :         dma_free_wc(&dev->dev, SZ_64K, fbdev->cb_base,
    1210                 :            :                               fbdev->cb_handle);
    1211                 :            : free_fb:
    1212                 :          0 :         dev_err(&dev->dev, "probe failed, err %d\n", ret);
    1213                 :            : 
    1214                 :          0 :         return ret;
    1215                 :            : }
    1216                 :            : 
    1217                 :          0 : static int bcm2708_fb_remove(struct platform_device *dev)
    1218                 :            : {
    1219                 :            :         struct bcm2708_fb_dev *fbdev = platform_get_drvdata(dev);
    1220                 :            :         int i;
    1221                 :            : 
    1222                 :            :         platform_set_drvdata(dev, NULL);
    1223                 :            : 
    1224         [ #  # ]:          0 :         for (i = 0; i < fbdev->num_displays; i++) {
    1225         [ #  # ]:          0 :                 if (fbdev->displays[i].fb.screen_base)
    1226                 :          0 :                         iounmap(fbdev->displays[i].fb.screen_base);
    1227                 :            : 
    1228         [ #  # ]:          0 :                 if (fbdev->displays[i].fbdev) {
    1229                 :          0 :                         unregister_framebuffer(&fbdev->displays[i].fb);
    1230                 :          0 :                         bcm2708_fb_debugfs_deinit(&fbdev->displays[i]);
    1231                 :            :                 }
    1232                 :            :         }
    1233                 :            : 
    1234                 :          0 :         dma_free_wc(&dev->dev, SZ_64K, fbdev->cb_base,
    1235                 :            :                               fbdev->cb_handle);
    1236                 :          0 :         bcm_dma_chan_free(fbdev->dma_chan);
    1237                 :          0 :         free_irq(fbdev->dma_irq, fbdev);
    1238                 :            : 
    1239                 :            :         mutex_destroy(&fbdev->dma_mutex);
    1240                 :            : 
    1241                 :          0 :         return 0;
    1242                 :            : }
    1243                 :            : 
    1244                 :            : static const struct of_device_id bcm2708_fb_of_match_table[] = {
    1245                 :            :         { .compatible = "brcm,bcm2708-fb", },
    1246                 :            :         {},
    1247                 :            : };
    1248                 :            : MODULE_DEVICE_TABLE(of, bcm2708_fb_of_match_table);
    1249                 :            : 
    1250                 :            : static struct platform_driver bcm2708_fb_driver = {
    1251                 :            :         .probe = bcm2708_fb_probe,
    1252                 :            :         .remove = bcm2708_fb_remove,
    1253                 :            :         .driver = {
    1254                 :            :                   .name = DRIVER_NAME,
    1255                 :            :                   .owner = THIS_MODULE,
    1256                 :            :                   .of_match_table = bcm2708_fb_of_match_table,
    1257                 :            :                   },
    1258                 :            : };
    1259                 :            : 
    1260                 :        207 : static int __init bcm2708_fb_init(void)
    1261                 :            : {
    1262                 :        207 :         return platform_driver_register(&bcm2708_fb_driver);
    1263                 :            : }
    1264                 :            : 
    1265                 :            : module_init(bcm2708_fb_init);
    1266                 :            : 
    1267                 :          0 : static void __exit bcm2708_fb_exit(void)
    1268                 :            : {
    1269                 :          0 :         platform_driver_unregister(&bcm2708_fb_driver);
    1270                 :          0 : }
    1271                 :            : 
    1272                 :            : module_exit(bcm2708_fb_exit);
    1273                 :            : 
    1274                 :            : module_param(fbwidth, int, 0644);
    1275                 :            : module_param(fbheight, int, 0644);
    1276                 :            : module_param(fbdepth, int, 0644);
    1277                 :            : module_param(fbswap, int, 0644);
    1278                 :            : 
    1279                 :            : MODULE_DESCRIPTION("BCM2708 framebuffer driver");
    1280                 :            : MODULE_LICENSE("GPL");
    1281                 :            : 
    1282                 :            : MODULE_PARM_DESC(fbwidth, "Width of ARM Framebuffer");
    1283                 :            : MODULE_PARM_DESC(fbheight, "Height of ARM Framebuffer");
    1284                 :            : MODULE_PARM_DESC(fbdepth, "Bit depth of ARM Framebuffer");
    1285                 :            : MODULE_PARM_DESC(fbswap, "Swap order of red and blue in 24 and 32 bit modes");

Generated by: LCOV version 1.14