root/drivers/video/fbdev/goldfishfb.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. goldfish_fb_interrupt
  2. convert_bitfield
  3. goldfish_fb_setcolreg
  4. goldfish_fb_check_var
  5. goldfish_fb_set_par
  6. goldfish_fb_pan_display
  7. goldfish_fb_blank
  8. goldfish_fb_probe
  9. goldfish_fb_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (C) 2007 Google, Inc.
   4  * Copyright (C) 2012 Intel, Inc.
   5  */
   6 
   7 #include <linux/module.h>
   8 #include <linux/kernel.h>
   9 #include <linux/dma-mapping.h>
  10 #include <linux/errno.h>
  11 #include <linux/string.h>
  12 #include <linux/slab.h>
  13 #include <linux/delay.h>
  14 #include <linux/mm.h>
  15 #include <linux/fb.h>
  16 #include <linux/init.h>
  17 #include <linux/interrupt.h>
  18 #include <linux/ioport.h>
  19 #include <linux/platform_device.h>
  20 #include <linux/acpi.h>
  21 
  22 enum {
  23         FB_GET_WIDTH        = 0x00,
  24         FB_GET_HEIGHT       = 0x04,
  25         FB_INT_STATUS       = 0x08,
  26         FB_INT_ENABLE       = 0x0c,
  27         FB_SET_BASE         = 0x10,
  28         FB_SET_ROTATION     = 0x14,
  29         FB_SET_BLANK        = 0x18,
  30         FB_GET_PHYS_WIDTH   = 0x1c,
  31         FB_GET_PHYS_HEIGHT  = 0x20,
  32 
  33         FB_INT_VSYNC             = 1U << 0,
  34         FB_INT_BASE_UPDATE_DONE  = 1U << 1
  35 };
  36 
  37 struct goldfish_fb {
  38         void __iomem *reg_base;
  39         int irq;
  40         spinlock_t lock;
  41         wait_queue_head_t wait;
  42         int base_update_count;
  43         int rotation;
  44         struct fb_info fb;
  45         u32 cmap[16];
  46 };
  47 
  48 static irqreturn_t goldfish_fb_interrupt(int irq, void *dev_id)
  49 {
  50         unsigned long irq_flags;
  51         struct goldfish_fb *fb = dev_id;
  52         u32 status;
  53 
  54         spin_lock_irqsave(&fb->lock, irq_flags);
  55         status = readl(fb->reg_base + FB_INT_STATUS);
  56         if (status & FB_INT_BASE_UPDATE_DONE) {
  57                 fb->base_update_count++;
  58                 wake_up(&fb->wait);
  59         }
  60         spin_unlock_irqrestore(&fb->lock, irq_flags);
  61         return status ? IRQ_HANDLED : IRQ_NONE;
  62 }
  63 
  64 static inline u32 convert_bitfield(int val, struct fb_bitfield *bf)
  65 {
  66         unsigned int mask = (1 << bf->length) - 1;
  67 
  68         return (val >> (16 - bf->length) & mask) << bf->offset;
  69 }
  70 
  71 static int
  72 goldfish_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
  73                  unsigned int blue, unsigned int transp, struct fb_info *info)
  74 {
  75         struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
  76 
  77         if (regno < 16) {
  78                 fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) |
  79                                   convert_bitfield(blue, &fb->fb.var.blue) |
  80                                   convert_bitfield(green, &fb->fb.var.green) |
  81                                   convert_bitfield(red, &fb->fb.var.red);
  82                 return 0;
  83         } else {
  84                 return 1;
  85         }
  86 }
  87 
  88 static int goldfish_fb_check_var(struct fb_var_screeninfo *var,
  89                                                         struct fb_info *info)
  90 {
  91         if ((var->rotate & 1) != (info->var.rotate & 1)) {
  92                 if ((var->xres != info->var.yres) ||
  93                                 (var->yres != info->var.xres) ||
  94                                 (var->xres_virtual != info->var.yres) ||
  95                                 (var->yres_virtual > info->var.xres * 2) ||
  96                                 (var->yres_virtual < info->var.xres)) {
  97                         return -EINVAL;
  98                 }
  99         } else {
 100                 if ((var->xres != info->var.xres) ||
 101                    (var->yres != info->var.yres) ||
 102                    (var->xres_virtual != info->var.xres) ||
 103                    (var->yres_virtual > info->var.yres * 2) ||
 104                    (var->yres_virtual < info->var.yres)) {
 105                         return -EINVAL;
 106                 }
 107         }
 108         if ((var->xoffset != info->var.xoffset) ||
 109                         (var->bits_per_pixel != info->var.bits_per_pixel) ||
 110                         (var->grayscale != info->var.grayscale)) {
 111                 return -EINVAL;
 112         }
 113         return 0;
 114 }
 115 
 116 static int goldfish_fb_set_par(struct fb_info *info)
 117 {
 118         struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
 119 
 120         if (fb->rotation != fb->fb.var.rotate) {
 121                 info->fix.line_length = info->var.xres * 2;
 122                 fb->rotation = fb->fb.var.rotate;
 123                 writel(fb->rotation, fb->reg_base + FB_SET_ROTATION);
 124         }
 125         return 0;
 126 }
 127 
 128 
 129 static int goldfish_fb_pan_display(struct fb_var_screeninfo *var,
 130                                                         struct fb_info *info)
 131 {
 132         unsigned long irq_flags;
 133         int base_update_count;
 134         struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
 135 
 136         spin_lock_irqsave(&fb->lock, irq_flags);
 137         base_update_count = fb->base_update_count;
 138         writel(fb->fb.fix.smem_start + fb->fb.var.xres * 2 * var->yoffset,
 139                                                 fb->reg_base + FB_SET_BASE);
 140         spin_unlock_irqrestore(&fb->lock, irq_flags);
 141         wait_event_timeout(fb->wait,
 142                         fb->base_update_count != base_update_count, HZ / 15);
 143         if (fb->base_update_count == base_update_count)
 144                 pr_err("%s: timeout waiting for base update\n", __func__);
 145         return 0;
 146 }
 147 
 148 static int goldfish_fb_blank(int blank, struct fb_info *info)
 149 {
 150         struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
 151 
 152         switch (blank) {
 153         case FB_BLANK_NORMAL:
 154                 writel(1, fb->reg_base + FB_SET_BLANK);
 155                 break;
 156         case FB_BLANK_UNBLANK:
 157                 writel(0, fb->reg_base + FB_SET_BLANK);
 158                 break;
 159         }
 160         return 0;
 161 }
 162 
 163 static struct fb_ops goldfish_fb_ops = {
 164         .owner          = THIS_MODULE,
 165         .fb_check_var   = goldfish_fb_check_var,
 166         .fb_set_par     = goldfish_fb_set_par,
 167         .fb_setcolreg   = goldfish_fb_setcolreg,
 168         .fb_pan_display = goldfish_fb_pan_display,
 169         .fb_blank       = goldfish_fb_blank,
 170         .fb_fillrect    = cfb_fillrect,
 171         .fb_copyarea    = cfb_copyarea,
 172         .fb_imageblit   = cfb_imageblit,
 173 };
 174 
 175 
 176 static int goldfish_fb_probe(struct platform_device *pdev)
 177 {
 178         int ret;
 179         struct resource *r;
 180         struct goldfish_fb *fb;
 181         size_t framesize;
 182         u32 width, height;
 183         dma_addr_t fbpaddr;
 184 
 185         fb = kzalloc(sizeof(*fb), GFP_KERNEL);
 186         if (fb == NULL) {
 187                 ret = -ENOMEM;
 188                 goto err_fb_alloc_failed;
 189         }
 190         spin_lock_init(&fb->lock);
 191         init_waitqueue_head(&fb->wait);
 192         platform_set_drvdata(pdev, fb);
 193 
 194         r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 195         if (r == NULL) {
 196                 ret = -ENODEV;
 197                 goto err_no_io_base;
 198         }
 199         fb->reg_base = ioremap(r->start, PAGE_SIZE);
 200         if (fb->reg_base == NULL) {
 201                 ret = -ENOMEM;
 202                 goto err_no_io_base;
 203         }
 204 
 205         fb->irq = platform_get_irq(pdev, 0);
 206         if (fb->irq <= 0) {
 207                 ret = -ENODEV;
 208                 goto err_no_irq;
 209         }
 210 
 211         width = readl(fb->reg_base + FB_GET_WIDTH);
 212         height = readl(fb->reg_base + FB_GET_HEIGHT);
 213 
 214         fb->fb.fbops            = &goldfish_fb_ops;
 215         fb->fb.flags            = FBINFO_FLAG_DEFAULT;
 216         fb->fb.pseudo_palette   = fb->cmap;
 217         fb->fb.fix.type         = FB_TYPE_PACKED_PIXELS;
 218         fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
 219         fb->fb.fix.line_length = width * 2;
 220         fb->fb.fix.accel        = FB_ACCEL_NONE;
 221         fb->fb.fix.ypanstep = 1;
 222 
 223         fb->fb.var.xres         = width;
 224         fb->fb.var.yres         = height;
 225         fb->fb.var.xres_virtual = width;
 226         fb->fb.var.yres_virtual = height * 2;
 227         fb->fb.var.bits_per_pixel = 16;
 228         fb->fb.var.activate     = FB_ACTIVATE_NOW;
 229         fb->fb.var.height       = readl(fb->reg_base + FB_GET_PHYS_HEIGHT);
 230         fb->fb.var.width        = readl(fb->reg_base + FB_GET_PHYS_WIDTH);
 231         fb->fb.var.pixclock     = 0;
 232 
 233         fb->fb.var.red.offset = 11;
 234         fb->fb.var.red.length = 5;
 235         fb->fb.var.green.offset = 5;
 236         fb->fb.var.green.length = 6;
 237         fb->fb.var.blue.offset = 0;
 238         fb->fb.var.blue.length = 5;
 239 
 240         framesize = width * height * 2 * 2;
 241         fb->fb.screen_base = (char __force __iomem *)dma_alloc_coherent(
 242                                                 &pdev->dev, framesize,
 243                                                 &fbpaddr, GFP_KERNEL);
 244         pr_debug("allocating frame buffer %d * %d, got %p\n",
 245                                         width, height, fb->fb.screen_base);
 246         if (fb->fb.screen_base == NULL) {
 247                 ret = -ENOMEM;
 248                 goto err_alloc_screen_base_failed;
 249         }
 250         fb->fb.fix.smem_start = fbpaddr;
 251         fb->fb.fix.smem_len = framesize;
 252 
 253         ret = fb_set_var(&fb->fb, &fb->fb.var);
 254         if (ret)
 255                 goto err_fb_set_var_failed;
 256 
 257         ret = request_irq(fb->irq, goldfish_fb_interrupt, IRQF_SHARED,
 258                                                         pdev->name, fb);
 259         if (ret)
 260                 goto err_request_irq_failed;
 261 
 262         writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB_INT_ENABLE);
 263         goldfish_fb_pan_display(&fb->fb.var, &fb->fb); /* updates base */
 264 
 265         ret = register_framebuffer(&fb->fb);
 266         if (ret)
 267                 goto err_register_framebuffer_failed;
 268         return 0;
 269 
 270 err_register_framebuffer_failed:
 271         free_irq(fb->irq, fb);
 272 err_request_irq_failed:
 273 err_fb_set_var_failed:
 274         dma_free_coherent(&pdev->dev, framesize,
 275                                 (void *)fb->fb.screen_base,
 276                                 fb->fb.fix.smem_start);
 277 err_alloc_screen_base_failed:
 278 err_no_irq:
 279         iounmap(fb->reg_base);
 280 err_no_io_base:
 281         kfree(fb);
 282 err_fb_alloc_failed:
 283         return ret;
 284 }
 285 
 286 static int goldfish_fb_remove(struct platform_device *pdev)
 287 {
 288         size_t framesize;
 289         struct goldfish_fb *fb = platform_get_drvdata(pdev);
 290 
 291         framesize = fb->fb.var.xres_virtual * fb->fb.var.yres_virtual * 2;
 292         unregister_framebuffer(&fb->fb);
 293         free_irq(fb->irq, fb);
 294 
 295         dma_free_coherent(&pdev->dev, framesize, (void *)fb->fb.screen_base,
 296                                                 fb->fb.fix.smem_start);
 297         iounmap(fb->reg_base);
 298         kfree(fb);
 299         return 0;
 300 }
 301 
 302 static const struct of_device_id goldfish_fb_of_match[] = {
 303         { .compatible = "google,goldfish-fb", },
 304         {},
 305 };
 306 MODULE_DEVICE_TABLE(of, goldfish_fb_of_match);
 307 
 308 static const struct acpi_device_id goldfish_fb_acpi_match[] = {
 309         { "GFSH0004", 0 },
 310         { },
 311 };
 312 MODULE_DEVICE_TABLE(acpi, goldfish_fb_acpi_match);
 313 
 314 static struct platform_driver goldfish_fb_driver = {
 315         .probe          = goldfish_fb_probe,
 316         .remove         = goldfish_fb_remove,
 317         .driver = {
 318                 .name = "goldfish_fb",
 319                 .of_match_table = goldfish_fb_of_match,
 320                 .acpi_match_table = ACPI_PTR(goldfish_fb_acpi_match),
 321         }
 322 };
 323 
 324 module_platform_driver(goldfish_fb_driver);
 325 
 326 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */