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