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