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