xref: /OK3568_Linux_fs/kernel/samples/vfio-mdev/mdpy-fb.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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