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