xref: /linux/drivers/gpu/drm/tiny/cirrus-qemu.c (revision 5c3c99868aa2e0b68ac69f8050a6b9c994e73397)
1*5c3c9986SThomas Zimmermann /* SPDX-License-Identifier: GPL-2.0 */
2*5c3c9986SThomas Zimmermann /*
3*5c3c9986SThomas Zimmermann  * Copyright 2012-2019 Red Hat
4*5c3c9986SThomas Zimmermann  *
5*5c3c9986SThomas Zimmermann  * This file is subject to the terms and conditions of the GNU General
6*5c3c9986SThomas Zimmermann  * Public License version 2. See the file COPYING in the main
7*5c3c9986SThomas Zimmermann  * directory of this archive for more details.
8*5c3c9986SThomas Zimmermann  *
9*5c3c9986SThomas Zimmermann  * Authors: Matthew Garrett
10*5c3c9986SThomas Zimmermann  *	    Dave Airlie
11*5c3c9986SThomas Zimmermann  *	    Gerd Hoffmann
12*5c3c9986SThomas Zimmermann  *
13*5c3c9986SThomas Zimmermann  * Portions of this code derived from cirrusfb.c:
14*5c3c9986SThomas Zimmermann  * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
15*5c3c9986SThomas Zimmermann  *
16*5c3c9986SThomas Zimmermann  * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
17*5c3c9986SThomas Zimmermann  */
18*5c3c9986SThomas Zimmermann 
19*5c3c9986SThomas Zimmermann #include <linux/aperture.h>
20*5c3c9986SThomas Zimmermann #include <linux/iosys-map.h>
21*5c3c9986SThomas Zimmermann #include <linux/module.h>
22*5c3c9986SThomas Zimmermann #include <linux/pci.h>
23*5c3c9986SThomas Zimmermann 
24*5c3c9986SThomas Zimmermann #include <video/cirrus.h>
25*5c3c9986SThomas Zimmermann #include <video/vga.h>
26*5c3c9986SThomas Zimmermann 
27*5c3c9986SThomas Zimmermann #include <drm/clients/drm_client_setup.h>
28*5c3c9986SThomas Zimmermann #include <drm/drm_atomic.h>
29*5c3c9986SThomas Zimmermann #include <drm/drm_atomic_helper.h>
30*5c3c9986SThomas Zimmermann #include <drm/drm_atomic_state_helper.h>
31*5c3c9986SThomas Zimmermann #include <drm/drm_connector.h>
32*5c3c9986SThomas Zimmermann #include <drm/drm_damage_helper.h>
33*5c3c9986SThomas Zimmermann #include <drm/drm_drv.h>
34*5c3c9986SThomas Zimmermann #include <drm/drm_edid.h>
35*5c3c9986SThomas Zimmermann #include <drm/drm_fbdev_shmem.h>
36*5c3c9986SThomas Zimmermann #include <drm/drm_file.h>
37*5c3c9986SThomas Zimmermann #include <drm/drm_format_helper.h>
38*5c3c9986SThomas Zimmermann #include <drm/drm_fourcc.h>
39*5c3c9986SThomas Zimmermann #include <drm/drm_framebuffer.h>
40*5c3c9986SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
41*5c3c9986SThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h>
42*5c3c9986SThomas Zimmermann #include <drm/drm_gem_shmem_helper.h>
43*5c3c9986SThomas Zimmermann #include <drm/drm_ioctl.h>
44*5c3c9986SThomas Zimmermann #include <drm/drm_managed.h>
45*5c3c9986SThomas Zimmermann #include <drm/drm_modeset_helper_vtables.h>
46*5c3c9986SThomas Zimmermann #include <drm/drm_module.h>
47*5c3c9986SThomas Zimmermann #include <drm/drm_probe_helper.h>
48*5c3c9986SThomas Zimmermann 
49*5c3c9986SThomas Zimmermann #define DRIVER_NAME "cirrus-qemu"
50*5c3c9986SThomas Zimmermann #define DRIVER_DESC "qemu cirrus vga"
51*5c3c9986SThomas Zimmermann #define DRIVER_DATE "2019"
52*5c3c9986SThomas Zimmermann #define DRIVER_MAJOR 2
53*5c3c9986SThomas Zimmermann #define DRIVER_MINOR 0
54*5c3c9986SThomas Zimmermann 
55*5c3c9986SThomas Zimmermann #define CIRRUS_MAX_PITCH (0x1FF << 3)      /* (4096 - 1) & ~111b bytes */
56*5c3c9986SThomas Zimmermann #define CIRRUS_VRAM_SIZE (4 * 1024 * 1024) /* 4 MB */
57*5c3c9986SThomas Zimmermann 
58*5c3c9986SThomas Zimmermann struct cirrus_device {
59*5c3c9986SThomas Zimmermann 	struct drm_device	       dev;
60*5c3c9986SThomas Zimmermann 
61*5c3c9986SThomas Zimmermann 	/* modesetting pipeline */
62*5c3c9986SThomas Zimmermann 	struct drm_plane	       primary_plane;
63*5c3c9986SThomas Zimmermann 	struct drm_crtc		       crtc;
64*5c3c9986SThomas Zimmermann 	struct drm_encoder	       encoder;
65*5c3c9986SThomas Zimmermann 	struct drm_connector	       connector;
66*5c3c9986SThomas Zimmermann 
67*5c3c9986SThomas Zimmermann 	/* HW resources */
68*5c3c9986SThomas Zimmermann 	void __iomem		       *vram;
69*5c3c9986SThomas Zimmermann 	void __iomem		       *mmio;
70*5c3c9986SThomas Zimmermann };
71*5c3c9986SThomas Zimmermann 
72*5c3c9986SThomas Zimmermann #define to_cirrus(_dev) container_of(_dev, struct cirrus_device, dev)
73*5c3c9986SThomas Zimmermann 
74*5c3c9986SThomas Zimmermann struct cirrus_primary_plane_state {
75*5c3c9986SThomas Zimmermann 	struct drm_shadow_plane_state base;
76*5c3c9986SThomas Zimmermann 
77*5c3c9986SThomas Zimmermann 	/* HW scanout buffer */
78*5c3c9986SThomas Zimmermann 	const struct drm_format_info   *format;
79*5c3c9986SThomas Zimmermann 	unsigned int		       pitch;
80*5c3c9986SThomas Zimmermann };
81*5c3c9986SThomas Zimmermann 
82*5c3c9986SThomas Zimmermann static inline struct cirrus_primary_plane_state *
83*5c3c9986SThomas Zimmermann to_cirrus_primary_plane_state(struct drm_plane_state *plane_state)
84*5c3c9986SThomas Zimmermann {
85*5c3c9986SThomas Zimmermann 	return container_of(plane_state, struct cirrus_primary_plane_state, base.base);
86*5c3c9986SThomas Zimmermann };
87*5c3c9986SThomas Zimmermann 
88*5c3c9986SThomas Zimmermann /* ------------------------------------------------------------------ */
89*5c3c9986SThomas Zimmermann /*
90*5c3c9986SThomas Zimmermann  * The meat of this driver. The core passes us a mode and we have to program
91*5c3c9986SThomas Zimmermann  * it. The modesetting here is the bare minimum required to satisfy the qemu
92*5c3c9986SThomas Zimmermann  * emulation of this hardware, and running this against a real device is
93*5c3c9986SThomas Zimmermann  * likely to result in an inadequately programmed mode. We've already had
94*5c3c9986SThomas Zimmermann  * the opportunity to modify the mode, so whatever we receive here should
95*5c3c9986SThomas Zimmermann  * be something that can be correctly programmed and displayed
96*5c3c9986SThomas Zimmermann  */
97*5c3c9986SThomas Zimmermann 
98*5c3c9986SThomas Zimmermann #define SEQ_INDEX 4
99*5c3c9986SThomas Zimmermann #define SEQ_DATA 5
100*5c3c9986SThomas Zimmermann 
101*5c3c9986SThomas Zimmermann static u8 rreg_seq(struct cirrus_device *cirrus, u8 reg)
102*5c3c9986SThomas Zimmermann {
103*5c3c9986SThomas Zimmermann 	iowrite8(reg, cirrus->mmio + SEQ_INDEX);
104*5c3c9986SThomas Zimmermann 	return ioread8(cirrus->mmio + SEQ_DATA);
105*5c3c9986SThomas Zimmermann }
106*5c3c9986SThomas Zimmermann 
107*5c3c9986SThomas Zimmermann static void wreg_seq(struct cirrus_device *cirrus, u8 reg, u8 val)
108*5c3c9986SThomas Zimmermann {
109*5c3c9986SThomas Zimmermann 	iowrite8(reg, cirrus->mmio + SEQ_INDEX);
110*5c3c9986SThomas Zimmermann 	iowrite8(val, cirrus->mmio + SEQ_DATA);
111*5c3c9986SThomas Zimmermann }
112*5c3c9986SThomas Zimmermann 
113*5c3c9986SThomas Zimmermann #define CRT_INDEX 0x14
114*5c3c9986SThomas Zimmermann #define CRT_DATA 0x15
115*5c3c9986SThomas Zimmermann 
116*5c3c9986SThomas Zimmermann static u8 rreg_crt(struct cirrus_device *cirrus, u8 reg)
117*5c3c9986SThomas Zimmermann {
118*5c3c9986SThomas Zimmermann 	iowrite8(reg, cirrus->mmio + CRT_INDEX);
119*5c3c9986SThomas Zimmermann 	return ioread8(cirrus->mmio + CRT_DATA);
120*5c3c9986SThomas Zimmermann }
121*5c3c9986SThomas Zimmermann 
122*5c3c9986SThomas Zimmermann static void wreg_crt(struct cirrus_device *cirrus, u8 reg, u8 val)
123*5c3c9986SThomas Zimmermann {
124*5c3c9986SThomas Zimmermann 	iowrite8(reg, cirrus->mmio + CRT_INDEX);
125*5c3c9986SThomas Zimmermann 	iowrite8(val, cirrus->mmio + CRT_DATA);
126*5c3c9986SThomas Zimmermann }
127*5c3c9986SThomas Zimmermann 
128*5c3c9986SThomas Zimmermann #define GFX_INDEX 0xe
129*5c3c9986SThomas Zimmermann #define GFX_DATA 0xf
130*5c3c9986SThomas Zimmermann 
131*5c3c9986SThomas Zimmermann static void wreg_gfx(struct cirrus_device *cirrus, u8 reg, u8 val)
132*5c3c9986SThomas Zimmermann {
133*5c3c9986SThomas Zimmermann 	iowrite8(reg, cirrus->mmio + GFX_INDEX);
134*5c3c9986SThomas Zimmermann 	iowrite8(val, cirrus->mmio + GFX_DATA);
135*5c3c9986SThomas Zimmermann }
136*5c3c9986SThomas Zimmermann 
137*5c3c9986SThomas Zimmermann #define VGA_DAC_MASK  0x06
138*5c3c9986SThomas Zimmermann 
139*5c3c9986SThomas Zimmermann static void wreg_hdr(struct cirrus_device *cirrus, u8 val)
140*5c3c9986SThomas Zimmermann {
141*5c3c9986SThomas Zimmermann 	ioread8(cirrus->mmio + VGA_DAC_MASK);
142*5c3c9986SThomas Zimmermann 	ioread8(cirrus->mmio + VGA_DAC_MASK);
143*5c3c9986SThomas Zimmermann 	ioread8(cirrus->mmio + VGA_DAC_MASK);
144*5c3c9986SThomas Zimmermann 	ioread8(cirrus->mmio + VGA_DAC_MASK);
145*5c3c9986SThomas Zimmermann 	iowrite8(val, cirrus->mmio + VGA_DAC_MASK);
146*5c3c9986SThomas Zimmermann }
147*5c3c9986SThomas Zimmermann 
148*5c3c9986SThomas Zimmermann static const struct drm_format_info *cirrus_convert_to(struct drm_framebuffer *fb)
149*5c3c9986SThomas Zimmermann {
150*5c3c9986SThomas Zimmermann 	if (fb->format->format == DRM_FORMAT_XRGB8888 && fb->pitches[0] > CIRRUS_MAX_PITCH) {
151*5c3c9986SThomas Zimmermann 		if (fb->width * 3 <= CIRRUS_MAX_PITCH)
152*5c3c9986SThomas Zimmermann 			/* convert from XR24 to RG24 */
153*5c3c9986SThomas Zimmermann 			return drm_format_info(DRM_FORMAT_RGB888);
154*5c3c9986SThomas Zimmermann 		else
155*5c3c9986SThomas Zimmermann 			/* convert from XR24 to RG16 */
156*5c3c9986SThomas Zimmermann 			return drm_format_info(DRM_FORMAT_RGB565);
157*5c3c9986SThomas Zimmermann 	}
158*5c3c9986SThomas Zimmermann 	return NULL;
159*5c3c9986SThomas Zimmermann }
160*5c3c9986SThomas Zimmermann 
161*5c3c9986SThomas Zimmermann static const struct drm_format_info *cirrus_format(struct drm_framebuffer *fb)
162*5c3c9986SThomas Zimmermann {
163*5c3c9986SThomas Zimmermann 	const struct drm_format_info *format = cirrus_convert_to(fb);
164*5c3c9986SThomas Zimmermann 
165*5c3c9986SThomas Zimmermann 	if (format)
166*5c3c9986SThomas Zimmermann 		return format;
167*5c3c9986SThomas Zimmermann 	return fb->format;
168*5c3c9986SThomas Zimmermann }
169*5c3c9986SThomas Zimmermann 
170*5c3c9986SThomas Zimmermann static int cirrus_pitch(struct drm_framebuffer *fb)
171*5c3c9986SThomas Zimmermann {
172*5c3c9986SThomas Zimmermann 	const struct drm_format_info *format = cirrus_convert_to(fb);
173*5c3c9986SThomas Zimmermann 
174*5c3c9986SThomas Zimmermann 	if (format)
175*5c3c9986SThomas Zimmermann 		return drm_format_info_min_pitch(format, 0, fb->width);
176*5c3c9986SThomas Zimmermann 	return fb->pitches[0];
177*5c3c9986SThomas Zimmermann }
178*5c3c9986SThomas Zimmermann 
179*5c3c9986SThomas Zimmermann static void cirrus_set_start_address(struct cirrus_device *cirrus, u32 offset)
180*5c3c9986SThomas Zimmermann {
181*5c3c9986SThomas Zimmermann 	u32 addr;
182*5c3c9986SThomas Zimmermann 	u8 tmp;
183*5c3c9986SThomas Zimmermann 
184*5c3c9986SThomas Zimmermann 	addr = offset >> 2;
185*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, 0x0c, (u8)((addr >> 8) & 0xff));
186*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, 0x0d, (u8)(addr & 0xff));
187*5c3c9986SThomas Zimmermann 
188*5c3c9986SThomas Zimmermann 	tmp = rreg_crt(cirrus, 0x1b);
189*5c3c9986SThomas Zimmermann 	tmp &= 0xf2;
190*5c3c9986SThomas Zimmermann 	tmp |= (addr >> 16) & 0x01;
191*5c3c9986SThomas Zimmermann 	tmp |= (addr >> 15) & 0x0c;
192*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, 0x1b, tmp);
193*5c3c9986SThomas Zimmermann 
194*5c3c9986SThomas Zimmermann 	tmp = rreg_crt(cirrus, 0x1d);
195*5c3c9986SThomas Zimmermann 	tmp &= 0x7f;
196*5c3c9986SThomas Zimmermann 	tmp |= (addr >> 12) & 0x80;
197*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, 0x1d, tmp);
198*5c3c9986SThomas Zimmermann }
199*5c3c9986SThomas Zimmermann 
200*5c3c9986SThomas Zimmermann static void cirrus_mode_set(struct cirrus_device *cirrus,
201*5c3c9986SThomas Zimmermann 			    struct drm_display_mode *mode)
202*5c3c9986SThomas Zimmermann {
203*5c3c9986SThomas Zimmermann 	int hsyncstart, hsyncend, htotal, hdispend;
204*5c3c9986SThomas Zimmermann 	int vtotal, vdispend;
205*5c3c9986SThomas Zimmermann 	int tmp;
206*5c3c9986SThomas Zimmermann 
207*5c3c9986SThomas Zimmermann 	htotal = mode->htotal / 8;
208*5c3c9986SThomas Zimmermann 	hsyncend = mode->hsync_end / 8;
209*5c3c9986SThomas Zimmermann 	hsyncstart = mode->hsync_start / 8;
210*5c3c9986SThomas Zimmermann 	hdispend = mode->hdisplay / 8;
211*5c3c9986SThomas Zimmermann 
212*5c3c9986SThomas Zimmermann 	vtotal = mode->vtotal;
213*5c3c9986SThomas Zimmermann 	vdispend = mode->vdisplay;
214*5c3c9986SThomas Zimmermann 
215*5c3c9986SThomas Zimmermann 	vdispend -= 1;
216*5c3c9986SThomas Zimmermann 	vtotal -= 2;
217*5c3c9986SThomas Zimmermann 
218*5c3c9986SThomas Zimmermann 	htotal -= 5;
219*5c3c9986SThomas Zimmermann 	hdispend -= 1;
220*5c3c9986SThomas Zimmermann 	hsyncstart += 1;
221*5c3c9986SThomas Zimmermann 	hsyncend += 1;
222*5c3c9986SThomas Zimmermann 
223*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, VGA_CRTC_V_SYNC_END, 0x20);
224*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, VGA_CRTC_H_TOTAL, htotal);
225*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, VGA_CRTC_H_DISP, hdispend);
226*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, VGA_CRTC_H_SYNC_START, hsyncstart);
227*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, VGA_CRTC_H_SYNC_END, hsyncend);
228*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, VGA_CRTC_V_TOTAL, vtotal & 0xff);
229*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, VGA_CRTC_V_DISP_END, vdispend & 0xff);
230*5c3c9986SThomas Zimmermann 
231*5c3c9986SThomas Zimmermann 	tmp = 0x40;
232*5c3c9986SThomas Zimmermann 	if ((vdispend + 1) & 512)
233*5c3c9986SThomas Zimmermann 		tmp |= 0x20;
234*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, VGA_CRTC_MAX_SCAN, tmp);
235*5c3c9986SThomas Zimmermann 
236*5c3c9986SThomas Zimmermann 	/*
237*5c3c9986SThomas Zimmermann 	 * Overflow bits for values that don't fit in the standard registers
238*5c3c9986SThomas Zimmermann 	 */
239*5c3c9986SThomas Zimmermann 	tmp = 0x10;
240*5c3c9986SThomas Zimmermann 	if (vtotal & 0x100)
241*5c3c9986SThomas Zimmermann 		tmp |= 0x01;
242*5c3c9986SThomas Zimmermann 	if (vdispend & 0x100)
243*5c3c9986SThomas Zimmermann 		tmp |= 0x02;
244*5c3c9986SThomas Zimmermann 	if ((vdispend + 1) & 0x100)
245*5c3c9986SThomas Zimmermann 		tmp |= 0x08;
246*5c3c9986SThomas Zimmermann 	if (vtotal & 0x200)
247*5c3c9986SThomas Zimmermann 		tmp |= 0x20;
248*5c3c9986SThomas Zimmermann 	if (vdispend & 0x200)
249*5c3c9986SThomas Zimmermann 		tmp |= 0x40;
250*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, VGA_CRTC_OVERFLOW, tmp);
251*5c3c9986SThomas Zimmermann 
252*5c3c9986SThomas Zimmermann 	tmp = 0;
253*5c3c9986SThomas Zimmermann 
254*5c3c9986SThomas Zimmermann 	/* More overflow bits */
255*5c3c9986SThomas Zimmermann 
256*5c3c9986SThomas Zimmermann 	if ((htotal + 5) & 0x40)
257*5c3c9986SThomas Zimmermann 		tmp |= 0x10;
258*5c3c9986SThomas Zimmermann 	if ((htotal + 5) & 0x80)
259*5c3c9986SThomas Zimmermann 		tmp |= 0x20;
260*5c3c9986SThomas Zimmermann 	if (vtotal & 0x100)
261*5c3c9986SThomas Zimmermann 		tmp |= 0x40;
262*5c3c9986SThomas Zimmermann 	if (vtotal & 0x200)
263*5c3c9986SThomas Zimmermann 		tmp |= 0x80;
264*5c3c9986SThomas Zimmermann 
265*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, CL_CRT1A, tmp);
266*5c3c9986SThomas Zimmermann 
267*5c3c9986SThomas Zimmermann 	/* Disable Hercules/CGA compatibility */
268*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, VGA_CRTC_MODE, 0x03);
269*5c3c9986SThomas Zimmermann }
270*5c3c9986SThomas Zimmermann 
271*5c3c9986SThomas Zimmermann static void cirrus_format_set(struct cirrus_device *cirrus,
272*5c3c9986SThomas Zimmermann 			      const struct drm_format_info *format)
273*5c3c9986SThomas Zimmermann {
274*5c3c9986SThomas Zimmermann 	u8 sr07, hdr;
275*5c3c9986SThomas Zimmermann 
276*5c3c9986SThomas Zimmermann 	sr07 = rreg_seq(cirrus, 0x07);
277*5c3c9986SThomas Zimmermann 	sr07 &= 0xe0;
278*5c3c9986SThomas Zimmermann 
279*5c3c9986SThomas Zimmermann 	switch (format->format) {
280*5c3c9986SThomas Zimmermann 	case DRM_FORMAT_C8:
281*5c3c9986SThomas Zimmermann 		sr07 |= 0x11;
282*5c3c9986SThomas Zimmermann 		hdr = 0x00;
283*5c3c9986SThomas Zimmermann 		break;
284*5c3c9986SThomas Zimmermann 	case DRM_FORMAT_RGB565:
285*5c3c9986SThomas Zimmermann 		sr07 |= 0x17;
286*5c3c9986SThomas Zimmermann 		hdr = 0xc1;
287*5c3c9986SThomas Zimmermann 		break;
288*5c3c9986SThomas Zimmermann 	case DRM_FORMAT_RGB888:
289*5c3c9986SThomas Zimmermann 		sr07 |= 0x15;
290*5c3c9986SThomas Zimmermann 		hdr = 0xc5;
291*5c3c9986SThomas Zimmermann 		break;
292*5c3c9986SThomas Zimmermann 	case DRM_FORMAT_XRGB8888:
293*5c3c9986SThomas Zimmermann 		sr07 |= 0x19;
294*5c3c9986SThomas Zimmermann 		hdr = 0xc5;
295*5c3c9986SThomas Zimmermann 		break;
296*5c3c9986SThomas Zimmermann 	default:
297*5c3c9986SThomas Zimmermann 		return;
298*5c3c9986SThomas Zimmermann 	}
299*5c3c9986SThomas Zimmermann 
300*5c3c9986SThomas Zimmermann 	wreg_seq(cirrus, 0x7, sr07);
301*5c3c9986SThomas Zimmermann 
302*5c3c9986SThomas Zimmermann 	/* Enable high-colour modes */
303*5c3c9986SThomas Zimmermann 	wreg_gfx(cirrus, VGA_GFX_MODE, 0x40);
304*5c3c9986SThomas Zimmermann 
305*5c3c9986SThomas Zimmermann 	/* And set graphics mode */
306*5c3c9986SThomas Zimmermann 	wreg_gfx(cirrus, VGA_GFX_MISC, 0x01);
307*5c3c9986SThomas Zimmermann 
308*5c3c9986SThomas Zimmermann 	wreg_hdr(cirrus, hdr);
309*5c3c9986SThomas Zimmermann }
310*5c3c9986SThomas Zimmermann 
311*5c3c9986SThomas Zimmermann static void cirrus_pitch_set(struct cirrus_device *cirrus, unsigned int pitch)
312*5c3c9986SThomas Zimmermann {
313*5c3c9986SThomas Zimmermann 	u8 cr13, cr1b;
314*5c3c9986SThomas Zimmermann 
315*5c3c9986SThomas Zimmermann 	/* Program the pitch */
316*5c3c9986SThomas Zimmermann 	cr13 = pitch / 8;
317*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, VGA_CRTC_OFFSET, cr13);
318*5c3c9986SThomas Zimmermann 
319*5c3c9986SThomas Zimmermann 	/* Enable extended blanking and pitch bits, and enable full memory */
320*5c3c9986SThomas Zimmermann 	cr1b = 0x22;
321*5c3c9986SThomas Zimmermann 	cr1b |= (pitch >> 7) & 0x10;
322*5c3c9986SThomas Zimmermann 	cr1b |= (pitch >> 6) & 0x40;
323*5c3c9986SThomas Zimmermann 	wreg_crt(cirrus, 0x1b, cr1b);
324*5c3c9986SThomas Zimmermann 
325*5c3c9986SThomas Zimmermann 	cirrus_set_start_address(cirrus, 0);
326*5c3c9986SThomas Zimmermann }
327*5c3c9986SThomas Zimmermann 
328*5c3c9986SThomas Zimmermann /* ------------------------------------------------------------------ */
329*5c3c9986SThomas Zimmermann /* cirrus display pipe						      */
330*5c3c9986SThomas Zimmermann 
331*5c3c9986SThomas Zimmermann static const uint32_t cirrus_primary_plane_formats[] = {
332*5c3c9986SThomas Zimmermann 	DRM_FORMAT_RGB565,
333*5c3c9986SThomas Zimmermann 	DRM_FORMAT_RGB888,
334*5c3c9986SThomas Zimmermann 	DRM_FORMAT_XRGB8888,
335*5c3c9986SThomas Zimmermann };
336*5c3c9986SThomas Zimmermann 
337*5c3c9986SThomas Zimmermann static const uint64_t cirrus_primary_plane_format_modifiers[] = {
338*5c3c9986SThomas Zimmermann 	DRM_FORMAT_MOD_LINEAR,
339*5c3c9986SThomas Zimmermann 	DRM_FORMAT_MOD_INVALID
340*5c3c9986SThomas Zimmermann };
341*5c3c9986SThomas Zimmermann 
342*5c3c9986SThomas Zimmermann static int cirrus_primary_plane_helper_atomic_check(struct drm_plane *plane,
343*5c3c9986SThomas Zimmermann 						    struct drm_atomic_state *state)
344*5c3c9986SThomas Zimmermann {
345*5c3c9986SThomas Zimmermann 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
346*5c3c9986SThomas Zimmermann 	struct cirrus_primary_plane_state *new_primary_plane_state =
347*5c3c9986SThomas Zimmermann 		to_cirrus_primary_plane_state(new_plane_state);
348*5c3c9986SThomas Zimmermann 	struct drm_framebuffer *fb = new_plane_state->fb;
349*5c3c9986SThomas Zimmermann 	struct drm_crtc *new_crtc = new_plane_state->crtc;
350*5c3c9986SThomas Zimmermann 	struct drm_crtc_state *new_crtc_state = NULL;
351*5c3c9986SThomas Zimmermann 	int ret;
352*5c3c9986SThomas Zimmermann 	unsigned int pitch;
353*5c3c9986SThomas Zimmermann 
354*5c3c9986SThomas Zimmermann 	if (new_crtc)
355*5c3c9986SThomas Zimmermann 		new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
356*5c3c9986SThomas Zimmermann 
357*5c3c9986SThomas Zimmermann 	ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
358*5c3c9986SThomas Zimmermann 						  DRM_PLANE_NO_SCALING,
359*5c3c9986SThomas Zimmermann 						  DRM_PLANE_NO_SCALING,
360*5c3c9986SThomas Zimmermann 						  false, false);
361*5c3c9986SThomas Zimmermann 	if (ret)
362*5c3c9986SThomas Zimmermann 		return ret;
363*5c3c9986SThomas Zimmermann 	else if (!new_plane_state->visible)
364*5c3c9986SThomas Zimmermann 		return 0;
365*5c3c9986SThomas Zimmermann 
366*5c3c9986SThomas Zimmermann 	pitch = cirrus_pitch(fb);
367*5c3c9986SThomas Zimmermann 
368*5c3c9986SThomas Zimmermann 	/* validate size constraints */
369*5c3c9986SThomas Zimmermann 	if (pitch > CIRRUS_MAX_PITCH)
370*5c3c9986SThomas Zimmermann 		return -EINVAL;
371*5c3c9986SThomas Zimmermann 	else if (pitch * fb->height > CIRRUS_VRAM_SIZE)
372*5c3c9986SThomas Zimmermann 		return -EINVAL;
373*5c3c9986SThomas Zimmermann 
374*5c3c9986SThomas Zimmermann 	new_primary_plane_state->format = cirrus_format(fb);
375*5c3c9986SThomas Zimmermann 	new_primary_plane_state->pitch = pitch;
376*5c3c9986SThomas Zimmermann 
377*5c3c9986SThomas Zimmermann 	return 0;
378*5c3c9986SThomas Zimmermann }
379*5c3c9986SThomas Zimmermann 
380*5c3c9986SThomas Zimmermann static void cirrus_primary_plane_helper_atomic_update(struct drm_plane *plane,
381*5c3c9986SThomas Zimmermann 						      struct drm_atomic_state *state)
382*5c3c9986SThomas Zimmermann {
383*5c3c9986SThomas Zimmermann 	struct cirrus_device *cirrus = to_cirrus(plane->dev);
384*5c3c9986SThomas Zimmermann 	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
385*5c3c9986SThomas Zimmermann 	struct cirrus_primary_plane_state *primary_plane_state =
386*5c3c9986SThomas Zimmermann 		to_cirrus_primary_plane_state(plane_state);
387*5c3c9986SThomas Zimmermann 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
388*5c3c9986SThomas Zimmermann 	struct drm_framebuffer *fb = plane_state->fb;
389*5c3c9986SThomas Zimmermann 	const struct drm_format_info *format = primary_plane_state->format;
390*5c3c9986SThomas Zimmermann 	unsigned int pitch = primary_plane_state->pitch;
391*5c3c9986SThomas Zimmermann 	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
392*5c3c9986SThomas Zimmermann 	struct cirrus_primary_plane_state *old_primary_plane_state =
393*5c3c9986SThomas Zimmermann 		to_cirrus_primary_plane_state(old_plane_state);
394*5c3c9986SThomas Zimmermann 	struct iosys_map vaddr = IOSYS_MAP_INIT_VADDR_IOMEM(cirrus->vram);
395*5c3c9986SThomas Zimmermann 	struct drm_atomic_helper_damage_iter iter;
396*5c3c9986SThomas Zimmermann 	struct drm_rect damage;
397*5c3c9986SThomas Zimmermann 	int idx;
398*5c3c9986SThomas Zimmermann 
399*5c3c9986SThomas Zimmermann 	if (!fb)
400*5c3c9986SThomas Zimmermann 		return;
401*5c3c9986SThomas Zimmermann 
402*5c3c9986SThomas Zimmermann 	if (!drm_dev_enter(&cirrus->dev, &idx))
403*5c3c9986SThomas Zimmermann 		return;
404*5c3c9986SThomas Zimmermann 
405*5c3c9986SThomas Zimmermann 	if (old_primary_plane_state->format != format)
406*5c3c9986SThomas Zimmermann 		cirrus_format_set(cirrus, format);
407*5c3c9986SThomas Zimmermann 	if (old_primary_plane_state->pitch != pitch)
408*5c3c9986SThomas Zimmermann 		cirrus_pitch_set(cirrus, pitch);
409*5c3c9986SThomas Zimmermann 
410*5c3c9986SThomas Zimmermann 	drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
411*5c3c9986SThomas Zimmermann 	drm_atomic_for_each_plane_damage(&iter, &damage) {
412*5c3c9986SThomas Zimmermann 		unsigned int offset = drm_fb_clip_offset(pitch, format, &damage);
413*5c3c9986SThomas Zimmermann 		struct iosys_map dst = IOSYS_MAP_INIT_OFFSET(&vaddr, offset);
414*5c3c9986SThomas Zimmermann 
415*5c3c9986SThomas Zimmermann 		drm_fb_blit(&dst, &pitch, format->format, shadow_plane_state->data, fb,
416*5c3c9986SThomas Zimmermann 			    &damage, &shadow_plane_state->fmtcnv_state);
417*5c3c9986SThomas Zimmermann 	}
418*5c3c9986SThomas Zimmermann 
419*5c3c9986SThomas Zimmermann 	drm_dev_exit(idx);
420*5c3c9986SThomas Zimmermann }
421*5c3c9986SThomas Zimmermann 
422*5c3c9986SThomas Zimmermann static const struct drm_plane_helper_funcs cirrus_primary_plane_helper_funcs = {
423*5c3c9986SThomas Zimmermann 	DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
424*5c3c9986SThomas Zimmermann 	.atomic_check = cirrus_primary_plane_helper_atomic_check,
425*5c3c9986SThomas Zimmermann 	.atomic_update = cirrus_primary_plane_helper_atomic_update,
426*5c3c9986SThomas Zimmermann };
427*5c3c9986SThomas Zimmermann 
428*5c3c9986SThomas Zimmermann static struct drm_plane_state *
429*5c3c9986SThomas Zimmermann cirrus_primary_plane_atomic_duplicate_state(struct drm_plane *plane)
430*5c3c9986SThomas Zimmermann {
431*5c3c9986SThomas Zimmermann 	struct drm_plane_state *plane_state = plane->state;
432*5c3c9986SThomas Zimmermann 	struct cirrus_primary_plane_state *primary_plane_state =
433*5c3c9986SThomas Zimmermann 		to_cirrus_primary_plane_state(plane_state);
434*5c3c9986SThomas Zimmermann 	struct cirrus_primary_plane_state *new_primary_plane_state;
435*5c3c9986SThomas Zimmermann 	struct drm_shadow_plane_state *new_shadow_plane_state;
436*5c3c9986SThomas Zimmermann 
437*5c3c9986SThomas Zimmermann 	if (!plane_state)
438*5c3c9986SThomas Zimmermann 		return NULL;
439*5c3c9986SThomas Zimmermann 
440*5c3c9986SThomas Zimmermann 	new_primary_plane_state = kzalloc(sizeof(*new_primary_plane_state), GFP_KERNEL);
441*5c3c9986SThomas Zimmermann 	if (!new_primary_plane_state)
442*5c3c9986SThomas Zimmermann 		return NULL;
443*5c3c9986SThomas Zimmermann 	new_shadow_plane_state = &new_primary_plane_state->base;
444*5c3c9986SThomas Zimmermann 
445*5c3c9986SThomas Zimmermann 	__drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state);
446*5c3c9986SThomas Zimmermann 	new_primary_plane_state->format = primary_plane_state->format;
447*5c3c9986SThomas Zimmermann 	new_primary_plane_state->pitch = primary_plane_state->pitch;
448*5c3c9986SThomas Zimmermann 
449*5c3c9986SThomas Zimmermann 	return &new_shadow_plane_state->base;
450*5c3c9986SThomas Zimmermann }
451*5c3c9986SThomas Zimmermann 
452*5c3c9986SThomas Zimmermann static void cirrus_primary_plane_atomic_destroy_state(struct drm_plane *plane,
453*5c3c9986SThomas Zimmermann 						      struct drm_plane_state *plane_state)
454*5c3c9986SThomas Zimmermann {
455*5c3c9986SThomas Zimmermann 	struct cirrus_primary_plane_state *primary_plane_state =
456*5c3c9986SThomas Zimmermann 		to_cirrus_primary_plane_state(plane_state);
457*5c3c9986SThomas Zimmermann 
458*5c3c9986SThomas Zimmermann 	__drm_gem_destroy_shadow_plane_state(&primary_plane_state->base);
459*5c3c9986SThomas Zimmermann 	kfree(primary_plane_state);
460*5c3c9986SThomas Zimmermann }
461*5c3c9986SThomas Zimmermann 
462*5c3c9986SThomas Zimmermann static void cirrus_reset_primary_plane(struct drm_plane *plane)
463*5c3c9986SThomas Zimmermann {
464*5c3c9986SThomas Zimmermann 	struct cirrus_primary_plane_state *primary_plane_state;
465*5c3c9986SThomas Zimmermann 
466*5c3c9986SThomas Zimmermann 	if (plane->state) {
467*5c3c9986SThomas Zimmermann 		cirrus_primary_plane_atomic_destroy_state(plane, plane->state);
468*5c3c9986SThomas Zimmermann 		plane->state = NULL; /* must be set to NULL here */
469*5c3c9986SThomas Zimmermann 	}
470*5c3c9986SThomas Zimmermann 
471*5c3c9986SThomas Zimmermann 	primary_plane_state = kzalloc(sizeof(*primary_plane_state), GFP_KERNEL);
472*5c3c9986SThomas Zimmermann 	if (!primary_plane_state)
473*5c3c9986SThomas Zimmermann 		return;
474*5c3c9986SThomas Zimmermann 	__drm_gem_reset_shadow_plane(plane, &primary_plane_state->base);
475*5c3c9986SThomas Zimmermann }
476*5c3c9986SThomas Zimmermann 
477*5c3c9986SThomas Zimmermann static const struct drm_plane_funcs cirrus_primary_plane_funcs = {
478*5c3c9986SThomas Zimmermann 	.update_plane = drm_atomic_helper_update_plane,
479*5c3c9986SThomas Zimmermann 	.disable_plane = drm_atomic_helper_disable_plane,
480*5c3c9986SThomas Zimmermann 	.destroy = drm_plane_cleanup,
481*5c3c9986SThomas Zimmermann 	.reset = cirrus_reset_primary_plane,
482*5c3c9986SThomas Zimmermann 	.atomic_duplicate_state = cirrus_primary_plane_atomic_duplicate_state,
483*5c3c9986SThomas Zimmermann 	.atomic_destroy_state = cirrus_primary_plane_atomic_destroy_state,
484*5c3c9986SThomas Zimmermann };
485*5c3c9986SThomas Zimmermann 
486*5c3c9986SThomas Zimmermann static int cirrus_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
487*5c3c9986SThomas Zimmermann {
488*5c3c9986SThomas Zimmermann 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
489*5c3c9986SThomas Zimmermann 	int ret;
490*5c3c9986SThomas Zimmermann 
491*5c3c9986SThomas Zimmermann 	if (!crtc_state->enable)
492*5c3c9986SThomas Zimmermann 		return 0;
493*5c3c9986SThomas Zimmermann 
494*5c3c9986SThomas Zimmermann 	ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
495*5c3c9986SThomas Zimmermann 	if (ret)
496*5c3c9986SThomas Zimmermann 		return ret;
497*5c3c9986SThomas Zimmermann 
498*5c3c9986SThomas Zimmermann 	return 0;
499*5c3c9986SThomas Zimmermann }
500*5c3c9986SThomas Zimmermann 
501*5c3c9986SThomas Zimmermann static void cirrus_crtc_helper_atomic_enable(struct drm_crtc *crtc,
502*5c3c9986SThomas Zimmermann 					     struct drm_atomic_state *state)
503*5c3c9986SThomas Zimmermann {
504*5c3c9986SThomas Zimmermann 	struct cirrus_device *cirrus = to_cirrus(crtc->dev);
505*5c3c9986SThomas Zimmermann 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
506*5c3c9986SThomas Zimmermann 	int idx;
507*5c3c9986SThomas Zimmermann 
508*5c3c9986SThomas Zimmermann 	if (!drm_dev_enter(&cirrus->dev, &idx))
509*5c3c9986SThomas Zimmermann 		return;
510*5c3c9986SThomas Zimmermann 
511*5c3c9986SThomas Zimmermann 	cirrus_mode_set(cirrus, &crtc_state->mode);
512*5c3c9986SThomas Zimmermann 
513*5c3c9986SThomas Zimmermann 	/* Unblank (needed on S3 resume, vgabios doesn't do it then) */
514*5c3c9986SThomas Zimmermann 	outb(VGA_AR_ENABLE_DISPLAY, VGA_ATT_W);
515*5c3c9986SThomas Zimmermann 
516*5c3c9986SThomas Zimmermann 	drm_dev_exit(idx);
517*5c3c9986SThomas Zimmermann }
518*5c3c9986SThomas Zimmermann 
519*5c3c9986SThomas Zimmermann static const struct drm_crtc_helper_funcs cirrus_crtc_helper_funcs = {
520*5c3c9986SThomas Zimmermann 	.atomic_check = cirrus_crtc_helper_atomic_check,
521*5c3c9986SThomas Zimmermann 	.atomic_enable = cirrus_crtc_helper_atomic_enable,
522*5c3c9986SThomas Zimmermann };
523*5c3c9986SThomas Zimmermann 
524*5c3c9986SThomas Zimmermann static const struct drm_crtc_funcs cirrus_crtc_funcs = {
525*5c3c9986SThomas Zimmermann 	.reset = drm_atomic_helper_crtc_reset,
526*5c3c9986SThomas Zimmermann 	.destroy = drm_crtc_cleanup,
527*5c3c9986SThomas Zimmermann 	.set_config = drm_atomic_helper_set_config,
528*5c3c9986SThomas Zimmermann 	.page_flip = drm_atomic_helper_page_flip,
529*5c3c9986SThomas Zimmermann 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
530*5c3c9986SThomas Zimmermann 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
531*5c3c9986SThomas Zimmermann };
532*5c3c9986SThomas Zimmermann 
533*5c3c9986SThomas Zimmermann static const struct drm_encoder_funcs cirrus_encoder_funcs = {
534*5c3c9986SThomas Zimmermann 	.destroy = drm_encoder_cleanup,
535*5c3c9986SThomas Zimmermann };
536*5c3c9986SThomas Zimmermann 
537*5c3c9986SThomas Zimmermann static int cirrus_connector_helper_get_modes(struct drm_connector *connector)
538*5c3c9986SThomas Zimmermann {
539*5c3c9986SThomas Zimmermann 	int count;
540*5c3c9986SThomas Zimmermann 
541*5c3c9986SThomas Zimmermann 	count = drm_add_modes_noedid(connector,
542*5c3c9986SThomas Zimmermann 				     connector->dev->mode_config.max_width,
543*5c3c9986SThomas Zimmermann 				     connector->dev->mode_config.max_height);
544*5c3c9986SThomas Zimmermann 	drm_set_preferred_mode(connector, 1024, 768);
545*5c3c9986SThomas Zimmermann 	return count;
546*5c3c9986SThomas Zimmermann }
547*5c3c9986SThomas Zimmermann 
548*5c3c9986SThomas Zimmermann static const struct drm_connector_helper_funcs cirrus_connector_helper_funcs = {
549*5c3c9986SThomas Zimmermann 	.get_modes = cirrus_connector_helper_get_modes,
550*5c3c9986SThomas Zimmermann };
551*5c3c9986SThomas Zimmermann 
552*5c3c9986SThomas Zimmermann static const struct drm_connector_funcs cirrus_connector_funcs = {
553*5c3c9986SThomas Zimmermann 	.fill_modes = drm_helper_probe_single_connector_modes,
554*5c3c9986SThomas Zimmermann 	.destroy = drm_connector_cleanup,
555*5c3c9986SThomas Zimmermann 	.reset = drm_atomic_helper_connector_reset,
556*5c3c9986SThomas Zimmermann 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
557*5c3c9986SThomas Zimmermann 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
558*5c3c9986SThomas Zimmermann };
559*5c3c9986SThomas Zimmermann 
560*5c3c9986SThomas Zimmermann static int cirrus_pipe_init(struct cirrus_device *cirrus)
561*5c3c9986SThomas Zimmermann {
562*5c3c9986SThomas Zimmermann 	struct drm_device *dev = &cirrus->dev;
563*5c3c9986SThomas Zimmermann 	struct drm_plane *primary_plane;
564*5c3c9986SThomas Zimmermann 	struct drm_crtc *crtc;
565*5c3c9986SThomas Zimmermann 	struct drm_encoder *encoder;
566*5c3c9986SThomas Zimmermann 	struct drm_connector *connector;
567*5c3c9986SThomas Zimmermann 	int ret;
568*5c3c9986SThomas Zimmermann 
569*5c3c9986SThomas Zimmermann 	primary_plane = &cirrus->primary_plane;
570*5c3c9986SThomas Zimmermann 	ret = drm_universal_plane_init(dev, primary_plane, 0,
571*5c3c9986SThomas Zimmermann 				       &cirrus_primary_plane_funcs,
572*5c3c9986SThomas Zimmermann 				       cirrus_primary_plane_formats,
573*5c3c9986SThomas Zimmermann 				       ARRAY_SIZE(cirrus_primary_plane_formats),
574*5c3c9986SThomas Zimmermann 				       cirrus_primary_plane_format_modifiers,
575*5c3c9986SThomas Zimmermann 				       DRM_PLANE_TYPE_PRIMARY, NULL);
576*5c3c9986SThomas Zimmermann 	if (ret)
577*5c3c9986SThomas Zimmermann 		return ret;
578*5c3c9986SThomas Zimmermann 	drm_plane_helper_add(primary_plane, &cirrus_primary_plane_helper_funcs);
579*5c3c9986SThomas Zimmermann 	drm_plane_enable_fb_damage_clips(primary_plane);
580*5c3c9986SThomas Zimmermann 
581*5c3c9986SThomas Zimmermann 	crtc = &cirrus->crtc;
582*5c3c9986SThomas Zimmermann 	ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
583*5c3c9986SThomas Zimmermann 					&cirrus_crtc_funcs, NULL);
584*5c3c9986SThomas Zimmermann 	if (ret)
585*5c3c9986SThomas Zimmermann 		return ret;
586*5c3c9986SThomas Zimmermann 	drm_crtc_helper_add(crtc, &cirrus_crtc_helper_funcs);
587*5c3c9986SThomas Zimmermann 
588*5c3c9986SThomas Zimmermann 	encoder = &cirrus->encoder;
589*5c3c9986SThomas Zimmermann 	ret = drm_encoder_init(dev, encoder, &cirrus_encoder_funcs,
590*5c3c9986SThomas Zimmermann 			       DRM_MODE_ENCODER_VIRTUAL, NULL);
591*5c3c9986SThomas Zimmermann 	if (ret)
592*5c3c9986SThomas Zimmermann 		return ret;
593*5c3c9986SThomas Zimmermann 	encoder->possible_crtcs = drm_crtc_mask(crtc);
594*5c3c9986SThomas Zimmermann 
595*5c3c9986SThomas Zimmermann 	connector = &cirrus->connector;
596*5c3c9986SThomas Zimmermann 	ret = drm_connector_init(dev, connector, &cirrus_connector_funcs,
597*5c3c9986SThomas Zimmermann 				 DRM_MODE_CONNECTOR_VIRTUAL);
598*5c3c9986SThomas Zimmermann 	if (ret)
599*5c3c9986SThomas Zimmermann 		return ret;
600*5c3c9986SThomas Zimmermann 	drm_connector_helper_add(connector, &cirrus_connector_helper_funcs);
601*5c3c9986SThomas Zimmermann 
602*5c3c9986SThomas Zimmermann 	ret = drm_connector_attach_encoder(connector, encoder);
603*5c3c9986SThomas Zimmermann 	if (ret)
604*5c3c9986SThomas Zimmermann 		return ret;
605*5c3c9986SThomas Zimmermann 
606*5c3c9986SThomas Zimmermann 	return 0;
607*5c3c9986SThomas Zimmermann }
608*5c3c9986SThomas Zimmermann 
609*5c3c9986SThomas Zimmermann /* ------------------------------------------------------------------ */
610*5c3c9986SThomas Zimmermann /* cirrus framebuffers & mode config				      */
611*5c3c9986SThomas Zimmermann 
612*5c3c9986SThomas Zimmermann static enum drm_mode_status cirrus_mode_config_mode_valid(struct drm_device *dev,
613*5c3c9986SThomas Zimmermann 							  const struct drm_display_mode *mode)
614*5c3c9986SThomas Zimmermann {
615*5c3c9986SThomas Zimmermann 	const struct drm_format_info *format = drm_format_info(DRM_FORMAT_XRGB8888);
616*5c3c9986SThomas Zimmermann 	uint64_t pitch = drm_format_info_min_pitch(format, 0, mode->hdisplay);
617*5c3c9986SThomas Zimmermann 
618*5c3c9986SThomas Zimmermann 	if (pitch * mode->vdisplay > CIRRUS_VRAM_SIZE)
619*5c3c9986SThomas Zimmermann 		return MODE_MEM;
620*5c3c9986SThomas Zimmermann 
621*5c3c9986SThomas Zimmermann 	return MODE_OK;
622*5c3c9986SThomas Zimmermann }
623*5c3c9986SThomas Zimmermann 
624*5c3c9986SThomas Zimmermann static const struct drm_mode_config_funcs cirrus_mode_config_funcs = {
625*5c3c9986SThomas Zimmermann 	.fb_create = drm_gem_fb_create_with_dirty,
626*5c3c9986SThomas Zimmermann 	.mode_valid = cirrus_mode_config_mode_valid,
627*5c3c9986SThomas Zimmermann 	.atomic_check = drm_atomic_helper_check,
628*5c3c9986SThomas Zimmermann 	.atomic_commit = drm_atomic_helper_commit,
629*5c3c9986SThomas Zimmermann };
630*5c3c9986SThomas Zimmermann 
631*5c3c9986SThomas Zimmermann static int cirrus_mode_config_init(struct cirrus_device *cirrus)
632*5c3c9986SThomas Zimmermann {
633*5c3c9986SThomas Zimmermann 	struct drm_device *dev = &cirrus->dev;
634*5c3c9986SThomas Zimmermann 	int ret;
635*5c3c9986SThomas Zimmermann 
636*5c3c9986SThomas Zimmermann 	ret = drmm_mode_config_init(dev);
637*5c3c9986SThomas Zimmermann 	if (ret)
638*5c3c9986SThomas Zimmermann 		return ret;
639*5c3c9986SThomas Zimmermann 
640*5c3c9986SThomas Zimmermann 	dev->mode_config.min_width = 0;
641*5c3c9986SThomas Zimmermann 	dev->mode_config.min_height = 0;
642*5c3c9986SThomas Zimmermann 	dev->mode_config.max_width = CIRRUS_MAX_PITCH / 2;
643*5c3c9986SThomas Zimmermann 	dev->mode_config.max_height = 1024;
644*5c3c9986SThomas Zimmermann 	dev->mode_config.preferred_depth = 16;
645*5c3c9986SThomas Zimmermann 	dev->mode_config.prefer_shadow = 0;
646*5c3c9986SThomas Zimmermann 	dev->mode_config.funcs = &cirrus_mode_config_funcs;
647*5c3c9986SThomas Zimmermann 
648*5c3c9986SThomas Zimmermann 	return 0;
649*5c3c9986SThomas Zimmermann }
650*5c3c9986SThomas Zimmermann 
651*5c3c9986SThomas Zimmermann /* ------------------------------------------------------------------ */
652*5c3c9986SThomas Zimmermann 
653*5c3c9986SThomas Zimmermann DEFINE_DRM_GEM_FOPS(cirrus_fops);
654*5c3c9986SThomas Zimmermann 
655*5c3c9986SThomas Zimmermann static const struct drm_driver cirrus_driver = {
656*5c3c9986SThomas Zimmermann 	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
657*5c3c9986SThomas Zimmermann 
658*5c3c9986SThomas Zimmermann 	.name		 = DRIVER_NAME,
659*5c3c9986SThomas Zimmermann 	.desc		 = DRIVER_DESC,
660*5c3c9986SThomas Zimmermann 	.date		 = DRIVER_DATE,
661*5c3c9986SThomas Zimmermann 	.major		 = DRIVER_MAJOR,
662*5c3c9986SThomas Zimmermann 	.minor		 = DRIVER_MINOR,
663*5c3c9986SThomas Zimmermann 
664*5c3c9986SThomas Zimmermann 	.fops		 = &cirrus_fops,
665*5c3c9986SThomas Zimmermann 	DRM_GEM_SHMEM_DRIVER_OPS,
666*5c3c9986SThomas Zimmermann 	DRM_FBDEV_SHMEM_DRIVER_OPS,
667*5c3c9986SThomas Zimmermann };
668*5c3c9986SThomas Zimmermann 
669*5c3c9986SThomas Zimmermann static int cirrus_pci_probe(struct pci_dev *pdev,
670*5c3c9986SThomas Zimmermann 			    const struct pci_device_id *ent)
671*5c3c9986SThomas Zimmermann {
672*5c3c9986SThomas Zimmermann 	struct drm_device *dev;
673*5c3c9986SThomas Zimmermann 	struct cirrus_device *cirrus;
674*5c3c9986SThomas Zimmermann 	int ret;
675*5c3c9986SThomas Zimmermann 
676*5c3c9986SThomas Zimmermann 	ret = aperture_remove_conflicting_pci_devices(pdev, cirrus_driver.name);
677*5c3c9986SThomas Zimmermann 	if (ret)
678*5c3c9986SThomas Zimmermann 		return ret;
679*5c3c9986SThomas Zimmermann 
680*5c3c9986SThomas Zimmermann 	ret = pcim_enable_device(pdev);
681*5c3c9986SThomas Zimmermann 	if (ret)
682*5c3c9986SThomas Zimmermann 		return ret;
683*5c3c9986SThomas Zimmermann 
684*5c3c9986SThomas Zimmermann 	ret = pci_request_regions(pdev, DRIVER_NAME);
685*5c3c9986SThomas Zimmermann 	if (ret)
686*5c3c9986SThomas Zimmermann 		return ret;
687*5c3c9986SThomas Zimmermann 
688*5c3c9986SThomas Zimmermann 	ret = -ENOMEM;
689*5c3c9986SThomas Zimmermann 	cirrus = devm_drm_dev_alloc(&pdev->dev, &cirrus_driver,
690*5c3c9986SThomas Zimmermann 				    struct cirrus_device, dev);
691*5c3c9986SThomas Zimmermann 	if (IS_ERR(cirrus))
692*5c3c9986SThomas Zimmermann 		return PTR_ERR(cirrus);
693*5c3c9986SThomas Zimmermann 
694*5c3c9986SThomas Zimmermann 	dev = &cirrus->dev;
695*5c3c9986SThomas Zimmermann 
696*5c3c9986SThomas Zimmermann 	cirrus->vram = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0),
697*5c3c9986SThomas Zimmermann 				    pci_resource_len(pdev, 0));
698*5c3c9986SThomas Zimmermann 	if (cirrus->vram == NULL)
699*5c3c9986SThomas Zimmermann 		return -ENOMEM;
700*5c3c9986SThomas Zimmermann 
701*5c3c9986SThomas Zimmermann 	cirrus->mmio = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 1),
702*5c3c9986SThomas Zimmermann 				    pci_resource_len(pdev, 1));
703*5c3c9986SThomas Zimmermann 	if (cirrus->mmio == NULL)
704*5c3c9986SThomas Zimmermann 		return -ENOMEM;
705*5c3c9986SThomas Zimmermann 
706*5c3c9986SThomas Zimmermann 	ret = cirrus_mode_config_init(cirrus);
707*5c3c9986SThomas Zimmermann 	if (ret)
708*5c3c9986SThomas Zimmermann 		return ret;
709*5c3c9986SThomas Zimmermann 
710*5c3c9986SThomas Zimmermann 	ret = cirrus_pipe_init(cirrus);
711*5c3c9986SThomas Zimmermann 	if (ret < 0)
712*5c3c9986SThomas Zimmermann 		return ret;
713*5c3c9986SThomas Zimmermann 
714*5c3c9986SThomas Zimmermann 	drm_mode_config_reset(dev);
715*5c3c9986SThomas Zimmermann 
716*5c3c9986SThomas Zimmermann 	pci_set_drvdata(pdev, dev);
717*5c3c9986SThomas Zimmermann 	ret = drm_dev_register(dev, 0);
718*5c3c9986SThomas Zimmermann 	if (ret)
719*5c3c9986SThomas Zimmermann 		return ret;
720*5c3c9986SThomas Zimmermann 
721*5c3c9986SThomas Zimmermann 	drm_client_setup(dev, NULL);
722*5c3c9986SThomas Zimmermann 	return 0;
723*5c3c9986SThomas Zimmermann }
724*5c3c9986SThomas Zimmermann 
725*5c3c9986SThomas Zimmermann static void cirrus_pci_remove(struct pci_dev *pdev)
726*5c3c9986SThomas Zimmermann {
727*5c3c9986SThomas Zimmermann 	struct drm_device *dev = pci_get_drvdata(pdev);
728*5c3c9986SThomas Zimmermann 
729*5c3c9986SThomas Zimmermann 	drm_dev_unplug(dev);
730*5c3c9986SThomas Zimmermann 	drm_atomic_helper_shutdown(dev);
731*5c3c9986SThomas Zimmermann }
732*5c3c9986SThomas Zimmermann 
733*5c3c9986SThomas Zimmermann static void cirrus_pci_shutdown(struct pci_dev *pdev)
734*5c3c9986SThomas Zimmermann {
735*5c3c9986SThomas Zimmermann 	drm_atomic_helper_shutdown(pci_get_drvdata(pdev));
736*5c3c9986SThomas Zimmermann }
737*5c3c9986SThomas Zimmermann 
738*5c3c9986SThomas Zimmermann static const struct pci_device_id pciidlist[] = {
739*5c3c9986SThomas Zimmermann 	{
740*5c3c9986SThomas Zimmermann 		.vendor    = PCI_VENDOR_ID_CIRRUS,
741*5c3c9986SThomas Zimmermann 		.device    = PCI_DEVICE_ID_CIRRUS_5446,
742*5c3c9986SThomas Zimmermann 		/* only bind to the cirrus chip in qemu */
743*5c3c9986SThomas Zimmermann 		.subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET,
744*5c3c9986SThomas Zimmermann 		.subdevice = PCI_SUBDEVICE_ID_QEMU,
745*5c3c9986SThomas Zimmermann 	}, {
746*5c3c9986SThomas Zimmermann 		.vendor    = PCI_VENDOR_ID_CIRRUS,
747*5c3c9986SThomas Zimmermann 		.device    = PCI_DEVICE_ID_CIRRUS_5446,
748*5c3c9986SThomas Zimmermann 		.subvendor = PCI_VENDOR_ID_XEN,
749*5c3c9986SThomas Zimmermann 		.subdevice = 0x0001,
750*5c3c9986SThomas Zimmermann 	},
751*5c3c9986SThomas Zimmermann 	{ /* end if list */ }
752*5c3c9986SThomas Zimmermann };
753*5c3c9986SThomas Zimmermann 
754*5c3c9986SThomas Zimmermann static struct pci_driver cirrus_pci_driver = {
755*5c3c9986SThomas Zimmermann 	.name = DRIVER_NAME,
756*5c3c9986SThomas Zimmermann 	.id_table = pciidlist,
757*5c3c9986SThomas Zimmermann 	.probe = cirrus_pci_probe,
758*5c3c9986SThomas Zimmermann 	.remove = cirrus_pci_remove,
759*5c3c9986SThomas Zimmermann 	.shutdown = cirrus_pci_shutdown,
760*5c3c9986SThomas Zimmermann };
761*5c3c9986SThomas Zimmermann 
762*5c3c9986SThomas Zimmermann drm_module_pci_driver(cirrus_pci_driver)
763*5c3c9986SThomas Zimmermann 
764*5c3c9986SThomas Zimmermann MODULE_DEVICE_TABLE(pci, pciidlist);
765*5c3c9986SThomas Zimmermann MODULE_DESCRIPTION("Cirrus driver for QEMU emulated device");
766*5c3c9986SThomas Zimmermann MODULE_LICENSE("GPL");
767