18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2012-2019 Red Hat
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General
68c2ecf20Sopenharmony_ci * Public License version 2. See the file COPYING in the main
78c2ecf20Sopenharmony_ci * directory of this archive for more details.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Authors: Matthew Garrett
108c2ecf20Sopenharmony_ci *	    Dave Airlie
118c2ecf20Sopenharmony_ci *	    Gerd Hoffmann
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Portions of this code derived from cirrusfb.c:
148c2ecf20Sopenharmony_ci * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/console.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/pci.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <video/cirrus.h>
248c2ecf20Sopenharmony_ci#include <video/vga.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
278c2ecf20Sopenharmony_ci#include <drm/drm_atomic_state_helper.h>
288c2ecf20Sopenharmony_ci#include <drm/drm_connector.h>
298c2ecf20Sopenharmony_ci#include <drm/drm_damage_helper.h>
308c2ecf20Sopenharmony_ci#include <drm/drm_drv.h>
318c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h>
328c2ecf20Sopenharmony_ci#include <drm/drm_file.h>
338c2ecf20Sopenharmony_ci#include <drm/drm_format_helper.h>
348c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
358c2ecf20Sopenharmony_ci#include <drm/drm_gem_shmem_helper.h>
368c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
378c2ecf20Sopenharmony_ci#include <drm/drm_ioctl.h>
388c2ecf20Sopenharmony_ci#include <drm/drm_managed.h>
398c2ecf20Sopenharmony_ci#include <drm/drm_modeset_helper_vtables.h>
408c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
418c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h>
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define DRIVER_NAME "cirrus"
448c2ecf20Sopenharmony_ci#define DRIVER_DESC "qemu cirrus vga"
458c2ecf20Sopenharmony_ci#define DRIVER_DATE "2019"
468c2ecf20Sopenharmony_ci#define DRIVER_MAJOR 2
478c2ecf20Sopenharmony_ci#define DRIVER_MINOR 0
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define CIRRUS_MAX_PITCH (0x1FF << 3)      /* (4096 - 1) & ~111b bytes */
508c2ecf20Sopenharmony_ci#define CIRRUS_VRAM_SIZE (4 * 1024 * 1024) /* 4 MB */
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistruct cirrus_device {
538c2ecf20Sopenharmony_ci	struct drm_device	       dev;
548c2ecf20Sopenharmony_ci	struct drm_simple_display_pipe pipe;
558c2ecf20Sopenharmony_ci	struct drm_connector	       conn;
568c2ecf20Sopenharmony_ci	unsigned int		       cpp;
578c2ecf20Sopenharmony_ci	unsigned int		       pitch;
588c2ecf20Sopenharmony_ci	void __iomem		       *vram;
598c2ecf20Sopenharmony_ci	void __iomem		       *mmio;
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define to_cirrus(_dev) container_of(_dev, struct cirrus_device, dev)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
658c2ecf20Sopenharmony_ci/*
668c2ecf20Sopenharmony_ci * The meat of this driver. The core passes us a mode and we have to program
678c2ecf20Sopenharmony_ci * it. The modesetting here is the bare minimum required to satisfy the qemu
688c2ecf20Sopenharmony_ci * emulation of this hardware, and running this against a real device is
698c2ecf20Sopenharmony_ci * likely to result in an inadequately programmed mode. We've already had
708c2ecf20Sopenharmony_ci * the opportunity to modify the mode, so whatever we receive here should
718c2ecf20Sopenharmony_ci * be something that can be correctly programmed and displayed
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#define SEQ_INDEX 4
758c2ecf20Sopenharmony_ci#define SEQ_DATA 5
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic u8 rreg_seq(struct cirrus_device *cirrus, u8 reg)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	iowrite8(reg, cirrus->mmio + SEQ_INDEX);
808c2ecf20Sopenharmony_ci	return ioread8(cirrus->mmio + SEQ_DATA);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic void wreg_seq(struct cirrus_device *cirrus, u8 reg, u8 val)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	iowrite8(reg, cirrus->mmio + SEQ_INDEX);
868c2ecf20Sopenharmony_ci	iowrite8(val, cirrus->mmio + SEQ_DATA);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#define CRT_INDEX 0x14
908c2ecf20Sopenharmony_ci#define CRT_DATA 0x15
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic u8 rreg_crt(struct cirrus_device *cirrus, u8 reg)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	iowrite8(reg, cirrus->mmio + CRT_INDEX);
958c2ecf20Sopenharmony_ci	return ioread8(cirrus->mmio + CRT_DATA);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic void wreg_crt(struct cirrus_device *cirrus, u8 reg, u8 val)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	iowrite8(reg, cirrus->mmio + CRT_INDEX);
1018c2ecf20Sopenharmony_ci	iowrite8(val, cirrus->mmio + CRT_DATA);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci#define GFX_INDEX 0xe
1058c2ecf20Sopenharmony_ci#define GFX_DATA 0xf
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic void wreg_gfx(struct cirrus_device *cirrus, u8 reg, u8 val)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	iowrite8(reg, cirrus->mmio + GFX_INDEX);
1108c2ecf20Sopenharmony_ci	iowrite8(val, cirrus->mmio + GFX_DATA);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#define VGA_DAC_MASK  0x06
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic void wreg_hdr(struct cirrus_device *cirrus, u8 val)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	ioread8(cirrus->mmio + VGA_DAC_MASK);
1188c2ecf20Sopenharmony_ci	ioread8(cirrus->mmio + VGA_DAC_MASK);
1198c2ecf20Sopenharmony_ci	ioread8(cirrus->mmio + VGA_DAC_MASK);
1208c2ecf20Sopenharmony_ci	ioread8(cirrus->mmio + VGA_DAC_MASK);
1218c2ecf20Sopenharmony_ci	iowrite8(val, cirrus->mmio + VGA_DAC_MASK);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int cirrus_convert_to(struct drm_framebuffer *fb)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	if (fb->format->cpp[0] == 4 && fb->pitches[0] > CIRRUS_MAX_PITCH) {
1278c2ecf20Sopenharmony_ci		if (fb->width * 3 <= CIRRUS_MAX_PITCH)
1288c2ecf20Sopenharmony_ci			/* convert from XR24 to RG24 */
1298c2ecf20Sopenharmony_ci			return 3;
1308c2ecf20Sopenharmony_ci		else
1318c2ecf20Sopenharmony_ci			/* convert from XR24 to RG16 */
1328c2ecf20Sopenharmony_ci			return 2;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci	return 0;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int cirrus_cpp(struct drm_framebuffer *fb)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	int convert_cpp = cirrus_convert_to(fb);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (convert_cpp)
1428c2ecf20Sopenharmony_ci		return convert_cpp;
1438c2ecf20Sopenharmony_ci	return fb->format->cpp[0];
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int cirrus_pitch(struct drm_framebuffer *fb)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	int convert_cpp = cirrus_convert_to(fb);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (convert_cpp)
1518c2ecf20Sopenharmony_ci		return convert_cpp * fb->width;
1528c2ecf20Sopenharmony_ci	return fb->pitches[0];
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic void cirrus_set_start_address(struct cirrus_device *cirrus, u32 offset)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	int idx;
1588c2ecf20Sopenharmony_ci	u32 addr;
1598c2ecf20Sopenharmony_ci	u8 tmp;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (!drm_dev_enter(&cirrus->dev, &idx))
1628c2ecf20Sopenharmony_ci		return;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	addr = offset >> 2;
1658c2ecf20Sopenharmony_ci	wreg_crt(cirrus, 0x0c, (u8)((addr >> 8) & 0xff));
1668c2ecf20Sopenharmony_ci	wreg_crt(cirrus, 0x0d, (u8)(addr & 0xff));
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	tmp = rreg_crt(cirrus, 0x1b);
1698c2ecf20Sopenharmony_ci	tmp &= 0xf2;
1708c2ecf20Sopenharmony_ci	tmp |= (addr >> 16) & 0x01;
1718c2ecf20Sopenharmony_ci	tmp |= (addr >> 15) & 0x0c;
1728c2ecf20Sopenharmony_ci	wreg_crt(cirrus, 0x1b, tmp);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	tmp = rreg_crt(cirrus, 0x1d);
1758c2ecf20Sopenharmony_ci	tmp &= 0x7f;
1768c2ecf20Sopenharmony_ci	tmp |= (addr >> 12) & 0x80;
1778c2ecf20Sopenharmony_ci	wreg_crt(cirrus, 0x1d, tmp);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	drm_dev_exit(idx);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic int cirrus_mode_set(struct cirrus_device *cirrus,
1838c2ecf20Sopenharmony_ci			   struct drm_display_mode *mode,
1848c2ecf20Sopenharmony_ci			   struct drm_framebuffer *fb)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	int hsyncstart, hsyncend, htotal, hdispend;
1878c2ecf20Sopenharmony_ci	int vtotal, vdispend;
1888c2ecf20Sopenharmony_ci	int tmp, idx;
1898c2ecf20Sopenharmony_ci	int sr07 = 0, hdr = 0;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (!drm_dev_enter(&cirrus->dev, &idx))
1928c2ecf20Sopenharmony_ci		return -1;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	htotal = mode->htotal / 8;
1958c2ecf20Sopenharmony_ci	hsyncend = mode->hsync_end / 8;
1968c2ecf20Sopenharmony_ci	hsyncstart = mode->hsync_start / 8;
1978c2ecf20Sopenharmony_ci	hdispend = mode->hdisplay / 8;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	vtotal = mode->vtotal;
2008c2ecf20Sopenharmony_ci	vdispend = mode->vdisplay;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	vdispend -= 1;
2038c2ecf20Sopenharmony_ci	vtotal -= 2;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	htotal -= 5;
2068c2ecf20Sopenharmony_ci	hdispend -= 1;
2078c2ecf20Sopenharmony_ci	hsyncstart += 1;
2088c2ecf20Sopenharmony_ci	hsyncend += 1;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_V_SYNC_END, 0x20);
2118c2ecf20Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_H_TOTAL, htotal);
2128c2ecf20Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_H_DISP, hdispend);
2138c2ecf20Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_H_SYNC_START, hsyncstart);
2148c2ecf20Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_H_SYNC_END, hsyncend);
2158c2ecf20Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_V_TOTAL, vtotal & 0xff);
2168c2ecf20Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_V_DISP_END, vdispend & 0xff);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	tmp = 0x40;
2198c2ecf20Sopenharmony_ci	if ((vdispend + 1) & 512)
2208c2ecf20Sopenharmony_ci		tmp |= 0x20;
2218c2ecf20Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_MAX_SCAN, tmp);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/*
2248c2ecf20Sopenharmony_ci	 * Overflow bits for values that don't fit in the standard registers
2258c2ecf20Sopenharmony_ci	 */
2268c2ecf20Sopenharmony_ci	tmp = 0x10;
2278c2ecf20Sopenharmony_ci	if (vtotal & 0x100)
2288c2ecf20Sopenharmony_ci		tmp |= 0x01;
2298c2ecf20Sopenharmony_ci	if (vdispend & 0x100)
2308c2ecf20Sopenharmony_ci		tmp |= 0x02;
2318c2ecf20Sopenharmony_ci	if ((vdispend + 1) & 0x100)
2328c2ecf20Sopenharmony_ci		tmp |= 0x08;
2338c2ecf20Sopenharmony_ci	if (vtotal & 0x200)
2348c2ecf20Sopenharmony_ci		tmp |= 0x20;
2358c2ecf20Sopenharmony_ci	if (vdispend & 0x200)
2368c2ecf20Sopenharmony_ci		tmp |= 0x40;
2378c2ecf20Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_OVERFLOW, tmp);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	tmp = 0;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	/* More overflow bits */
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if ((htotal + 5) & 0x40)
2448c2ecf20Sopenharmony_ci		tmp |= 0x10;
2458c2ecf20Sopenharmony_ci	if ((htotal + 5) & 0x80)
2468c2ecf20Sopenharmony_ci		tmp |= 0x20;
2478c2ecf20Sopenharmony_ci	if (vtotal & 0x100)
2488c2ecf20Sopenharmony_ci		tmp |= 0x40;
2498c2ecf20Sopenharmony_ci	if (vtotal & 0x200)
2508c2ecf20Sopenharmony_ci		tmp |= 0x80;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	wreg_crt(cirrus, CL_CRT1A, tmp);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* Disable Hercules/CGA compatibility */
2558c2ecf20Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_MODE, 0x03);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	sr07 = rreg_seq(cirrus, 0x07);
2588c2ecf20Sopenharmony_ci	sr07 &= 0xe0;
2598c2ecf20Sopenharmony_ci	hdr = 0;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	cirrus->cpp = cirrus_cpp(fb);
2628c2ecf20Sopenharmony_ci	switch (cirrus->cpp * 8) {
2638c2ecf20Sopenharmony_ci	case 8:
2648c2ecf20Sopenharmony_ci		sr07 |= 0x11;
2658c2ecf20Sopenharmony_ci		break;
2668c2ecf20Sopenharmony_ci	case 16:
2678c2ecf20Sopenharmony_ci		sr07 |= 0x17;
2688c2ecf20Sopenharmony_ci		hdr = 0xc1;
2698c2ecf20Sopenharmony_ci		break;
2708c2ecf20Sopenharmony_ci	case 24:
2718c2ecf20Sopenharmony_ci		sr07 |= 0x15;
2728c2ecf20Sopenharmony_ci		hdr = 0xc5;
2738c2ecf20Sopenharmony_ci		break;
2748c2ecf20Sopenharmony_ci	case 32:
2758c2ecf20Sopenharmony_ci		sr07 |= 0x19;
2768c2ecf20Sopenharmony_ci		hdr = 0xc5;
2778c2ecf20Sopenharmony_ci		break;
2788c2ecf20Sopenharmony_ci	default:
2798c2ecf20Sopenharmony_ci		drm_dev_exit(idx);
2808c2ecf20Sopenharmony_ci		return -1;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	wreg_seq(cirrus, 0x7, sr07);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	/* Program the pitch */
2868c2ecf20Sopenharmony_ci	cirrus->pitch = cirrus_pitch(fb);
2878c2ecf20Sopenharmony_ci	tmp = cirrus->pitch / 8;
2888c2ecf20Sopenharmony_ci	wreg_crt(cirrus, VGA_CRTC_OFFSET, tmp);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/* Enable extended blanking and pitch bits, and enable full memory */
2918c2ecf20Sopenharmony_ci	tmp = 0x22;
2928c2ecf20Sopenharmony_ci	tmp |= (cirrus->pitch >> 7) & 0x10;
2938c2ecf20Sopenharmony_ci	tmp |= (cirrus->pitch >> 6) & 0x40;
2948c2ecf20Sopenharmony_ci	wreg_crt(cirrus, 0x1b, tmp);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* Enable high-colour modes */
2978c2ecf20Sopenharmony_ci	wreg_gfx(cirrus, VGA_GFX_MODE, 0x40);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/* And set graphics mode */
3008c2ecf20Sopenharmony_ci	wreg_gfx(cirrus, VGA_GFX_MISC, 0x01);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	wreg_hdr(cirrus, hdr);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	cirrus_set_start_address(cirrus, 0);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* Unblank (needed on S3 resume, vgabios doesn't do it then) */
3078c2ecf20Sopenharmony_ci	outb(0x20, 0x3c0);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	drm_dev_exit(idx);
3108c2ecf20Sopenharmony_ci	return 0;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic int cirrus_fb_blit_rect(struct drm_framebuffer *fb,
3148c2ecf20Sopenharmony_ci			       struct drm_rect *rect)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	struct cirrus_device *cirrus = to_cirrus(fb->dev);
3178c2ecf20Sopenharmony_ci	void *vmap;
3188c2ecf20Sopenharmony_ci	int idx, ret;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	ret = -ENODEV;
3218c2ecf20Sopenharmony_ci	if (!drm_dev_enter(&cirrus->dev, &idx))
3228c2ecf20Sopenharmony_ci		goto out;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	ret = -ENOMEM;
3258c2ecf20Sopenharmony_ci	vmap = drm_gem_shmem_vmap(fb->obj[0]);
3268c2ecf20Sopenharmony_ci	if (!vmap)
3278c2ecf20Sopenharmony_ci		goto out_dev_exit;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (cirrus->cpp == fb->format->cpp[0])
3308c2ecf20Sopenharmony_ci		drm_fb_memcpy_dstclip(cirrus->vram,
3318c2ecf20Sopenharmony_ci				      vmap, fb, rect);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	else if (fb->format->cpp[0] == 4 && cirrus->cpp == 2)
3348c2ecf20Sopenharmony_ci		drm_fb_xrgb8888_to_rgb565_dstclip(cirrus->vram,
3358c2ecf20Sopenharmony_ci						  cirrus->pitch,
3368c2ecf20Sopenharmony_ci						  vmap, fb, rect, false);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	else if (fb->format->cpp[0] == 4 && cirrus->cpp == 3)
3398c2ecf20Sopenharmony_ci		drm_fb_xrgb8888_to_rgb888_dstclip(cirrus->vram,
3408c2ecf20Sopenharmony_ci						  cirrus->pitch,
3418c2ecf20Sopenharmony_ci						  vmap, fb, rect);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	else
3448c2ecf20Sopenharmony_ci		WARN_ON_ONCE("cpp mismatch");
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	drm_gem_shmem_vunmap(fb->obj[0], vmap);
3478c2ecf20Sopenharmony_ci	ret = 0;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ciout_dev_exit:
3508c2ecf20Sopenharmony_ci	drm_dev_exit(idx);
3518c2ecf20Sopenharmony_ciout:
3528c2ecf20Sopenharmony_ci	return ret;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic int cirrus_fb_blit_fullscreen(struct drm_framebuffer *fb)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct drm_rect fullscreen = {
3588c2ecf20Sopenharmony_ci		.x1 = 0,
3598c2ecf20Sopenharmony_ci		.x2 = fb->width,
3608c2ecf20Sopenharmony_ci		.y1 = 0,
3618c2ecf20Sopenharmony_ci		.y2 = fb->height,
3628c2ecf20Sopenharmony_ci	};
3638c2ecf20Sopenharmony_ci	return cirrus_fb_blit_rect(fb, &fullscreen);
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic int cirrus_check_size(int width, int height,
3678c2ecf20Sopenharmony_ci			     struct drm_framebuffer *fb)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	int pitch = width * 2;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (fb)
3728c2ecf20Sopenharmony_ci		pitch = cirrus_pitch(fb);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	if (pitch > CIRRUS_MAX_PITCH)
3758c2ecf20Sopenharmony_ci		return -EINVAL;
3768c2ecf20Sopenharmony_ci	if (pitch * height > CIRRUS_VRAM_SIZE)
3778c2ecf20Sopenharmony_ci		return -EINVAL;
3788c2ecf20Sopenharmony_ci	return 0;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
3828c2ecf20Sopenharmony_ci/* cirrus connector						      */
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic int cirrus_conn_get_modes(struct drm_connector *conn)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	int count;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	count = drm_add_modes_noedid(conn,
3898c2ecf20Sopenharmony_ci				     conn->dev->mode_config.max_width,
3908c2ecf20Sopenharmony_ci				     conn->dev->mode_config.max_height);
3918c2ecf20Sopenharmony_ci	drm_set_preferred_mode(conn, 1024, 768);
3928c2ecf20Sopenharmony_ci	return count;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs cirrus_conn_helper_funcs = {
3968c2ecf20Sopenharmony_ci	.get_modes = cirrus_conn_get_modes,
3978c2ecf20Sopenharmony_ci};
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs cirrus_conn_funcs = {
4008c2ecf20Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
4018c2ecf20Sopenharmony_ci	.destroy = drm_connector_cleanup,
4028c2ecf20Sopenharmony_ci	.reset = drm_atomic_helper_connector_reset,
4038c2ecf20Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
4048c2ecf20Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
4058c2ecf20Sopenharmony_ci};
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic int cirrus_conn_init(struct cirrus_device *cirrus)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	drm_connector_helper_add(&cirrus->conn, &cirrus_conn_helper_funcs);
4108c2ecf20Sopenharmony_ci	return drm_connector_init(&cirrus->dev, &cirrus->conn,
4118c2ecf20Sopenharmony_ci				  &cirrus_conn_funcs, DRM_MODE_CONNECTOR_VGA);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
4168c2ecf20Sopenharmony_ci/* cirrus (simple) display pipe					      */
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cistatic enum drm_mode_status cirrus_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
4198c2ecf20Sopenharmony_ci						   const struct drm_display_mode *mode)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	if (cirrus_check_size(mode->hdisplay, mode->vdisplay, NULL) < 0)
4228c2ecf20Sopenharmony_ci		return MODE_BAD;
4238c2ecf20Sopenharmony_ci	return MODE_OK;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic int cirrus_pipe_check(struct drm_simple_display_pipe *pipe,
4278c2ecf20Sopenharmony_ci			     struct drm_plane_state *plane_state,
4288c2ecf20Sopenharmony_ci			     struct drm_crtc_state *crtc_state)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	struct drm_framebuffer *fb = plane_state->fb;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	if (!fb)
4338c2ecf20Sopenharmony_ci		return 0;
4348c2ecf20Sopenharmony_ci	return cirrus_check_size(fb->width, fb->height, fb);
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic void cirrus_pipe_enable(struct drm_simple_display_pipe *pipe,
4388c2ecf20Sopenharmony_ci			       struct drm_crtc_state *crtc_state,
4398c2ecf20Sopenharmony_ci			       struct drm_plane_state *plane_state)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct cirrus_device *cirrus = to_cirrus(pipe->crtc.dev);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	cirrus_mode_set(cirrus, &crtc_state->mode, plane_state->fb);
4448c2ecf20Sopenharmony_ci	cirrus_fb_blit_fullscreen(plane_state->fb);
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic void cirrus_pipe_update(struct drm_simple_display_pipe *pipe,
4488c2ecf20Sopenharmony_ci			       struct drm_plane_state *old_state)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	struct cirrus_device *cirrus = to_cirrus(pipe->crtc.dev);
4518c2ecf20Sopenharmony_ci	struct drm_plane_state *state = pipe->plane.state;
4528c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = &pipe->crtc;
4538c2ecf20Sopenharmony_ci	struct drm_rect rect;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (pipe->plane.state->fb &&
4568c2ecf20Sopenharmony_ci	    cirrus->cpp != cirrus_cpp(pipe->plane.state->fb))
4578c2ecf20Sopenharmony_ci		cirrus_mode_set(cirrus, &crtc->mode,
4588c2ecf20Sopenharmony_ci				pipe->plane.state->fb);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if (drm_atomic_helper_damage_merged(old_state, state, &rect))
4618c2ecf20Sopenharmony_ci		cirrus_fb_blit_rect(pipe->plane.state->fb, &rect);
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic const struct drm_simple_display_pipe_funcs cirrus_pipe_funcs = {
4658c2ecf20Sopenharmony_ci	.mode_valid = cirrus_pipe_mode_valid,
4668c2ecf20Sopenharmony_ci	.check	    = cirrus_pipe_check,
4678c2ecf20Sopenharmony_ci	.enable	    = cirrus_pipe_enable,
4688c2ecf20Sopenharmony_ci	.update	    = cirrus_pipe_update,
4698c2ecf20Sopenharmony_ci};
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic const uint32_t cirrus_formats[] = {
4728c2ecf20Sopenharmony_ci	DRM_FORMAT_RGB565,
4738c2ecf20Sopenharmony_ci	DRM_FORMAT_RGB888,
4748c2ecf20Sopenharmony_ci	DRM_FORMAT_XRGB8888,
4758c2ecf20Sopenharmony_ci};
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic const uint64_t cirrus_modifiers[] = {
4788c2ecf20Sopenharmony_ci	DRM_FORMAT_MOD_LINEAR,
4798c2ecf20Sopenharmony_ci	DRM_FORMAT_MOD_INVALID
4808c2ecf20Sopenharmony_ci};
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic int cirrus_pipe_init(struct cirrus_device *cirrus)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	return drm_simple_display_pipe_init(&cirrus->dev,
4858c2ecf20Sopenharmony_ci					    &cirrus->pipe,
4868c2ecf20Sopenharmony_ci					    &cirrus_pipe_funcs,
4878c2ecf20Sopenharmony_ci					    cirrus_formats,
4888c2ecf20Sopenharmony_ci					    ARRAY_SIZE(cirrus_formats),
4898c2ecf20Sopenharmony_ci					    cirrus_modifiers,
4908c2ecf20Sopenharmony_ci					    &cirrus->conn);
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
4948c2ecf20Sopenharmony_ci/* cirrus framebuffers & mode config				      */
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic struct drm_framebuffer*
4978c2ecf20Sopenharmony_cicirrus_fb_create(struct drm_device *dev, struct drm_file *file_priv,
4988c2ecf20Sopenharmony_ci		 const struct drm_mode_fb_cmd2 *mode_cmd)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	if (mode_cmd->pixel_format != DRM_FORMAT_RGB565 &&
5018c2ecf20Sopenharmony_ci	    mode_cmd->pixel_format != DRM_FORMAT_RGB888 &&
5028c2ecf20Sopenharmony_ci	    mode_cmd->pixel_format != DRM_FORMAT_XRGB8888)
5038c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
5048c2ecf20Sopenharmony_ci	if (cirrus_check_size(mode_cmd->width, mode_cmd->height, NULL) < 0)
5058c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
5068c2ecf20Sopenharmony_ci	return drm_gem_fb_create_with_dirty(dev, file_priv, mode_cmd);
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs cirrus_mode_config_funcs = {
5108c2ecf20Sopenharmony_ci	.fb_create = cirrus_fb_create,
5118c2ecf20Sopenharmony_ci	.atomic_check = drm_atomic_helper_check,
5128c2ecf20Sopenharmony_ci	.atomic_commit = drm_atomic_helper_commit,
5138c2ecf20Sopenharmony_ci};
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cistatic int cirrus_mode_config_init(struct cirrus_device *cirrus)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	struct drm_device *dev = &cirrus->dev;
5188c2ecf20Sopenharmony_ci	int ret;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	ret = drmm_mode_config_init(dev);
5218c2ecf20Sopenharmony_ci	if (ret)
5228c2ecf20Sopenharmony_ci		return ret;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	dev->mode_config.min_width = 0;
5258c2ecf20Sopenharmony_ci	dev->mode_config.min_height = 0;
5268c2ecf20Sopenharmony_ci	dev->mode_config.max_width = CIRRUS_MAX_PITCH / 2;
5278c2ecf20Sopenharmony_ci	dev->mode_config.max_height = 1024;
5288c2ecf20Sopenharmony_ci	dev->mode_config.preferred_depth = 16;
5298c2ecf20Sopenharmony_ci	dev->mode_config.prefer_shadow = 0;
5308c2ecf20Sopenharmony_ci	dev->mode_config.funcs = &cirrus_mode_config_funcs;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	return 0;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ciDEFINE_DRM_GEM_FOPS(cirrus_fops);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic struct drm_driver cirrus_driver = {
5408c2ecf20Sopenharmony_ci	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	.name		 = DRIVER_NAME,
5438c2ecf20Sopenharmony_ci	.desc		 = DRIVER_DESC,
5448c2ecf20Sopenharmony_ci	.date		 = DRIVER_DATE,
5458c2ecf20Sopenharmony_ci	.major		 = DRIVER_MAJOR,
5468c2ecf20Sopenharmony_ci	.minor		 = DRIVER_MINOR,
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	.fops		 = &cirrus_fops,
5498c2ecf20Sopenharmony_ci	DRM_GEM_SHMEM_DRIVER_OPS,
5508c2ecf20Sopenharmony_ci};
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_cistatic int cirrus_pci_probe(struct pci_dev *pdev,
5538c2ecf20Sopenharmony_ci			    const struct pci_device_id *ent)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	struct drm_device *dev;
5568c2ecf20Sopenharmony_ci	struct cirrus_device *cirrus;
5578c2ecf20Sopenharmony_ci	int ret;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, "cirrusdrmfb");
5608c2ecf20Sopenharmony_ci	if (ret)
5618c2ecf20Sopenharmony_ci		return ret;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	ret = pcim_enable_device(pdev);
5648c2ecf20Sopenharmony_ci	if (ret)
5658c2ecf20Sopenharmony_ci		return ret;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	ret = pci_request_regions(pdev, DRIVER_NAME);
5688c2ecf20Sopenharmony_ci	if (ret)
5698c2ecf20Sopenharmony_ci		return ret;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	ret = -ENOMEM;
5728c2ecf20Sopenharmony_ci	cirrus = devm_drm_dev_alloc(&pdev->dev, &cirrus_driver,
5738c2ecf20Sopenharmony_ci				    struct cirrus_device, dev);
5748c2ecf20Sopenharmony_ci	if (IS_ERR(cirrus))
5758c2ecf20Sopenharmony_ci		return PTR_ERR(cirrus);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	dev = &cirrus->dev;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	cirrus->vram = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0),
5808c2ecf20Sopenharmony_ci				    pci_resource_len(pdev, 0));
5818c2ecf20Sopenharmony_ci	if (cirrus->vram == NULL)
5828c2ecf20Sopenharmony_ci		return -ENOMEM;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	cirrus->mmio = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 1),
5858c2ecf20Sopenharmony_ci				    pci_resource_len(pdev, 1));
5868c2ecf20Sopenharmony_ci	if (cirrus->mmio == NULL)
5878c2ecf20Sopenharmony_ci		return -ENOMEM;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	ret = cirrus_mode_config_init(cirrus);
5908c2ecf20Sopenharmony_ci	if (ret)
5918c2ecf20Sopenharmony_ci		return ret;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	ret = cirrus_conn_init(cirrus);
5948c2ecf20Sopenharmony_ci	if (ret < 0)
5958c2ecf20Sopenharmony_ci		return ret;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	ret = cirrus_pipe_init(cirrus);
5988c2ecf20Sopenharmony_ci	if (ret < 0)
5998c2ecf20Sopenharmony_ci		return ret;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	drm_mode_config_reset(dev);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	dev->pdev = pdev;
6048c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, dev);
6058c2ecf20Sopenharmony_ci	ret = drm_dev_register(dev, 0);
6068c2ecf20Sopenharmony_ci	if (ret)
6078c2ecf20Sopenharmony_ci		return ret;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	drm_fbdev_generic_setup(dev, dev->mode_config.preferred_depth);
6108c2ecf20Sopenharmony_ci	return 0;
6118c2ecf20Sopenharmony_ci}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_cistatic void cirrus_pci_remove(struct pci_dev *pdev)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	struct drm_device *dev = pci_get_drvdata(pdev);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	drm_dev_unplug(dev);
6188c2ecf20Sopenharmony_ci	drm_atomic_helper_shutdown(dev);
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_cistatic const struct pci_device_id pciidlist[] = {
6228c2ecf20Sopenharmony_ci	{
6238c2ecf20Sopenharmony_ci		.vendor    = PCI_VENDOR_ID_CIRRUS,
6248c2ecf20Sopenharmony_ci		.device    = PCI_DEVICE_ID_CIRRUS_5446,
6258c2ecf20Sopenharmony_ci		/* only bind to the cirrus chip in qemu */
6268c2ecf20Sopenharmony_ci		.subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET,
6278c2ecf20Sopenharmony_ci		.subdevice = PCI_SUBDEVICE_ID_QEMU,
6288c2ecf20Sopenharmony_ci	}, {
6298c2ecf20Sopenharmony_ci		.vendor    = PCI_VENDOR_ID_CIRRUS,
6308c2ecf20Sopenharmony_ci		.device    = PCI_DEVICE_ID_CIRRUS_5446,
6318c2ecf20Sopenharmony_ci		.subvendor = PCI_VENDOR_ID_XEN,
6328c2ecf20Sopenharmony_ci		.subdevice = 0x0001,
6338c2ecf20Sopenharmony_ci	},
6348c2ecf20Sopenharmony_ci	{ /* end if list */ }
6358c2ecf20Sopenharmony_ci};
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_cistatic struct pci_driver cirrus_pci_driver = {
6388c2ecf20Sopenharmony_ci	.name = DRIVER_NAME,
6398c2ecf20Sopenharmony_ci	.id_table = pciidlist,
6408c2ecf20Sopenharmony_ci	.probe = cirrus_pci_probe,
6418c2ecf20Sopenharmony_ci	.remove = cirrus_pci_remove,
6428c2ecf20Sopenharmony_ci};
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic int __init cirrus_init(void)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	if (vgacon_text_force())
6478c2ecf20Sopenharmony_ci		return -EINVAL;
6488c2ecf20Sopenharmony_ci	return pci_register_driver(&cirrus_pci_driver);
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic void __exit cirrus_exit(void)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	pci_unregister_driver(&cirrus_pci_driver);
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_cimodule_init(cirrus_init);
6578c2ecf20Sopenharmony_cimodule_exit(cirrus_exit);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pciidlist);
6608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
661