xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/tiny/cirrus.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /* SPDX-License-Identifier: GPL-2.0 */
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright 2012-2019 Red Hat
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * This file is subject to the terms and conditions of the GNU General
6*4882a593Smuzhiyun  * Public License version 2. See the file COPYING in the main
7*4882a593Smuzhiyun  * directory of this archive for more details.
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * Authors: Matthew Garrett
10*4882a593Smuzhiyun  *	    Dave Airlie
11*4882a593Smuzhiyun  *	    Gerd Hoffmann
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  * Portions of this code derived from cirrusfb.c:
14*4882a593Smuzhiyun  * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
15*4882a593Smuzhiyun  *
16*4882a593Smuzhiyun  * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
17*4882a593Smuzhiyun  */
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #include <linux/console.h>
20*4882a593Smuzhiyun #include <linux/module.h>
21*4882a593Smuzhiyun #include <linux/pci.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #include <video/cirrus.h>
24*4882a593Smuzhiyun #include <video/vga.h>
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #include <drm/drm_atomic_helper.h>
27*4882a593Smuzhiyun #include <drm/drm_atomic_state_helper.h>
28*4882a593Smuzhiyun #include <drm/drm_connector.h>
29*4882a593Smuzhiyun #include <drm/drm_damage_helper.h>
30*4882a593Smuzhiyun #include <drm/drm_drv.h>
31*4882a593Smuzhiyun #include <drm/drm_fb_helper.h>
32*4882a593Smuzhiyun #include <drm/drm_file.h>
33*4882a593Smuzhiyun #include <drm/drm_format_helper.h>
34*4882a593Smuzhiyun #include <drm/drm_fourcc.h>
35*4882a593Smuzhiyun #include <drm/drm_gem_shmem_helper.h>
36*4882a593Smuzhiyun #include <drm/drm_gem_framebuffer_helper.h>
37*4882a593Smuzhiyun #include <drm/drm_ioctl.h>
38*4882a593Smuzhiyun #include <drm/drm_managed.h>
39*4882a593Smuzhiyun #include <drm/drm_modeset_helper_vtables.h>
40*4882a593Smuzhiyun #include <drm/drm_probe_helper.h>
41*4882a593Smuzhiyun #include <drm/drm_simple_kms_helper.h>
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun #define DRIVER_NAME "cirrus"
44*4882a593Smuzhiyun #define DRIVER_DESC "qemu cirrus vga"
45*4882a593Smuzhiyun #define DRIVER_DATE "2019"
46*4882a593Smuzhiyun #define DRIVER_MAJOR 2
47*4882a593Smuzhiyun #define DRIVER_MINOR 0
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun #define CIRRUS_MAX_PITCH (0x1FF << 3)      /* (4096 - 1) & ~111b bytes */
50*4882a593Smuzhiyun #define CIRRUS_VRAM_SIZE (4 * 1024 * 1024) /* 4 MB */
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun struct cirrus_device {
53*4882a593Smuzhiyun 	struct drm_device	       dev;
54*4882a593Smuzhiyun 	struct drm_simple_display_pipe pipe;
55*4882a593Smuzhiyun 	struct drm_connector	       conn;
56*4882a593Smuzhiyun 	unsigned int		       cpp;
57*4882a593Smuzhiyun 	unsigned int		       pitch;
58*4882a593Smuzhiyun 	void __iomem		       *vram;
59*4882a593Smuzhiyun 	void __iomem		       *mmio;
60*4882a593Smuzhiyun };
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun #define to_cirrus(_dev) container_of(_dev, struct cirrus_device, dev)
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
65*4882a593Smuzhiyun /*
66*4882a593Smuzhiyun  * The meat of this driver. The core passes us a mode and we have to program
67*4882a593Smuzhiyun  * it. The modesetting here is the bare minimum required to satisfy the qemu
68*4882a593Smuzhiyun  * emulation of this hardware, and running this against a real device is
69*4882a593Smuzhiyun  * likely to result in an inadequately programmed mode. We've already had
70*4882a593Smuzhiyun  * the opportunity to modify the mode, so whatever we receive here should
71*4882a593Smuzhiyun  * be something that can be correctly programmed and displayed
72*4882a593Smuzhiyun  */
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun #define SEQ_INDEX 4
75*4882a593Smuzhiyun #define SEQ_DATA 5
76*4882a593Smuzhiyun 
rreg_seq(struct cirrus_device * cirrus,u8 reg)77*4882a593Smuzhiyun static u8 rreg_seq(struct cirrus_device *cirrus, u8 reg)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun 	iowrite8(reg, cirrus->mmio + SEQ_INDEX);
80*4882a593Smuzhiyun 	return ioread8(cirrus->mmio + SEQ_DATA);
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun 
wreg_seq(struct cirrus_device * cirrus,u8 reg,u8 val)83*4882a593Smuzhiyun static void wreg_seq(struct cirrus_device *cirrus, u8 reg, u8 val)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun 	iowrite8(reg, cirrus->mmio + SEQ_INDEX);
86*4882a593Smuzhiyun 	iowrite8(val, cirrus->mmio + SEQ_DATA);
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun #define CRT_INDEX 0x14
90*4882a593Smuzhiyun #define CRT_DATA 0x15
91*4882a593Smuzhiyun 
rreg_crt(struct cirrus_device * cirrus,u8 reg)92*4882a593Smuzhiyun static u8 rreg_crt(struct cirrus_device *cirrus, u8 reg)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun 	iowrite8(reg, cirrus->mmio + CRT_INDEX);
95*4882a593Smuzhiyun 	return ioread8(cirrus->mmio + CRT_DATA);
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun 
wreg_crt(struct cirrus_device * cirrus,u8 reg,u8 val)98*4882a593Smuzhiyun static void wreg_crt(struct cirrus_device *cirrus, u8 reg, u8 val)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun 	iowrite8(reg, cirrus->mmio + CRT_INDEX);
101*4882a593Smuzhiyun 	iowrite8(val, cirrus->mmio + CRT_DATA);
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun #define GFX_INDEX 0xe
105*4882a593Smuzhiyun #define GFX_DATA 0xf
106*4882a593Smuzhiyun 
wreg_gfx(struct cirrus_device * cirrus,u8 reg,u8 val)107*4882a593Smuzhiyun static void wreg_gfx(struct cirrus_device *cirrus, u8 reg, u8 val)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun 	iowrite8(reg, cirrus->mmio + GFX_INDEX);
110*4882a593Smuzhiyun 	iowrite8(val, cirrus->mmio + GFX_DATA);
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun #define VGA_DAC_MASK  0x06
114*4882a593Smuzhiyun 
wreg_hdr(struct cirrus_device * cirrus,u8 val)115*4882a593Smuzhiyun static void wreg_hdr(struct cirrus_device *cirrus, u8 val)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun 	ioread8(cirrus->mmio + VGA_DAC_MASK);
118*4882a593Smuzhiyun 	ioread8(cirrus->mmio + VGA_DAC_MASK);
119*4882a593Smuzhiyun 	ioread8(cirrus->mmio + VGA_DAC_MASK);
120*4882a593Smuzhiyun 	ioread8(cirrus->mmio + VGA_DAC_MASK);
121*4882a593Smuzhiyun 	iowrite8(val, cirrus->mmio + VGA_DAC_MASK);
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun 
cirrus_convert_to(struct drm_framebuffer * fb)124*4882a593Smuzhiyun static int cirrus_convert_to(struct drm_framebuffer *fb)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun 	if (fb->format->cpp[0] == 4 && fb->pitches[0] > CIRRUS_MAX_PITCH) {
127*4882a593Smuzhiyun 		if (fb->width * 3 <= CIRRUS_MAX_PITCH)
128*4882a593Smuzhiyun 			/* convert from XR24 to RG24 */
129*4882a593Smuzhiyun 			return 3;
130*4882a593Smuzhiyun 		else
131*4882a593Smuzhiyun 			/* convert from XR24 to RG16 */
132*4882a593Smuzhiyun 			return 2;
133*4882a593Smuzhiyun 	}
134*4882a593Smuzhiyun 	return 0;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun 
cirrus_cpp(struct drm_framebuffer * fb)137*4882a593Smuzhiyun static int cirrus_cpp(struct drm_framebuffer *fb)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 	int convert_cpp = cirrus_convert_to(fb);
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	if (convert_cpp)
142*4882a593Smuzhiyun 		return convert_cpp;
143*4882a593Smuzhiyun 	return fb->format->cpp[0];
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
cirrus_pitch(struct drm_framebuffer * fb)146*4882a593Smuzhiyun static int cirrus_pitch(struct drm_framebuffer *fb)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun 	int convert_cpp = cirrus_convert_to(fb);
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	if (convert_cpp)
151*4882a593Smuzhiyun 		return convert_cpp * fb->width;
152*4882a593Smuzhiyun 	return fb->pitches[0];
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
cirrus_set_start_address(struct cirrus_device * cirrus,u32 offset)155*4882a593Smuzhiyun static void cirrus_set_start_address(struct cirrus_device *cirrus, u32 offset)
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun 	int idx;
158*4882a593Smuzhiyun 	u32 addr;
159*4882a593Smuzhiyun 	u8 tmp;
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	if (!drm_dev_enter(&cirrus->dev, &idx))
162*4882a593Smuzhiyun 		return;
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	addr = offset >> 2;
165*4882a593Smuzhiyun 	wreg_crt(cirrus, 0x0c, (u8)((addr >> 8) & 0xff));
166*4882a593Smuzhiyun 	wreg_crt(cirrus, 0x0d, (u8)(addr & 0xff));
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	tmp = rreg_crt(cirrus, 0x1b);
169*4882a593Smuzhiyun 	tmp &= 0xf2;
170*4882a593Smuzhiyun 	tmp |= (addr >> 16) & 0x01;
171*4882a593Smuzhiyun 	tmp |= (addr >> 15) & 0x0c;
172*4882a593Smuzhiyun 	wreg_crt(cirrus, 0x1b, tmp);
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 	tmp = rreg_crt(cirrus, 0x1d);
175*4882a593Smuzhiyun 	tmp &= 0x7f;
176*4882a593Smuzhiyun 	tmp |= (addr >> 12) & 0x80;
177*4882a593Smuzhiyun 	wreg_crt(cirrus, 0x1d, tmp);
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	drm_dev_exit(idx);
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun 
cirrus_mode_set(struct cirrus_device * cirrus,struct drm_display_mode * mode,struct drm_framebuffer * fb)182*4882a593Smuzhiyun static int cirrus_mode_set(struct cirrus_device *cirrus,
183*4882a593Smuzhiyun 			   struct drm_display_mode *mode,
184*4882a593Smuzhiyun 			   struct drm_framebuffer *fb)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun 	int hsyncstart, hsyncend, htotal, hdispend;
187*4882a593Smuzhiyun 	int vtotal, vdispend;
188*4882a593Smuzhiyun 	int tmp, idx;
189*4882a593Smuzhiyun 	int sr07 = 0, hdr = 0;
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	if (!drm_dev_enter(&cirrus->dev, &idx))
192*4882a593Smuzhiyun 		return -1;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	htotal = mode->htotal / 8;
195*4882a593Smuzhiyun 	hsyncend = mode->hsync_end / 8;
196*4882a593Smuzhiyun 	hsyncstart = mode->hsync_start / 8;
197*4882a593Smuzhiyun 	hdispend = mode->hdisplay / 8;
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	vtotal = mode->vtotal;
200*4882a593Smuzhiyun 	vdispend = mode->vdisplay;
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	vdispend -= 1;
203*4882a593Smuzhiyun 	vtotal -= 2;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	htotal -= 5;
206*4882a593Smuzhiyun 	hdispend -= 1;
207*4882a593Smuzhiyun 	hsyncstart += 1;
208*4882a593Smuzhiyun 	hsyncend += 1;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	wreg_crt(cirrus, VGA_CRTC_V_SYNC_END, 0x20);
211*4882a593Smuzhiyun 	wreg_crt(cirrus, VGA_CRTC_H_TOTAL, htotal);
212*4882a593Smuzhiyun 	wreg_crt(cirrus, VGA_CRTC_H_DISP, hdispend);
213*4882a593Smuzhiyun 	wreg_crt(cirrus, VGA_CRTC_H_SYNC_START, hsyncstart);
214*4882a593Smuzhiyun 	wreg_crt(cirrus, VGA_CRTC_H_SYNC_END, hsyncend);
215*4882a593Smuzhiyun 	wreg_crt(cirrus, VGA_CRTC_V_TOTAL, vtotal & 0xff);
216*4882a593Smuzhiyun 	wreg_crt(cirrus, VGA_CRTC_V_DISP_END, vdispend & 0xff);
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	tmp = 0x40;
219*4882a593Smuzhiyun 	if ((vdispend + 1) & 512)
220*4882a593Smuzhiyun 		tmp |= 0x20;
221*4882a593Smuzhiyun 	wreg_crt(cirrus, VGA_CRTC_MAX_SCAN, tmp);
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	/*
224*4882a593Smuzhiyun 	 * Overflow bits for values that don't fit in the standard registers
225*4882a593Smuzhiyun 	 */
226*4882a593Smuzhiyun 	tmp = 0x10;
227*4882a593Smuzhiyun 	if (vtotal & 0x100)
228*4882a593Smuzhiyun 		tmp |= 0x01;
229*4882a593Smuzhiyun 	if (vdispend & 0x100)
230*4882a593Smuzhiyun 		tmp |= 0x02;
231*4882a593Smuzhiyun 	if ((vdispend + 1) & 0x100)
232*4882a593Smuzhiyun 		tmp |= 0x08;
233*4882a593Smuzhiyun 	if (vtotal & 0x200)
234*4882a593Smuzhiyun 		tmp |= 0x20;
235*4882a593Smuzhiyun 	if (vdispend & 0x200)
236*4882a593Smuzhiyun 		tmp |= 0x40;
237*4882a593Smuzhiyun 	wreg_crt(cirrus, VGA_CRTC_OVERFLOW, tmp);
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	tmp = 0;
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	/* More overflow bits */
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	if ((htotal + 5) & 0x40)
244*4882a593Smuzhiyun 		tmp |= 0x10;
245*4882a593Smuzhiyun 	if ((htotal + 5) & 0x80)
246*4882a593Smuzhiyun 		tmp |= 0x20;
247*4882a593Smuzhiyun 	if (vtotal & 0x100)
248*4882a593Smuzhiyun 		tmp |= 0x40;
249*4882a593Smuzhiyun 	if (vtotal & 0x200)
250*4882a593Smuzhiyun 		tmp |= 0x80;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	wreg_crt(cirrus, CL_CRT1A, tmp);
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	/* Disable Hercules/CGA compatibility */
255*4882a593Smuzhiyun 	wreg_crt(cirrus, VGA_CRTC_MODE, 0x03);
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	sr07 = rreg_seq(cirrus, 0x07);
258*4882a593Smuzhiyun 	sr07 &= 0xe0;
259*4882a593Smuzhiyun 	hdr = 0;
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	cirrus->cpp = cirrus_cpp(fb);
262*4882a593Smuzhiyun 	switch (cirrus->cpp * 8) {
263*4882a593Smuzhiyun 	case 8:
264*4882a593Smuzhiyun 		sr07 |= 0x11;
265*4882a593Smuzhiyun 		break;
266*4882a593Smuzhiyun 	case 16:
267*4882a593Smuzhiyun 		sr07 |= 0x17;
268*4882a593Smuzhiyun 		hdr = 0xc1;
269*4882a593Smuzhiyun 		break;
270*4882a593Smuzhiyun 	case 24:
271*4882a593Smuzhiyun 		sr07 |= 0x15;
272*4882a593Smuzhiyun 		hdr = 0xc5;
273*4882a593Smuzhiyun 		break;
274*4882a593Smuzhiyun 	case 32:
275*4882a593Smuzhiyun 		sr07 |= 0x19;
276*4882a593Smuzhiyun 		hdr = 0xc5;
277*4882a593Smuzhiyun 		break;
278*4882a593Smuzhiyun 	default:
279*4882a593Smuzhiyun 		drm_dev_exit(idx);
280*4882a593Smuzhiyun 		return -1;
281*4882a593Smuzhiyun 	}
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	wreg_seq(cirrus, 0x7, sr07);
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	/* Program the pitch */
286*4882a593Smuzhiyun 	cirrus->pitch = cirrus_pitch(fb);
287*4882a593Smuzhiyun 	tmp = cirrus->pitch / 8;
288*4882a593Smuzhiyun 	wreg_crt(cirrus, VGA_CRTC_OFFSET, tmp);
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	/* Enable extended blanking and pitch bits, and enable full memory */
291*4882a593Smuzhiyun 	tmp = 0x22;
292*4882a593Smuzhiyun 	tmp |= (cirrus->pitch >> 7) & 0x10;
293*4882a593Smuzhiyun 	tmp |= (cirrus->pitch >> 6) & 0x40;
294*4882a593Smuzhiyun 	wreg_crt(cirrus, 0x1b, tmp);
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	/* Enable high-colour modes */
297*4882a593Smuzhiyun 	wreg_gfx(cirrus, VGA_GFX_MODE, 0x40);
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	/* And set graphics mode */
300*4882a593Smuzhiyun 	wreg_gfx(cirrus, VGA_GFX_MISC, 0x01);
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	wreg_hdr(cirrus, hdr);
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	cirrus_set_start_address(cirrus, 0);
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	/* Unblank (needed on S3 resume, vgabios doesn't do it then) */
307*4882a593Smuzhiyun 	outb(0x20, 0x3c0);
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	drm_dev_exit(idx);
310*4882a593Smuzhiyun 	return 0;
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun 
cirrus_fb_blit_rect(struct drm_framebuffer * fb,struct drm_rect * rect)313*4882a593Smuzhiyun static int cirrus_fb_blit_rect(struct drm_framebuffer *fb,
314*4882a593Smuzhiyun 			       struct drm_rect *rect)
315*4882a593Smuzhiyun {
316*4882a593Smuzhiyun 	struct cirrus_device *cirrus = to_cirrus(fb->dev);
317*4882a593Smuzhiyun 	void *vmap;
318*4882a593Smuzhiyun 	int idx, ret;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	ret = -ENODEV;
321*4882a593Smuzhiyun 	if (!drm_dev_enter(&cirrus->dev, &idx))
322*4882a593Smuzhiyun 		goto out;
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	ret = -ENOMEM;
325*4882a593Smuzhiyun 	vmap = drm_gem_shmem_vmap(fb->obj[0]);
326*4882a593Smuzhiyun 	if (!vmap)
327*4882a593Smuzhiyun 		goto out_dev_exit;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	if (cirrus->cpp == fb->format->cpp[0])
330*4882a593Smuzhiyun 		drm_fb_memcpy_dstclip(cirrus->vram,
331*4882a593Smuzhiyun 				      vmap, fb, rect);
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	else if (fb->format->cpp[0] == 4 && cirrus->cpp == 2)
334*4882a593Smuzhiyun 		drm_fb_xrgb8888_to_rgb565_dstclip(cirrus->vram,
335*4882a593Smuzhiyun 						  cirrus->pitch,
336*4882a593Smuzhiyun 						  vmap, fb, rect, false);
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	else if (fb->format->cpp[0] == 4 && cirrus->cpp == 3)
339*4882a593Smuzhiyun 		drm_fb_xrgb8888_to_rgb888_dstclip(cirrus->vram,
340*4882a593Smuzhiyun 						  cirrus->pitch,
341*4882a593Smuzhiyun 						  vmap, fb, rect);
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	else
344*4882a593Smuzhiyun 		WARN_ON_ONCE("cpp mismatch");
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	drm_gem_shmem_vunmap(fb->obj[0], vmap);
347*4882a593Smuzhiyun 	ret = 0;
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun out_dev_exit:
350*4882a593Smuzhiyun 	drm_dev_exit(idx);
351*4882a593Smuzhiyun out:
352*4882a593Smuzhiyun 	return ret;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun 
cirrus_fb_blit_fullscreen(struct drm_framebuffer * fb)355*4882a593Smuzhiyun static int cirrus_fb_blit_fullscreen(struct drm_framebuffer *fb)
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun 	struct drm_rect fullscreen = {
358*4882a593Smuzhiyun 		.x1 = 0,
359*4882a593Smuzhiyun 		.x2 = fb->width,
360*4882a593Smuzhiyun 		.y1 = 0,
361*4882a593Smuzhiyun 		.y2 = fb->height,
362*4882a593Smuzhiyun 	};
363*4882a593Smuzhiyun 	return cirrus_fb_blit_rect(fb, &fullscreen);
364*4882a593Smuzhiyun }
365*4882a593Smuzhiyun 
cirrus_check_size(int width,int height,struct drm_framebuffer * fb)366*4882a593Smuzhiyun static int cirrus_check_size(int width, int height,
367*4882a593Smuzhiyun 			     struct drm_framebuffer *fb)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun 	int pitch = width * 2;
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	if (fb)
372*4882a593Smuzhiyun 		pitch = cirrus_pitch(fb);
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	if (pitch > CIRRUS_MAX_PITCH)
375*4882a593Smuzhiyun 		return -EINVAL;
376*4882a593Smuzhiyun 	if (pitch * height > CIRRUS_VRAM_SIZE)
377*4882a593Smuzhiyun 		return -EINVAL;
378*4882a593Smuzhiyun 	return 0;
379*4882a593Smuzhiyun }
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
382*4882a593Smuzhiyun /* cirrus connector						      */
383*4882a593Smuzhiyun 
cirrus_conn_get_modes(struct drm_connector * conn)384*4882a593Smuzhiyun static int cirrus_conn_get_modes(struct drm_connector *conn)
385*4882a593Smuzhiyun {
386*4882a593Smuzhiyun 	int count;
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	count = drm_add_modes_noedid(conn,
389*4882a593Smuzhiyun 				     conn->dev->mode_config.max_width,
390*4882a593Smuzhiyun 				     conn->dev->mode_config.max_height);
391*4882a593Smuzhiyun 	drm_set_preferred_mode(conn, 1024, 768);
392*4882a593Smuzhiyun 	return count;
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun static const struct drm_connector_helper_funcs cirrus_conn_helper_funcs = {
396*4882a593Smuzhiyun 	.get_modes = cirrus_conn_get_modes,
397*4882a593Smuzhiyun };
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun static const struct drm_connector_funcs cirrus_conn_funcs = {
400*4882a593Smuzhiyun 	.fill_modes = drm_helper_probe_single_connector_modes,
401*4882a593Smuzhiyun 	.destroy = drm_connector_cleanup,
402*4882a593Smuzhiyun 	.reset = drm_atomic_helper_connector_reset,
403*4882a593Smuzhiyun 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
404*4882a593Smuzhiyun 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
405*4882a593Smuzhiyun };
406*4882a593Smuzhiyun 
cirrus_conn_init(struct cirrus_device * cirrus)407*4882a593Smuzhiyun static int cirrus_conn_init(struct cirrus_device *cirrus)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun 	drm_connector_helper_add(&cirrus->conn, &cirrus_conn_helper_funcs);
410*4882a593Smuzhiyun 	return drm_connector_init(&cirrus->dev, &cirrus->conn,
411*4882a593Smuzhiyun 				  &cirrus_conn_funcs, DRM_MODE_CONNECTOR_VGA);
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun }
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
416*4882a593Smuzhiyun /* cirrus (simple) display pipe					      */
417*4882a593Smuzhiyun 
cirrus_pipe_mode_valid(struct drm_simple_display_pipe * pipe,const struct drm_display_mode * mode)418*4882a593Smuzhiyun static enum drm_mode_status cirrus_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
419*4882a593Smuzhiyun 						   const struct drm_display_mode *mode)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun 	if (cirrus_check_size(mode->hdisplay, mode->vdisplay, NULL) < 0)
422*4882a593Smuzhiyun 		return MODE_BAD;
423*4882a593Smuzhiyun 	return MODE_OK;
424*4882a593Smuzhiyun }
425*4882a593Smuzhiyun 
cirrus_pipe_check(struct drm_simple_display_pipe * pipe,struct drm_plane_state * plane_state,struct drm_crtc_state * crtc_state)426*4882a593Smuzhiyun static int cirrus_pipe_check(struct drm_simple_display_pipe *pipe,
427*4882a593Smuzhiyun 			     struct drm_plane_state *plane_state,
428*4882a593Smuzhiyun 			     struct drm_crtc_state *crtc_state)
429*4882a593Smuzhiyun {
430*4882a593Smuzhiyun 	struct drm_framebuffer *fb = plane_state->fb;
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	if (!fb)
433*4882a593Smuzhiyun 		return 0;
434*4882a593Smuzhiyun 	return cirrus_check_size(fb->width, fb->height, fb);
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun 
cirrus_pipe_enable(struct drm_simple_display_pipe * pipe,struct drm_crtc_state * crtc_state,struct drm_plane_state * plane_state)437*4882a593Smuzhiyun static void cirrus_pipe_enable(struct drm_simple_display_pipe *pipe,
438*4882a593Smuzhiyun 			       struct drm_crtc_state *crtc_state,
439*4882a593Smuzhiyun 			       struct drm_plane_state *plane_state)
440*4882a593Smuzhiyun {
441*4882a593Smuzhiyun 	struct cirrus_device *cirrus = to_cirrus(pipe->crtc.dev);
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 	cirrus_mode_set(cirrus, &crtc_state->mode, plane_state->fb);
444*4882a593Smuzhiyun 	cirrus_fb_blit_fullscreen(plane_state->fb);
445*4882a593Smuzhiyun }
446*4882a593Smuzhiyun 
cirrus_pipe_update(struct drm_simple_display_pipe * pipe,struct drm_plane_state * old_state)447*4882a593Smuzhiyun static void cirrus_pipe_update(struct drm_simple_display_pipe *pipe,
448*4882a593Smuzhiyun 			       struct drm_plane_state *old_state)
449*4882a593Smuzhiyun {
450*4882a593Smuzhiyun 	struct cirrus_device *cirrus = to_cirrus(pipe->crtc.dev);
451*4882a593Smuzhiyun 	struct drm_plane_state *state = pipe->plane.state;
452*4882a593Smuzhiyun 	struct drm_crtc *crtc = &pipe->crtc;
453*4882a593Smuzhiyun 	struct drm_rect rect;
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun 	if (pipe->plane.state->fb &&
456*4882a593Smuzhiyun 	    cirrus->cpp != cirrus_cpp(pipe->plane.state->fb))
457*4882a593Smuzhiyun 		cirrus_mode_set(cirrus, &crtc->mode,
458*4882a593Smuzhiyun 				pipe->plane.state->fb);
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	if (drm_atomic_helper_damage_merged(old_state, state, &rect))
461*4882a593Smuzhiyun 		cirrus_fb_blit_rect(pipe->plane.state->fb, &rect);
462*4882a593Smuzhiyun }
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun static const struct drm_simple_display_pipe_funcs cirrus_pipe_funcs = {
465*4882a593Smuzhiyun 	.mode_valid = cirrus_pipe_mode_valid,
466*4882a593Smuzhiyun 	.check	    = cirrus_pipe_check,
467*4882a593Smuzhiyun 	.enable	    = cirrus_pipe_enable,
468*4882a593Smuzhiyun 	.update	    = cirrus_pipe_update,
469*4882a593Smuzhiyun };
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun static const uint32_t cirrus_formats[] = {
472*4882a593Smuzhiyun 	DRM_FORMAT_RGB565,
473*4882a593Smuzhiyun 	DRM_FORMAT_RGB888,
474*4882a593Smuzhiyun 	DRM_FORMAT_XRGB8888,
475*4882a593Smuzhiyun };
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun static const uint64_t cirrus_modifiers[] = {
478*4882a593Smuzhiyun 	DRM_FORMAT_MOD_LINEAR,
479*4882a593Smuzhiyun 	DRM_FORMAT_MOD_INVALID
480*4882a593Smuzhiyun };
481*4882a593Smuzhiyun 
cirrus_pipe_init(struct cirrus_device * cirrus)482*4882a593Smuzhiyun static int cirrus_pipe_init(struct cirrus_device *cirrus)
483*4882a593Smuzhiyun {
484*4882a593Smuzhiyun 	return drm_simple_display_pipe_init(&cirrus->dev,
485*4882a593Smuzhiyun 					    &cirrus->pipe,
486*4882a593Smuzhiyun 					    &cirrus_pipe_funcs,
487*4882a593Smuzhiyun 					    cirrus_formats,
488*4882a593Smuzhiyun 					    ARRAY_SIZE(cirrus_formats),
489*4882a593Smuzhiyun 					    cirrus_modifiers,
490*4882a593Smuzhiyun 					    &cirrus->conn);
491*4882a593Smuzhiyun }
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
494*4882a593Smuzhiyun /* cirrus framebuffers & mode config				      */
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun static struct drm_framebuffer*
cirrus_fb_create(struct drm_device * dev,struct drm_file * file_priv,const struct drm_mode_fb_cmd2 * mode_cmd)497*4882a593Smuzhiyun cirrus_fb_create(struct drm_device *dev, struct drm_file *file_priv,
498*4882a593Smuzhiyun 		 const struct drm_mode_fb_cmd2 *mode_cmd)
499*4882a593Smuzhiyun {
500*4882a593Smuzhiyun 	if (mode_cmd->pixel_format != DRM_FORMAT_RGB565 &&
501*4882a593Smuzhiyun 	    mode_cmd->pixel_format != DRM_FORMAT_RGB888 &&
502*4882a593Smuzhiyun 	    mode_cmd->pixel_format != DRM_FORMAT_XRGB8888)
503*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
504*4882a593Smuzhiyun 	if (cirrus_check_size(mode_cmd->width, mode_cmd->height, NULL) < 0)
505*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
506*4882a593Smuzhiyun 	return drm_gem_fb_create_with_dirty(dev, file_priv, mode_cmd);
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun static const struct drm_mode_config_funcs cirrus_mode_config_funcs = {
510*4882a593Smuzhiyun 	.fb_create = cirrus_fb_create,
511*4882a593Smuzhiyun 	.atomic_check = drm_atomic_helper_check,
512*4882a593Smuzhiyun 	.atomic_commit = drm_atomic_helper_commit,
513*4882a593Smuzhiyun };
514*4882a593Smuzhiyun 
cirrus_mode_config_init(struct cirrus_device * cirrus)515*4882a593Smuzhiyun static int cirrus_mode_config_init(struct cirrus_device *cirrus)
516*4882a593Smuzhiyun {
517*4882a593Smuzhiyun 	struct drm_device *dev = &cirrus->dev;
518*4882a593Smuzhiyun 	int ret;
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	ret = drmm_mode_config_init(dev);
521*4882a593Smuzhiyun 	if (ret)
522*4882a593Smuzhiyun 		return ret;
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	dev->mode_config.min_width = 0;
525*4882a593Smuzhiyun 	dev->mode_config.min_height = 0;
526*4882a593Smuzhiyun 	dev->mode_config.max_width = CIRRUS_MAX_PITCH / 2;
527*4882a593Smuzhiyun 	dev->mode_config.max_height = 1024;
528*4882a593Smuzhiyun 	dev->mode_config.preferred_depth = 16;
529*4882a593Smuzhiyun 	dev->mode_config.prefer_shadow = 0;
530*4882a593Smuzhiyun 	dev->mode_config.funcs = &cirrus_mode_config_funcs;
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	return 0;
533*4882a593Smuzhiyun }
534*4882a593Smuzhiyun 
535*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun DEFINE_DRM_GEM_FOPS(cirrus_fops);
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun static struct drm_driver cirrus_driver = {
540*4882a593Smuzhiyun 	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	.name		 = DRIVER_NAME,
543*4882a593Smuzhiyun 	.desc		 = DRIVER_DESC,
544*4882a593Smuzhiyun 	.date		 = DRIVER_DATE,
545*4882a593Smuzhiyun 	.major		 = DRIVER_MAJOR,
546*4882a593Smuzhiyun 	.minor		 = DRIVER_MINOR,
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun 	.fops		 = &cirrus_fops,
549*4882a593Smuzhiyun 	DRM_GEM_SHMEM_DRIVER_OPS,
550*4882a593Smuzhiyun };
551*4882a593Smuzhiyun 
cirrus_pci_probe(struct pci_dev * pdev,const struct pci_device_id * ent)552*4882a593Smuzhiyun static int cirrus_pci_probe(struct pci_dev *pdev,
553*4882a593Smuzhiyun 			    const struct pci_device_id *ent)
554*4882a593Smuzhiyun {
555*4882a593Smuzhiyun 	struct drm_device *dev;
556*4882a593Smuzhiyun 	struct cirrus_device *cirrus;
557*4882a593Smuzhiyun 	int ret;
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 	ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, "cirrusdrmfb");
560*4882a593Smuzhiyun 	if (ret)
561*4882a593Smuzhiyun 		return ret;
562*4882a593Smuzhiyun 
563*4882a593Smuzhiyun 	ret = pcim_enable_device(pdev);
564*4882a593Smuzhiyun 	if (ret)
565*4882a593Smuzhiyun 		return ret;
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	ret = pci_request_regions(pdev, DRIVER_NAME);
568*4882a593Smuzhiyun 	if (ret)
569*4882a593Smuzhiyun 		return ret;
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun 	ret = -ENOMEM;
572*4882a593Smuzhiyun 	cirrus = devm_drm_dev_alloc(&pdev->dev, &cirrus_driver,
573*4882a593Smuzhiyun 				    struct cirrus_device, dev);
574*4882a593Smuzhiyun 	if (IS_ERR(cirrus))
575*4882a593Smuzhiyun 		return PTR_ERR(cirrus);
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 	dev = &cirrus->dev;
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 	cirrus->vram = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0),
580*4882a593Smuzhiyun 				    pci_resource_len(pdev, 0));
581*4882a593Smuzhiyun 	if (cirrus->vram == NULL)
582*4882a593Smuzhiyun 		return -ENOMEM;
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 	cirrus->mmio = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 1),
585*4882a593Smuzhiyun 				    pci_resource_len(pdev, 1));
586*4882a593Smuzhiyun 	if (cirrus->mmio == NULL)
587*4882a593Smuzhiyun 		return -ENOMEM;
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	ret = cirrus_mode_config_init(cirrus);
590*4882a593Smuzhiyun 	if (ret)
591*4882a593Smuzhiyun 		return ret;
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	ret = cirrus_conn_init(cirrus);
594*4882a593Smuzhiyun 	if (ret < 0)
595*4882a593Smuzhiyun 		return ret;
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	ret = cirrus_pipe_init(cirrus);
598*4882a593Smuzhiyun 	if (ret < 0)
599*4882a593Smuzhiyun 		return ret;
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	drm_mode_config_reset(dev);
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 	dev->pdev = pdev;
604*4882a593Smuzhiyun 	pci_set_drvdata(pdev, dev);
605*4882a593Smuzhiyun 	ret = drm_dev_register(dev, 0);
606*4882a593Smuzhiyun 	if (ret)
607*4882a593Smuzhiyun 		return ret;
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun 	drm_fbdev_generic_setup(dev, dev->mode_config.preferred_depth);
610*4882a593Smuzhiyun 	return 0;
611*4882a593Smuzhiyun }
612*4882a593Smuzhiyun 
cirrus_pci_remove(struct pci_dev * pdev)613*4882a593Smuzhiyun static void cirrus_pci_remove(struct pci_dev *pdev)
614*4882a593Smuzhiyun {
615*4882a593Smuzhiyun 	struct drm_device *dev = pci_get_drvdata(pdev);
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun 	drm_dev_unplug(dev);
618*4882a593Smuzhiyun 	drm_atomic_helper_shutdown(dev);
619*4882a593Smuzhiyun }
620*4882a593Smuzhiyun 
621*4882a593Smuzhiyun static const struct pci_device_id pciidlist[] = {
622*4882a593Smuzhiyun 	{
623*4882a593Smuzhiyun 		.vendor    = PCI_VENDOR_ID_CIRRUS,
624*4882a593Smuzhiyun 		.device    = PCI_DEVICE_ID_CIRRUS_5446,
625*4882a593Smuzhiyun 		/* only bind to the cirrus chip in qemu */
626*4882a593Smuzhiyun 		.subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET,
627*4882a593Smuzhiyun 		.subdevice = PCI_SUBDEVICE_ID_QEMU,
628*4882a593Smuzhiyun 	}, {
629*4882a593Smuzhiyun 		.vendor    = PCI_VENDOR_ID_CIRRUS,
630*4882a593Smuzhiyun 		.device    = PCI_DEVICE_ID_CIRRUS_5446,
631*4882a593Smuzhiyun 		.subvendor = PCI_VENDOR_ID_XEN,
632*4882a593Smuzhiyun 		.subdevice = 0x0001,
633*4882a593Smuzhiyun 	},
634*4882a593Smuzhiyun 	{ /* end if list */ }
635*4882a593Smuzhiyun };
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun static struct pci_driver cirrus_pci_driver = {
638*4882a593Smuzhiyun 	.name = DRIVER_NAME,
639*4882a593Smuzhiyun 	.id_table = pciidlist,
640*4882a593Smuzhiyun 	.probe = cirrus_pci_probe,
641*4882a593Smuzhiyun 	.remove = cirrus_pci_remove,
642*4882a593Smuzhiyun };
643*4882a593Smuzhiyun 
cirrus_init(void)644*4882a593Smuzhiyun static int __init cirrus_init(void)
645*4882a593Smuzhiyun {
646*4882a593Smuzhiyun 	if (vgacon_text_force())
647*4882a593Smuzhiyun 		return -EINVAL;
648*4882a593Smuzhiyun 	return pci_register_driver(&cirrus_pci_driver);
649*4882a593Smuzhiyun }
650*4882a593Smuzhiyun 
cirrus_exit(void)651*4882a593Smuzhiyun static void __exit cirrus_exit(void)
652*4882a593Smuzhiyun {
653*4882a593Smuzhiyun 	pci_unregister_driver(&cirrus_pci_driver);
654*4882a593Smuzhiyun }
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun module_init(cirrus_init);
657*4882a593Smuzhiyun module_exit(cirrus_exit);
658*4882a593Smuzhiyun 
659*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, pciidlist);
660*4882a593Smuzhiyun MODULE_LICENSE("GPL");
661