linux/samples/vfio-mdev/mdpy-fb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Framebuffer driver for mdpy (mediated virtual pci display device).
   4 *
   5 * See mdpy-defs.h for device specs
   6 *
   7 *   (c) Gerd Hoffmann <kraxel@redhat.com>
   8 *
   9 * Using some code snippets from simplefb and cirrusfb.
  10 *
  11 * This program is free software; you can redistribute it and/or modify it
  12 * under the terms and conditions of the GNU General Public License,
  13 * version 2, as published by the Free Software Foundation.
  14 *
  15 * This program is distributed in the hope it will be useful, but WITHOUT
  16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  17 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  18 * more details.
  19 */
  20#include <linux/errno.h>
  21#include <linux/fb.h>
  22#include <linux/io.h>
  23#include <linux/pci.h>
  24#include <linux/module.h>
  25#include <drm/drm_fourcc.h>
  26#include "mdpy-defs.h"
  27
  28static const struct fb_fix_screeninfo mdpy_fb_fix = {
  29        .id             = "mdpy-fb",
  30        .type           = FB_TYPE_PACKED_PIXELS,
  31        .visual         = FB_VISUAL_TRUECOLOR,
  32        .accel          = FB_ACCEL_NONE,
  33};
  34
  35static const struct fb_var_screeninfo mdpy_fb_var = {
  36        .height         = -1,
  37        .width          = -1,
  38        .activate       = FB_ACTIVATE_NOW,
  39        .vmode          = FB_VMODE_NONINTERLACED,
  40
  41        .bits_per_pixel = 32,
  42        .transp.offset  = 24,
  43        .red.offset     = 16,
  44        .green.offset   = 8,
  45        .blue.offset    = 0,
  46        .transp.length  = 8,
  47        .red.length     = 8,
  48        .green.length   = 8,
  49        .blue.length    = 8,
  50};
  51
  52#define PSEUDO_PALETTE_SIZE 16
  53
  54struct mdpy_fb_par {
  55        u32 palette[PSEUDO_PALETTE_SIZE];
  56};
  57
  58static int mdpy_fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
  59                              u_int transp, struct fb_info *info)
  60{
  61        u32 *pal = info->pseudo_palette;
  62        u32 cr = red >> (16 - info->var.red.length);
  63        u32 cg = green >> (16 - info->var.green.length);
  64        u32 cb = blue >> (16 - info->var.blue.length);
  65        u32 value, mask;
  66
  67        if (regno >= PSEUDO_PALETTE_SIZE)
  68                return -EINVAL;
  69
  70        value = (cr << info->var.red.offset) |
  71                (cg << info->var.green.offset) |
  72                (cb << info->var.blue.offset);
  73        if (info->var.transp.length > 0) {
  74                mask = (1 << info->var.transp.length) - 1;
  75                mask <<= info->var.transp.offset;
  76                value |= mask;
  77        }
  78        pal[regno] = value;
  79
  80        return 0;
  81}
  82
  83static void mdpy_fb_destroy(struct fb_info *info)
  84{
  85        if (info->screen_base)
  86                iounmap(info->screen_base);
  87}
  88
  89static const struct fb_ops mdpy_fb_ops = {
  90        .owner          = THIS_MODULE,
  91        .fb_destroy     = mdpy_fb_destroy,
  92        .fb_setcolreg   = mdpy_fb_setcolreg,
  93        .fb_fillrect    = cfb_fillrect,
  94        .fb_copyarea    = cfb_copyarea,
  95        .fb_imageblit   = cfb_imageblit,
  96};
  97
  98static int mdpy_fb_probe(struct pci_dev *pdev,
  99                         const struct pci_device_id *ent)
 100{
 101        struct fb_info *info;
 102        struct mdpy_fb_par *par;
 103        u32 format, width, height;
 104        int ret;
 105
 106        ret = pci_enable_device(pdev);
 107        if (ret < 0)
 108                return ret;
 109
 110        ret = pci_request_regions(pdev, "mdpy-fb");
 111        if (ret < 0)
 112                return ret;
 113
 114        pci_read_config_dword(pdev, MDPY_FORMAT_OFFSET, &format);
 115        pci_read_config_dword(pdev, MDPY_WIDTH_OFFSET,  &width);
 116        pci_read_config_dword(pdev, MDPY_HEIGHT_OFFSET, &height);
 117        if (format != DRM_FORMAT_XRGB8888) {
 118                pci_err(pdev, "format mismatch (0x%x != 0x%x)\n",
 119                        format, DRM_FORMAT_XRGB8888);
 120                ret = -EINVAL;
 121                goto err_release_regions;
 122        }
 123        if (width < 100  || width > 10000) {
 124                pci_err(pdev, "width (%d) out of range\n", width);
 125                ret = -EINVAL;
 126                goto err_release_regions;
 127        }
 128        if (height < 100 || height > 10000) {
 129                pci_err(pdev, "height (%d) out of range\n", height);
 130                ret = -EINVAL;
 131                goto err_release_regions;
 132        }
 133        pci_info(pdev, "mdpy found: %dx%d framebuffer\n",
 134                 width, height);
 135
 136        info = framebuffer_alloc(sizeof(struct mdpy_fb_par), &pdev->dev);
 137        if (!info) {
 138                ret = -ENOMEM;
 139                goto err_release_regions;
 140        }
 141        pci_set_drvdata(pdev, info);
 142        par = info->par;
 143
 144        info->fix = mdpy_fb_fix;
 145        info->fix.smem_start = pci_resource_start(pdev, 0);
 146        info->fix.smem_len = pci_resource_len(pdev, 0);
 147        info->fix.line_length = width * 4;
 148
 149        info->var = mdpy_fb_var;
 150        info->var.xres = width;
 151        info->var.yres = height;
 152        info->var.xres_virtual = width;
 153        info->var.yres_virtual = height;
 154
 155        info->screen_size = info->fix.smem_len;
 156        info->screen_base = ioremap(info->fix.smem_start,
 157                                    info->screen_size);
 158        if (!info->screen_base) {
 159                pci_err(pdev, "ioremap(pcibar) failed\n");
 160                ret = -EIO;
 161                goto err_release_fb;
 162        }
 163
 164        info->apertures = alloc_apertures(1);
 165        if (!info->apertures) {
 166                ret = -ENOMEM;
 167                goto err_unmap;
 168        }
 169        info->apertures->ranges[0].base = info->fix.smem_start;
 170        info->apertures->ranges[0].size = info->fix.smem_len;
 171
 172        info->fbops = &mdpy_fb_ops;
 173        info->flags = FBINFO_DEFAULT;
 174        info->pseudo_palette = par->palette;
 175
 176        ret = register_framebuffer(info);
 177        if (ret < 0) {
 178                pci_err(pdev, "mdpy-fb device register failed: %d\n", ret);
 179                goto err_unmap;
 180        }
 181
 182        pci_info(pdev, "fb%d registered\n", info->node);
 183        return 0;
 184
 185err_unmap:
 186        iounmap(info->screen_base);
 187
 188err_release_fb:
 189        framebuffer_release(info);
 190
 191err_release_regions:
 192        pci_release_regions(pdev);
 193
 194        return ret;
 195}
 196
 197static void mdpy_fb_remove(struct pci_dev *pdev)
 198{
 199        struct fb_info *info = pci_get_drvdata(pdev);
 200
 201        unregister_framebuffer(info);
 202        framebuffer_release(info);
 203}
 204
 205static struct pci_device_id mdpy_fb_pci_table[] = {
 206        {
 207                .vendor    = MDPY_PCI_VENDOR_ID,
 208                .device    = MDPY_PCI_DEVICE_ID,
 209                .subvendor = MDPY_PCI_SUBVENDOR_ID,
 210                .subdevice = MDPY_PCI_SUBDEVICE_ID,
 211        }, {
 212                /* end of list */
 213        }
 214};
 215
 216static struct pci_driver mdpy_fb_pci_driver = {
 217        .name           = "mdpy-fb",
 218        .id_table       = mdpy_fb_pci_table,
 219        .probe          = mdpy_fb_probe,
 220        .remove         = mdpy_fb_remove,
 221};
 222
 223static int __init mdpy_fb_init(void)
 224{
 225        int ret;
 226
 227        ret = pci_register_driver(&mdpy_fb_pci_driver);
 228        if (ret)
 229                return ret;
 230
 231        return 0;
 232}
 233
 234module_init(mdpy_fb_init);
 235
 236MODULE_DEVICE_TABLE(pci, mdpy_fb_pci_table);
 237MODULE_LICENSE("GPL v2");
 238