162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2012-2019 Red Hat
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General
662306a36Sopenharmony_ci * Public License version 2. See the file COPYING in the main
762306a36Sopenharmony_ci * directory of this archive for more details.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Authors: Matthew Garrett
1062306a36Sopenharmony_ci *	    Dave Airlie
1162306a36Sopenharmony_ci *	    Gerd Hoffmann
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Portions of this code derived from cirrusfb.c:
1462306a36Sopenharmony_ci * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/iosys-map.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/pci.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <video/cirrus.h>
2462306a36Sopenharmony_ci#include <video/vga.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <drm/drm_aperture.h>
2762306a36Sopenharmony_ci#include <drm/drm_atomic.h>
2862306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
2962306a36Sopenharmony_ci#include <drm/drm_atomic_state_helper.h>
3062306a36Sopenharmony_ci#include <drm/drm_connector.h>
3162306a36Sopenharmony_ci#include <drm/drm_damage_helper.h>
3262306a36Sopenharmony_ci#include <drm/drm_drv.h>
3362306a36Sopenharmony_ci#include <drm/drm_edid.h>
3462306a36Sopenharmony_ci#include <drm/drm_fbdev_generic.h>
3562306a36Sopenharmony_ci#include <drm/drm_file.h>
3662306a36Sopenharmony_ci#include <drm/drm_format_helper.h>
3762306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
3862306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
3962306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h>
4062306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
4162306a36Sopenharmony_ci#include <drm/drm_gem_shmem_helper.h>
4262306a36Sopenharmony_ci#include <drm/drm_ioctl.h>
4362306a36Sopenharmony_ci#include <drm/drm_managed.h>
4462306a36Sopenharmony_ci#include <drm/drm_modeset_helper_vtables.h>
4562306a36Sopenharmony_ci#include <drm/drm_module.h>
4662306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define DRIVER_NAME "cirrus"
4962306a36Sopenharmony_ci#define DRIVER_DESC "qemu cirrus vga"
5062306a36Sopenharmony_ci#define DRIVER_DATE "2019"
5162306a36Sopenharmony_ci#define DRIVER_MAJOR 2
5262306a36Sopenharmony_ci#define DRIVER_MINOR 0
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define CIRRUS_MAX_PITCH (0x1FF << 3)      /* (4096 - 1) & ~111b bytes */
5562306a36Sopenharmony_ci#define CIRRUS_VRAM_SIZE (4 * 1024 * 1024) /* 4 MB */
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistruct cirrus_device {
5862306a36Sopenharmony_ci	struct drm_device	       dev;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* modesetting pipeline */
6162306a36Sopenharmony_ci	struct drm_plane	       primary_plane;
6262306a36Sopenharmony_ci	struct drm_crtc		       crtc;
6362306a36Sopenharmony_ci	struct drm_encoder	       encoder;
6462306a36Sopenharmony_ci	struct drm_connector	       connector;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	/* HW resources */
6762306a36Sopenharmony_ci	void __iomem		       *vram;
6862306a36Sopenharmony_ci	void __iomem		       *mmio;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define to_cirrus(_dev) container_of(_dev, struct cirrus_device, dev)
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct cirrus_primary_plane_state {
7462306a36Sopenharmony_ci	struct drm_shadow_plane_state base;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* HW scanout buffer */
7762306a36Sopenharmony_ci	const struct drm_format_info   *format;
7862306a36Sopenharmony_ci	unsigned int		       pitch;
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic inline struct cirrus_primary_plane_state *
8262306a36Sopenharmony_cito_cirrus_primary_plane_state(struct drm_plane_state *plane_state)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	return container_of(plane_state, struct cirrus_primary_plane_state, base.base);
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
8862306a36Sopenharmony_ci/*
8962306a36Sopenharmony_ci * The meat of this driver. The core passes us a mode and we have to program
9062306a36Sopenharmony_ci * it. The modesetting here is the bare minimum required to satisfy the qemu
9162306a36Sopenharmony_ci * emulation of this hardware, and running this against a real device is
9262306a36Sopenharmony_ci * likely to result in an inadequately programmed mode. We've already had
9362306a36Sopenharmony_ci * the opportunity to modify the mode, so whatever we receive here should
9462306a36Sopenharmony_ci * be something that can be correctly programmed and displayed
9562306a36Sopenharmony_ci */
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci#define SEQ_INDEX 4
9862306a36Sopenharmony_ci#define SEQ_DATA 5
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic u8 rreg_seq(struct cirrus_device *cirrus, u8 reg)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	iowrite8(reg, cirrus->mmio + SEQ_INDEX);
10362306a36Sopenharmony_ci	return ioread8(cirrus->mmio + SEQ_DATA);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void wreg_seq(struct cirrus_device *cirrus, u8 reg, u8 val)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	iowrite8(reg, cirrus->mmio + SEQ_INDEX);
10962306a36Sopenharmony_ci	iowrite8(val, cirrus->mmio + SEQ_DATA);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci#define CRT_INDEX 0x14
11362306a36Sopenharmony_ci#define CRT_DATA 0x15
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic u8 rreg_crt(struct cirrus_device *cirrus, u8 reg)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	iowrite8(reg, cirrus->mmio + CRT_INDEX);
11862306a36Sopenharmony_ci	return ioread8(cirrus->mmio + CRT_DATA);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void wreg_crt(struct cirrus_device *cirrus, u8 reg, u8 val)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	iowrite8(reg, cirrus->mmio + CRT_INDEX);
12462306a36Sopenharmony_ci	iowrite8(val, cirrus->mmio + CRT_DATA);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci#define GFX_INDEX 0xe
12862306a36Sopenharmony_ci#define GFX_DATA 0xf
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void wreg_gfx(struct cirrus_device *cirrus, u8 reg, u8 val)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	iowrite8(reg, cirrus->mmio + GFX_INDEX);
13362306a36Sopenharmony_ci	iowrite8(val, cirrus->mmio + GFX_DATA);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci#define VGA_DAC_MASK  0x06
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void wreg_hdr(struct cirrus_device *cirrus, u8 val)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	ioread8(cirrus->mmio + VGA_DAC_MASK);
14162306a36Sopenharmony_ci	ioread8(cirrus->mmio + VGA_DAC_MASK);
14262306a36Sopenharmony_ci	ioread8(cirrus->mmio + VGA_DAC_MASK);
14362306a36Sopenharmony_ci	ioread8(cirrus->mmio + VGA_DAC_MASK);
14462306a36Sopenharmony_ci	iowrite8(val, cirrus->mmio + VGA_DAC_MASK);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic const struct drm_format_info *cirrus_convert_to(struct drm_framebuffer *fb)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	if (fb->format->format == DRM_FORMAT_XRGB8888 && fb->pitches[0] > CIRRUS_MAX_PITCH) {
15062306a36Sopenharmony_ci		if (fb->width * 3 <= CIRRUS_MAX_PITCH)
15162306a36Sopenharmony_ci			/* convert from XR24 to RG24 */
15262306a36Sopenharmony_ci			return drm_format_info(DRM_FORMAT_RGB888);
15362306a36Sopenharmony_ci		else
15462306a36Sopenharmony_ci			/* convert from XR24 to RG16 */
15562306a36Sopenharmony_ci			return drm_format_info(DRM_FORMAT_RGB565);
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci	return NULL;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic const struct drm_format_info *cirrus_format(struct drm_framebuffer *fb)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	const struct drm_format_info *format = cirrus_convert_to(fb);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (format)
16562306a36Sopenharmony_ci		return format;
16662306a36Sopenharmony_ci	return fb->format;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int cirrus_pitch(struct drm_framebuffer *fb)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	const struct drm_format_info *format = cirrus_convert_to(fb);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (format)
17462306a36Sopenharmony_ci		return drm_format_info_min_pitch(format, 0, fb->width);
17562306a36Sopenharmony_ci	return fb->pitches[0];
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic void cirrus_set_start_address(struct cirrus_device *cirrus, u32 offset)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	u32 addr;
18162306a36Sopenharmony_ci	u8 tmp;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	addr = offset >> 2;
18462306a36Sopenharmony_ci	wreg_crt(cirrus, 0x0c, (u8)((addr >> 8) & 0xff));
18562306a36Sopenharmony_ci	wreg_crt(cirrus, 0x0d, (u8)(addr & 0xff));
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	tmp = rreg_crt(cirrus, 0x1b);
18862306a36Sopenharmony_ci	tmp &= 0xf2;
18962306a36Sopenharmony_ci	tmp |= (addr >> 16) & 0x01;
19062306a36Sopenharmony_ci	tmp |= (addr >> 15) & 0x0c;
19162306a36Sopenharmony_ci	wreg_crt(cirrus, 0x1b, tmp);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	tmp = rreg_crt(cirrus, 0x1d);
19462306a36Sopenharmony_ci	tmp &= 0x7f;
19562306a36Sopenharmony_ci	tmp |= (addr >> 12) & 0x80;
19662306a36Sopenharmony_ci	wreg_crt(cirrus, 0x1d, tmp);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void cirrus_mode_set(struct cirrus_device *cirrus,
20062306a36Sopenharmony_ci			    struct drm_display_mode *mode)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	int hsyncstart, hsyncend, htotal, hdispend;
20362306a36Sopenharmony_ci	int vtotal, vdispend;
20462306a36Sopenharmony_ci	int tmp;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	htotal = mode->htotal / 8;
20762306a36Sopenharmony_ci	hsyncend = mode->hsync_end / 8;
20862306a36Sopenharmony_ci	hsyncstart = mode->hsync_start / 8;
20962306a36Sopenharmony_ci	hdispend = mode->hdisplay / 8;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	vtotal = mode->vtotal;
21262306a36Sopenharmony_ci	vdispend = mode->vdisplay;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	vdispend -= 1;
21562306a36Sopenharmony_ci	vtotal -= 2;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	htotal -= 5;
21862306a36Sopenharmony_ci	hdispend -= 1;
21962306a36Sopenharmony_ci	hsyncstart += 1;
22062306a36Sopenharmony_ci	hsyncend += 1;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_V_SYNC_END, 0x20);
22362306a36Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_H_TOTAL, htotal);
22462306a36Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_H_DISP, hdispend);
22562306a36Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_H_SYNC_START, hsyncstart);
22662306a36Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_H_SYNC_END, hsyncend);
22762306a36Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_V_TOTAL, vtotal & 0xff);
22862306a36Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_V_DISP_END, vdispend & 0xff);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	tmp = 0x40;
23162306a36Sopenharmony_ci	if ((vdispend + 1) & 512)
23262306a36Sopenharmony_ci		tmp |= 0x20;
23362306a36Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_MAX_SCAN, tmp);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/*
23662306a36Sopenharmony_ci	 * Overflow bits for values that don't fit in the standard registers
23762306a36Sopenharmony_ci	 */
23862306a36Sopenharmony_ci	tmp = 0x10;
23962306a36Sopenharmony_ci	if (vtotal & 0x100)
24062306a36Sopenharmony_ci		tmp |= 0x01;
24162306a36Sopenharmony_ci	if (vdispend & 0x100)
24262306a36Sopenharmony_ci		tmp |= 0x02;
24362306a36Sopenharmony_ci	if ((vdispend + 1) & 0x100)
24462306a36Sopenharmony_ci		tmp |= 0x08;
24562306a36Sopenharmony_ci	if (vtotal & 0x200)
24662306a36Sopenharmony_ci		tmp |= 0x20;
24762306a36Sopenharmony_ci	if (vdispend & 0x200)
24862306a36Sopenharmony_ci		tmp |= 0x40;
24962306a36Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_OVERFLOW, tmp);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	tmp = 0;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* More overflow bits */
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if ((htotal + 5) & 0x40)
25662306a36Sopenharmony_ci		tmp |= 0x10;
25762306a36Sopenharmony_ci	if ((htotal + 5) & 0x80)
25862306a36Sopenharmony_ci		tmp |= 0x20;
25962306a36Sopenharmony_ci	if (vtotal & 0x100)
26062306a36Sopenharmony_ci		tmp |= 0x40;
26162306a36Sopenharmony_ci	if (vtotal & 0x200)
26262306a36Sopenharmony_ci		tmp |= 0x80;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	wreg_crt(cirrus, CL_CRT1A, tmp);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/* Disable Hercules/CGA compatibility */
26762306a36Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_MODE, 0x03);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic void cirrus_format_set(struct cirrus_device *cirrus,
27162306a36Sopenharmony_ci			      const struct drm_format_info *format)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	u8 sr07, hdr;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	sr07 = rreg_seq(cirrus, 0x07);
27662306a36Sopenharmony_ci	sr07 &= 0xe0;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	switch (format->format) {
27962306a36Sopenharmony_ci	case DRM_FORMAT_C8:
28062306a36Sopenharmony_ci		sr07 |= 0x11;
28162306a36Sopenharmony_ci		hdr = 0x00;
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
28462306a36Sopenharmony_ci		sr07 |= 0x17;
28562306a36Sopenharmony_ci		hdr = 0xc1;
28662306a36Sopenharmony_ci		break;
28762306a36Sopenharmony_ci	case DRM_FORMAT_RGB888:
28862306a36Sopenharmony_ci		sr07 |= 0x15;
28962306a36Sopenharmony_ci		hdr = 0xc5;
29062306a36Sopenharmony_ci		break;
29162306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
29262306a36Sopenharmony_ci		sr07 |= 0x19;
29362306a36Sopenharmony_ci		hdr = 0xc5;
29462306a36Sopenharmony_ci		break;
29562306a36Sopenharmony_ci	default:
29662306a36Sopenharmony_ci		return;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	wreg_seq(cirrus, 0x7, sr07);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/* Enable high-colour modes */
30262306a36Sopenharmony_ci	wreg_gfx(cirrus, VGA_GFX_MODE, 0x40);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* And set graphics mode */
30562306a36Sopenharmony_ci	wreg_gfx(cirrus, VGA_GFX_MISC, 0x01);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	wreg_hdr(cirrus, hdr);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic void cirrus_pitch_set(struct cirrus_device *cirrus, unsigned int pitch)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	u8 cr13, cr1b;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* Program the pitch */
31562306a36Sopenharmony_ci	cr13 = pitch / 8;
31662306a36Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_OFFSET, cr13);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	/* Enable extended blanking and pitch bits, and enable full memory */
31962306a36Sopenharmony_ci	cr1b = 0x22;
32062306a36Sopenharmony_ci	cr1b |= (pitch >> 7) & 0x10;
32162306a36Sopenharmony_ci	cr1b |= (pitch >> 6) & 0x40;
32262306a36Sopenharmony_ci	wreg_crt(cirrus, 0x1b, cr1b);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	cirrus_set_start_address(cirrus, 0);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
32862306a36Sopenharmony_ci/* cirrus display pipe						      */
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic const uint32_t cirrus_primary_plane_formats[] = {
33162306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
33262306a36Sopenharmony_ci	DRM_FORMAT_RGB888,
33362306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
33462306a36Sopenharmony_ci};
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic const uint64_t cirrus_primary_plane_format_modifiers[] = {
33762306a36Sopenharmony_ci	DRM_FORMAT_MOD_LINEAR,
33862306a36Sopenharmony_ci	DRM_FORMAT_MOD_INVALID
33962306a36Sopenharmony_ci};
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic int cirrus_primary_plane_helper_atomic_check(struct drm_plane *plane,
34262306a36Sopenharmony_ci						    struct drm_atomic_state *state)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
34562306a36Sopenharmony_ci	struct cirrus_primary_plane_state *new_primary_plane_state =
34662306a36Sopenharmony_ci		to_cirrus_primary_plane_state(new_plane_state);
34762306a36Sopenharmony_ci	struct drm_framebuffer *fb = new_plane_state->fb;
34862306a36Sopenharmony_ci	struct drm_crtc *new_crtc = new_plane_state->crtc;
34962306a36Sopenharmony_ci	struct drm_crtc_state *new_crtc_state = NULL;
35062306a36Sopenharmony_ci	int ret;
35162306a36Sopenharmony_ci	unsigned int pitch;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (new_crtc)
35462306a36Sopenharmony_ci		new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
35762306a36Sopenharmony_ci						  DRM_PLANE_NO_SCALING,
35862306a36Sopenharmony_ci						  DRM_PLANE_NO_SCALING,
35962306a36Sopenharmony_ci						  false, false);
36062306a36Sopenharmony_ci	if (ret)
36162306a36Sopenharmony_ci		return ret;
36262306a36Sopenharmony_ci	else if (!new_plane_state->visible)
36362306a36Sopenharmony_ci		return 0;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	pitch = cirrus_pitch(fb);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/* validate size constraints */
36862306a36Sopenharmony_ci	if (pitch > CIRRUS_MAX_PITCH)
36962306a36Sopenharmony_ci		return -EINVAL;
37062306a36Sopenharmony_ci	else if (pitch * fb->height > CIRRUS_VRAM_SIZE)
37162306a36Sopenharmony_ci		return -EINVAL;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	new_primary_plane_state->format = cirrus_format(fb);
37462306a36Sopenharmony_ci	new_primary_plane_state->pitch = pitch;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	return 0;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic void cirrus_primary_plane_helper_atomic_update(struct drm_plane *plane,
38062306a36Sopenharmony_ci						      struct drm_atomic_state *state)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct cirrus_device *cirrus = to_cirrus(plane->dev);
38362306a36Sopenharmony_ci	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
38462306a36Sopenharmony_ci	struct cirrus_primary_plane_state *primary_plane_state =
38562306a36Sopenharmony_ci		to_cirrus_primary_plane_state(plane_state);
38662306a36Sopenharmony_ci	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
38762306a36Sopenharmony_ci	struct drm_framebuffer *fb = plane_state->fb;
38862306a36Sopenharmony_ci	const struct drm_format_info *format = primary_plane_state->format;
38962306a36Sopenharmony_ci	unsigned int pitch = primary_plane_state->pitch;
39062306a36Sopenharmony_ci	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
39162306a36Sopenharmony_ci	struct cirrus_primary_plane_state *old_primary_plane_state =
39262306a36Sopenharmony_ci		to_cirrus_primary_plane_state(old_plane_state);
39362306a36Sopenharmony_ci	struct iosys_map vaddr = IOSYS_MAP_INIT_VADDR_IOMEM(cirrus->vram);
39462306a36Sopenharmony_ci	struct drm_atomic_helper_damage_iter iter;
39562306a36Sopenharmony_ci	struct drm_rect damage;
39662306a36Sopenharmony_ci	int idx;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	if (!fb)
39962306a36Sopenharmony_ci		return;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (!drm_dev_enter(&cirrus->dev, &idx))
40262306a36Sopenharmony_ci		return;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (old_primary_plane_state->format != format)
40562306a36Sopenharmony_ci		cirrus_format_set(cirrus, format);
40662306a36Sopenharmony_ci	if (old_primary_plane_state->pitch != pitch)
40762306a36Sopenharmony_ci		cirrus_pitch_set(cirrus, pitch);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
41062306a36Sopenharmony_ci	drm_atomic_for_each_plane_damage(&iter, &damage) {
41162306a36Sopenharmony_ci		unsigned int offset = drm_fb_clip_offset(pitch, format, &damage);
41262306a36Sopenharmony_ci		struct iosys_map dst = IOSYS_MAP_INIT_OFFSET(&vaddr, offset);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		drm_fb_blit(&dst, &pitch, format->format, shadow_plane_state->data, fb, &damage);
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	drm_dev_exit(idx);
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs cirrus_primary_plane_helper_funcs = {
42162306a36Sopenharmony_ci	DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
42262306a36Sopenharmony_ci	.atomic_check = cirrus_primary_plane_helper_atomic_check,
42362306a36Sopenharmony_ci	.atomic_update = cirrus_primary_plane_helper_atomic_update,
42462306a36Sopenharmony_ci};
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic struct drm_plane_state *
42762306a36Sopenharmony_cicirrus_primary_plane_atomic_duplicate_state(struct drm_plane *plane)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	struct drm_plane_state *plane_state = plane->state;
43062306a36Sopenharmony_ci	struct cirrus_primary_plane_state *primary_plane_state =
43162306a36Sopenharmony_ci		to_cirrus_primary_plane_state(plane_state);
43262306a36Sopenharmony_ci	struct cirrus_primary_plane_state *new_primary_plane_state;
43362306a36Sopenharmony_ci	struct drm_shadow_plane_state *new_shadow_plane_state;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (!plane_state)
43662306a36Sopenharmony_ci		return NULL;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	new_primary_plane_state = kzalloc(sizeof(*new_primary_plane_state), GFP_KERNEL);
43962306a36Sopenharmony_ci	if (!new_primary_plane_state)
44062306a36Sopenharmony_ci		return NULL;
44162306a36Sopenharmony_ci	new_shadow_plane_state = &new_primary_plane_state->base;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	__drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state);
44462306a36Sopenharmony_ci	new_primary_plane_state->format = primary_plane_state->format;
44562306a36Sopenharmony_ci	new_primary_plane_state->pitch = primary_plane_state->pitch;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	return &new_shadow_plane_state->base;
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic void cirrus_primary_plane_atomic_destroy_state(struct drm_plane *plane,
45162306a36Sopenharmony_ci						      struct drm_plane_state *plane_state)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct cirrus_primary_plane_state *primary_plane_state =
45462306a36Sopenharmony_ci		to_cirrus_primary_plane_state(plane_state);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	__drm_gem_destroy_shadow_plane_state(&primary_plane_state->base);
45762306a36Sopenharmony_ci	kfree(primary_plane_state);
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic void cirrus_reset_primary_plane(struct drm_plane *plane)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct cirrus_primary_plane_state *primary_plane_state;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (plane->state) {
46562306a36Sopenharmony_ci		cirrus_primary_plane_atomic_destroy_state(plane, plane->state);
46662306a36Sopenharmony_ci		plane->state = NULL; /* must be set to NULL here */
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	primary_plane_state = kzalloc(sizeof(*primary_plane_state), GFP_KERNEL);
47062306a36Sopenharmony_ci	if (!primary_plane_state)
47162306a36Sopenharmony_ci		return;
47262306a36Sopenharmony_ci	__drm_gem_reset_shadow_plane(plane, &primary_plane_state->base);
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic const struct drm_plane_funcs cirrus_primary_plane_funcs = {
47662306a36Sopenharmony_ci	.update_plane = drm_atomic_helper_update_plane,
47762306a36Sopenharmony_ci	.disable_plane = drm_atomic_helper_disable_plane,
47862306a36Sopenharmony_ci	.destroy = drm_plane_cleanup,
47962306a36Sopenharmony_ci	.reset = cirrus_reset_primary_plane,
48062306a36Sopenharmony_ci	.atomic_duplicate_state = cirrus_primary_plane_atomic_duplicate_state,
48162306a36Sopenharmony_ci	.atomic_destroy_state = cirrus_primary_plane_atomic_destroy_state,
48262306a36Sopenharmony_ci};
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int cirrus_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
48762306a36Sopenharmony_ci	int ret;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (!crtc_state->enable)
49062306a36Sopenharmony_ci		return 0;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
49362306a36Sopenharmony_ci	if (ret)
49462306a36Sopenharmony_ci		return ret;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return 0;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic void cirrus_crtc_helper_atomic_enable(struct drm_crtc *crtc,
50062306a36Sopenharmony_ci					     struct drm_atomic_state *state)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct cirrus_device *cirrus = to_cirrus(crtc->dev);
50362306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
50462306a36Sopenharmony_ci	int idx;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (!drm_dev_enter(&cirrus->dev, &idx))
50762306a36Sopenharmony_ci		return;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	cirrus_mode_set(cirrus, &crtc_state->mode);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	/* Unblank (needed on S3 resume, vgabios doesn't do it then) */
51262306a36Sopenharmony_ci	outb(VGA_AR_ENABLE_DISPLAY, VGA_ATT_W);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	drm_dev_exit(idx);
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs cirrus_crtc_helper_funcs = {
51862306a36Sopenharmony_ci	.atomic_check = cirrus_crtc_helper_atomic_check,
51962306a36Sopenharmony_ci	.atomic_enable = cirrus_crtc_helper_atomic_enable,
52062306a36Sopenharmony_ci};
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic const struct drm_crtc_funcs cirrus_crtc_funcs = {
52362306a36Sopenharmony_ci	.reset = drm_atomic_helper_crtc_reset,
52462306a36Sopenharmony_ci	.destroy = drm_crtc_cleanup,
52562306a36Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
52662306a36Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
52762306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
52862306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
52962306a36Sopenharmony_ci};
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic const struct drm_encoder_funcs cirrus_encoder_funcs = {
53262306a36Sopenharmony_ci	.destroy = drm_encoder_cleanup,
53362306a36Sopenharmony_ci};
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic int cirrus_connector_helper_get_modes(struct drm_connector *connector)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	int count;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	count = drm_add_modes_noedid(connector,
54062306a36Sopenharmony_ci				     connector->dev->mode_config.max_width,
54162306a36Sopenharmony_ci				     connector->dev->mode_config.max_height);
54262306a36Sopenharmony_ci	drm_set_preferred_mode(connector, 1024, 768);
54362306a36Sopenharmony_ci	return count;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs cirrus_connector_helper_funcs = {
54762306a36Sopenharmony_ci	.get_modes = cirrus_connector_helper_get_modes,
54862306a36Sopenharmony_ci};
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic const struct drm_connector_funcs cirrus_connector_funcs = {
55162306a36Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
55262306a36Sopenharmony_ci	.destroy = drm_connector_cleanup,
55362306a36Sopenharmony_ci	.reset = drm_atomic_helper_connector_reset,
55462306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
55562306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
55662306a36Sopenharmony_ci};
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic int cirrus_pipe_init(struct cirrus_device *cirrus)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	struct drm_device *dev = &cirrus->dev;
56162306a36Sopenharmony_ci	struct drm_plane *primary_plane;
56262306a36Sopenharmony_ci	struct drm_crtc *crtc;
56362306a36Sopenharmony_ci	struct drm_encoder *encoder;
56462306a36Sopenharmony_ci	struct drm_connector *connector;
56562306a36Sopenharmony_ci	int ret;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	primary_plane = &cirrus->primary_plane;
56862306a36Sopenharmony_ci	ret = drm_universal_plane_init(dev, primary_plane, 0,
56962306a36Sopenharmony_ci				       &cirrus_primary_plane_funcs,
57062306a36Sopenharmony_ci				       cirrus_primary_plane_formats,
57162306a36Sopenharmony_ci				       ARRAY_SIZE(cirrus_primary_plane_formats),
57262306a36Sopenharmony_ci				       cirrus_primary_plane_format_modifiers,
57362306a36Sopenharmony_ci				       DRM_PLANE_TYPE_PRIMARY, NULL);
57462306a36Sopenharmony_ci	if (ret)
57562306a36Sopenharmony_ci		return ret;
57662306a36Sopenharmony_ci	drm_plane_helper_add(primary_plane, &cirrus_primary_plane_helper_funcs);
57762306a36Sopenharmony_ci	drm_plane_enable_fb_damage_clips(primary_plane);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	crtc = &cirrus->crtc;
58062306a36Sopenharmony_ci	ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
58162306a36Sopenharmony_ci					&cirrus_crtc_funcs, NULL);
58262306a36Sopenharmony_ci	if (ret)
58362306a36Sopenharmony_ci		return ret;
58462306a36Sopenharmony_ci	drm_crtc_helper_add(crtc, &cirrus_crtc_helper_funcs);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	encoder = &cirrus->encoder;
58762306a36Sopenharmony_ci	ret = drm_encoder_init(dev, encoder, &cirrus_encoder_funcs,
58862306a36Sopenharmony_ci			       DRM_MODE_ENCODER_DAC, NULL);
58962306a36Sopenharmony_ci	if (ret)
59062306a36Sopenharmony_ci		return ret;
59162306a36Sopenharmony_ci	encoder->possible_crtcs = drm_crtc_mask(crtc);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	connector = &cirrus->connector;
59462306a36Sopenharmony_ci	ret = drm_connector_init(dev, connector, &cirrus_connector_funcs,
59562306a36Sopenharmony_ci				 DRM_MODE_CONNECTOR_VGA);
59662306a36Sopenharmony_ci	if (ret)
59762306a36Sopenharmony_ci		return ret;
59862306a36Sopenharmony_ci	drm_connector_helper_add(connector, &cirrus_connector_helper_funcs);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	ret = drm_connector_attach_encoder(connector, encoder);
60162306a36Sopenharmony_ci	if (ret)
60262306a36Sopenharmony_ci		return ret;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	return 0;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
60862306a36Sopenharmony_ci/* cirrus framebuffers & mode config				      */
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic enum drm_mode_status cirrus_mode_config_mode_valid(struct drm_device *dev,
61162306a36Sopenharmony_ci							  const struct drm_display_mode *mode)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	const struct drm_format_info *format = drm_format_info(DRM_FORMAT_XRGB8888);
61462306a36Sopenharmony_ci	uint64_t pitch = drm_format_info_min_pitch(format, 0, mode->hdisplay);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (pitch * mode->vdisplay > CIRRUS_VRAM_SIZE)
61762306a36Sopenharmony_ci		return MODE_MEM;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	return MODE_OK;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic const struct drm_mode_config_funcs cirrus_mode_config_funcs = {
62362306a36Sopenharmony_ci	.fb_create = drm_gem_fb_create_with_dirty,
62462306a36Sopenharmony_ci	.mode_valid = cirrus_mode_config_mode_valid,
62562306a36Sopenharmony_ci	.atomic_check = drm_atomic_helper_check,
62662306a36Sopenharmony_ci	.atomic_commit = drm_atomic_helper_commit,
62762306a36Sopenharmony_ci};
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cistatic int cirrus_mode_config_init(struct cirrus_device *cirrus)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	struct drm_device *dev = &cirrus->dev;
63262306a36Sopenharmony_ci	int ret;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	ret = drmm_mode_config_init(dev);
63562306a36Sopenharmony_ci	if (ret)
63662306a36Sopenharmony_ci		return ret;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	dev->mode_config.min_width = 0;
63962306a36Sopenharmony_ci	dev->mode_config.min_height = 0;
64062306a36Sopenharmony_ci	dev->mode_config.max_width = CIRRUS_MAX_PITCH / 2;
64162306a36Sopenharmony_ci	dev->mode_config.max_height = 1024;
64262306a36Sopenharmony_ci	dev->mode_config.preferred_depth = 16;
64362306a36Sopenharmony_ci	dev->mode_config.prefer_shadow = 0;
64462306a36Sopenharmony_ci	dev->mode_config.funcs = &cirrus_mode_config_funcs;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	return 0;
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ciDEFINE_DRM_GEM_FOPS(cirrus_fops);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic const struct drm_driver cirrus_driver = {
65462306a36Sopenharmony_ci	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	.name		 = DRIVER_NAME,
65762306a36Sopenharmony_ci	.desc		 = DRIVER_DESC,
65862306a36Sopenharmony_ci	.date		 = DRIVER_DATE,
65962306a36Sopenharmony_ci	.major		 = DRIVER_MAJOR,
66062306a36Sopenharmony_ci	.minor		 = DRIVER_MINOR,
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	.fops		 = &cirrus_fops,
66362306a36Sopenharmony_ci	DRM_GEM_SHMEM_DRIVER_OPS,
66462306a36Sopenharmony_ci};
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic int cirrus_pci_probe(struct pci_dev *pdev,
66762306a36Sopenharmony_ci			    const struct pci_device_id *ent)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct drm_device *dev;
67062306a36Sopenharmony_ci	struct cirrus_device *cirrus;
67162306a36Sopenharmony_ci	int ret;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &cirrus_driver);
67462306a36Sopenharmony_ci	if (ret)
67562306a36Sopenharmony_ci		return ret;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	ret = pcim_enable_device(pdev);
67862306a36Sopenharmony_ci	if (ret)
67962306a36Sopenharmony_ci		return ret;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	ret = pci_request_regions(pdev, DRIVER_NAME);
68262306a36Sopenharmony_ci	if (ret)
68362306a36Sopenharmony_ci		return ret;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	ret = -ENOMEM;
68662306a36Sopenharmony_ci	cirrus = devm_drm_dev_alloc(&pdev->dev, &cirrus_driver,
68762306a36Sopenharmony_ci				    struct cirrus_device, dev);
68862306a36Sopenharmony_ci	if (IS_ERR(cirrus))
68962306a36Sopenharmony_ci		return PTR_ERR(cirrus);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	dev = &cirrus->dev;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	cirrus->vram = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0),
69462306a36Sopenharmony_ci				    pci_resource_len(pdev, 0));
69562306a36Sopenharmony_ci	if (cirrus->vram == NULL)
69662306a36Sopenharmony_ci		return -ENOMEM;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	cirrus->mmio = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 1),
69962306a36Sopenharmony_ci				    pci_resource_len(pdev, 1));
70062306a36Sopenharmony_ci	if (cirrus->mmio == NULL)
70162306a36Sopenharmony_ci		return -ENOMEM;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	ret = cirrus_mode_config_init(cirrus);
70462306a36Sopenharmony_ci	if (ret)
70562306a36Sopenharmony_ci		return ret;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	ret = cirrus_pipe_init(cirrus);
70862306a36Sopenharmony_ci	if (ret < 0)
70962306a36Sopenharmony_ci		return ret;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	drm_mode_config_reset(dev);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	pci_set_drvdata(pdev, dev);
71462306a36Sopenharmony_ci	ret = drm_dev_register(dev, 0);
71562306a36Sopenharmony_ci	if (ret)
71662306a36Sopenharmony_ci		return ret;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	drm_fbdev_generic_setup(dev, 16);
71962306a36Sopenharmony_ci	return 0;
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistatic void cirrus_pci_remove(struct pci_dev *pdev)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct drm_device *dev = pci_get_drvdata(pdev);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	drm_dev_unplug(dev);
72762306a36Sopenharmony_ci	drm_atomic_helper_shutdown(dev);
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cistatic const struct pci_device_id pciidlist[] = {
73162306a36Sopenharmony_ci	{
73262306a36Sopenharmony_ci		.vendor    = PCI_VENDOR_ID_CIRRUS,
73362306a36Sopenharmony_ci		.device    = PCI_DEVICE_ID_CIRRUS_5446,
73462306a36Sopenharmony_ci		/* only bind to the cirrus chip in qemu */
73562306a36Sopenharmony_ci		.subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET,
73662306a36Sopenharmony_ci		.subdevice = PCI_SUBDEVICE_ID_QEMU,
73762306a36Sopenharmony_ci	}, {
73862306a36Sopenharmony_ci		.vendor    = PCI_VENDOR_ID_CIRRUS,
73962306a36Sopenharmony_ci		.device    = PCI_DEVICE_ID_CIRRUS_5446,
74062306a36Sopenharmony_ci		.subvendor = PCI_VENDOR_ID_XEN,
74162306a36Sopenharmony_ci		.subdevice = 0x0001,
74262306a36Sopenharmony_ci	},
74362306a36Sopenharmony_ci	{ /* end if list */ }
74462306a36Sopenharmony_ci};
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_cistatic struct pci_driver cirrus_pci_driver = {
74762306a36Sopenharmony_ci	.name = DRIVER_NAME,
74862306a36Sopenharmony_ci	.id_table = pciidlist,
74962306a36Sopenharmony_ci	.probe = cirrus_pci_probe,
75062306a36Sopenharmony_ci	.remove = cirrus_pci_remove,
75162306a36Sopenharmony_ci};
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cidrm_module_pci_driver(cirrus_pci_driver)
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pciidlist);
75662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
757