15c3c9986SThomas Zimmermann /* SPDX-License-Identifier: GPL-2.0 */ 25c3c9986SThomas Zimmermann /* 35c3c9986SThomas Zimmermann * Copyright 2012-2019 Red Hat 45c3c9986SThomas Zimmermann * 55c3c9986SThomas Zimmermann * This file is subject to the terms and conditions of the GNU General 65c3c9986SThomas Zimmermann * Public License version 2. See the file COPYING in the main 75c3c9986SThomas Zimmermann * directory of this archive for more details. 85c3c9986SThomas Zimmermann * 95c3c9986SThomas Zimmermann * Authors: Matthew Garrett 105c3c9986SThomas Zimmermann * Dave Airlie 115c3c9986SThomas Zimmermann * Gerd Hoffmann 125c3c9986SThomas Zimmermann * 135c3c9986SThomas Zimmermann * Portions of this code derived from cirrusfb.c: 145c3c9986SThomas Zimmermann * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets 155c3c9986SThomas Zimmermann * 165c3c9986SThomas Zimmermann * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com> 175c3c9986SThomas Zimmermann */ 185c3c9986SThomas Zimmermann 195c3c9986SThomas Zimmermann #include <linux/aperture.h> 205c3c9986SThomas Zimmermann #include <linux/iosys-map.h> 215c3c9986SThomas Zimmermann #include <linux/module.h> 225c3c9986SThomas Zimmermann #include <linux/pci.h> 235c3c9986SThomas Zimmermann 245c3c9986SThomas Zimmermann #include <video/cirrus.h> 255c3c9986SThomas Zimmermann #include <video/vga.h> 265c3c9986SThomas Zimmermann 275c3c9986SThomas Zimmermann #include <drm/clients/drm_client_setup.h> 285c3c9986SThomas Zimmermann #include <drm/drm_atomic.h> 295c3c9986SThomas Zimmermann #include <drm/drm_atomic_helper.h> 305c3c9986SThomas Zimmermann #include <drm/drm_atomic_state_helper.h> 315c3c9986SThomas Zimmermann #include <drm/drm_connector.h> 325c3c9986SThomas Zimmermann #include <drm/drm_damage_helper.h> 335c3c9986SThomas Zimmermann #include <drm/drm_drv.h> 345c3c9986SThomas Zimmermann #include <drm/drm_edid.h> 355c3c9986SThomas Zimmermann #include <drm/drm_fbdev_shmem.h> 365c3c9986SThomas Zimmermann #include <drm/drm_file.h> 375c3c9986SThomas Zimmermann #include <drm/drm_format_helper.h> 385c3c9986SThomas Zimmermann #include <drm/drm_fourcc.h> 395c3c9986SThomas Zimmermann #include <drm/drm_framebuffer.h> 405c3c9986SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h> 415c3c9986SThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h> 425c3c9986SThomas Zimmermann #include <drm/drm_gem_shmem_helper.h> 435c3c9986SThomas Zimmermann #include <drm/drm_ioctl.h> 445c3c9986SThomas Zimmermann #include <drm/drm_managed.h> 455c3c9986SThomas Zimmermann #include <drm/drm_modeset_helper_vtables.h> 465c3c9986SThomas Zimmermann #include <drm/drm_module.h> 475c3c9986SThomas Zimmermann #include <drm/drm_probe_helper.h> 485c3c9986SThomas Zimmermann 495c3c9986SThomas Zimmermann #define DRIVER_NAME "cirrus-qemu" 505c3c9986SThomas Zimmermann #define DRIVER_DESC "qemu cirrus vga" 515c3c9986SThomas Zimmermann #define DRIVER_DATE "2019" 525c3c9986SThomas Zimmermann #define DRIVER_MAJOR 2 535c3c9986SThomas Zimmermann #define DRIVER_MINOR 0 545c3c9986SThomas Zimmermann 555c3c9986SThomas Zimmermann #define CIRRUS_MAX_PITCH (0x1FF << 3) /* (4096 - 1) & ~111b bytes */ 565c3c9986SThomas Zimmermann #define CIRRUS_VRAM_SIZE (4 * 1024 * 1024) /* 4 MB */ 575c3c9986SThomas Zimmermann 585c3c9986SThomas Zimmermann struct cirrus_device { 595c3c9986SThomas Zimmermann struct drm_device dev; 605c3c9986SThomas Zimmermann 615c3c9986SThomas Zimmermann /* modesetting pipeline */ 625c3c9986SThomas Zimmermann struct drm_plane primary_plane; 635c3c9986SThomas Zimmermann struct drm_crtc crtc; 645c3c9986SThomas Zimmermann struct drm_encoder encoder; 655c3c9986SThomas Zimmermann struct drm_connector connector; 665c3c9986SThomas Zimmermann 675c3c9986SThomas Zimmermann /* HW resources */ 685c3c9986SThomas Zimmermann void __iomem *vram; 695c3c9986SThomas Zimmermann void __iomem *mmio; 705c3c9986SThomas Zimmermann }; 715c3c9986SThomas Zimmermann 725c3c9986SThomas Zimmermann #define to_cirrus(_dev) container_of(_dev, struct cirrus_device, dev) 735c3c9986SThomas Zimmermann 745c3c9986SThomas Zimmermann struct cirrus_primary_plane_state { 755c3c9986SThomas Zimmermann struct drm_shadow_plane_state base; 765c3c9986SThomas Zimmermann 775c3c9986SThomas Zimmermann /* HW scanout buffer */ 785c3c9986SThomas Zimmermann const struct drm_format_info *format; 795c3c9986SThomas Zimmermann unsigned int pitch; 805c3c9986SThomas Zimmermann }; 815c3c9986SThomas Zimmermann 825c3c9986SThomas Zimmermann static inline struct cirrus_primary_plane_state * 835c3c9986SThomas Zimmermann to_cirrus_primary_plane_state(struct drm_plane_state *plane_state) 845c3c9986SThomas Zimmermann { 855c3c9986SThomas Zimmermann return container_of(plane_state, struct cirrus_primary_plane_state, base.base); 865c3c9986SThomas Zimmermann }; 875c3c9986SThomas Zimmermann 885c3c9986SThomas Zimmermann /* ------------------------------------------------------------------ */ 895c3c9986SThomas Zimmermann /* 905c3c9986SThomas Zimmermann * The meat of this driver. The core passes us a mode and we have to program 915c3c9986SThomas Zimmermann * it. The modesetting here is the bare minimum required to satisfy the qemu 925c3c9986SThomas Zimmermann * emulation of this hardware, and running this against a real device is 935c3c9986SThomas Zimmermann * likely to result in an inadequately programmed mode. We've already had 945c3c9986SThomas Zimmermann * the opportunity to modify the mode, so whatever we receive here should 955c3c9986SThomas Zimmermann * be something that can be correctly programmed and displayed 965c3c9986SThomas Zimmermann */ 975c3c9986SThomas Zimmermann 985c3c9986SThomas Zimmermann #define SEQ_INDEX 4 995c3c9986SThomas Zimmermann #define SEQ_DATA 5 1005c3c9986SThomas Zimmermann 1015c3c9986SThomas Zimmermann static u8 rreg_seq(struct cirrus_device *cirrus, u8 reg) 1025c3c9986SThomas Zimmermann { 1035c3c9986SThomas Zimmermann iowrite8(reg, cirrus->mmio + SEQ_INDEX); 1045c3c9986SThomas Zimmermann return ioread8(cirrus->mmio + SEQ_DATA); 1055c3c9986SThomas Zimmermann } 1065c3c9986SThomas Zimmermann 1075c3c9986SThomas Zimmermann static void wreg_seq(struct cirrus_device *cirrus, u8 reg, u8 val) 1085c3c9986SThomas Zimmermann { 1095c3c9986SThomas Zimmermann iowrite8(reg, cirrus->mmio + SEQ_INDEX); 1105c3c9986SThomas Zimmermann iowrite8(val, cirrus->mmio + SEQ_DATA); 1115c3c9986SThomas Zimmermann } 1125c3c9986SThomas Zimmermann 1135c3c9986SThomas Zimmermann #define CRT_INDEX 0x14 1145c3c9986SThomas Zimmermann #define CRT_DATA 0x15 1155c3c9986SThomas Zimmermann 1165c3c9986SThomas Zimmermann static u8 rreg_crt(struct cirrus_device *cirrus, u8 reg) 1175c3c9986SThomas Zimmermann { 1185c3c9986SThomas Zimmermann iowrite8(reg, cirrus->mmio + CRT_INDEX); 1195c3c9986SThomas Zimmermann return ioread8(cirrus->mmio + CRT_DATA); 1205c3c9986SThomas Zimmermann } 1215c3c9986SThomas Zimmermann 1225c3c9986SThomas Zimmermann static void wreg_crt(struct cirrus_device *cirrus, u8 reg, u8 val) 1235c3c9986SThomas Zimmermann { 1245c3c9986SThomas Zimmermann iowrite8(reg, cirrus->mmio + CRT_INDEX); 1255c3c9986SThomas Zimmermann iowrite8(val, cirrus->mmio + CRT_DATA); 1265c3c9986SThomas Zimmermann } 1275c3c9986SThomas Zimmermann 1285c3c9986SThomas Zimmermann #define GFX_INDEX 0xe 1295c3c9986SThomas Zimmermann #define GFX_DATA 0xf 1305c3c9986SThomas Zimmermann 1315c3c9986SThomas Zimmermann static void wreg_gfx(struct cirrus_device *cirrus, u8 reg, u8 val) 1325c3c9986SThomas Zimmermann { 1335c3c9986SThomas Zimmermann iowrite8(reg, cirrus->mmio + GFX_INDEX); 1345c3c9986SThomas Zimmermann iowrite8(val, cirrus->mmio + GFX_DATA); 1355c3c9986SThomas Zimmermann } 1365c3c9986SThomas Zimmermann 1375c3c9986SThomas Zimmermann #define VGA_DAC_MASK 0x06 1385c3c9986SThomas Zimmermann 1395c3c9986SThomas Zimmermann static void wreg_hdr(struct cirrus_device *cirrus, u8 val) 1405c3c9986SThomas Zimmermann { 1415c3c9986SThomas Zimmermann ioread8(cirrus->mmio + VGA_DAC_MASK); 1425c3c9986SThomas Zimmermann ioread8(cirrus->mmio + VGA_DAC_MASK); 1435c3c9986SThomas Zimmermann ioread8(cirrus->mmio + VGA_DAC_MASK); 1445c3c9986SThomas Zimmermann ioread8(cirrus->mmio + VGA_DAC_MASK); 1455c3c9986SThomas Zimmermann iowrite8(val, cirrus->mmio + VGA_DAC_MASK); 1465c3c9986SThomas Zimmermann } 1475c3c9986SThomas Zimmermann 1485c3c9986SThomas Zimmermann static const struct drm_format_info *cirrus_convert_to(struct drm_framebuffer *fb) 1495c3c9986SThomas Zimmermann { 1505c3c9986SThomas Zimmermann if (fb->format->format == DRM_FORMAT_XRGB8888 && fb->pitches[0] > CIRRUS_MAX_PITCH) { 1515c3c9986SThomas Zimmermann if (fb->width * 3 <= CIRRUS_MAX_PITCH) 1525c3c9986SThomas Zimmermann /* convert from XR24 to RG24 */ 1535c3c9986SThomas Zimmermann return drm_format_info(DRM_FORMAT_RGB888); 1545c3c9986SThomas Zimmermann else 1555c3c9986SThomas Zimmermann /* convert from XR24 to RG16 */ 1565c3c9986SThomas Zimmermann return drm_format_info(DRM_FORMAT_RGB565); 1575c3c9986SThomas Zimmermann } 1585c3c9986SThomas Zimmermann return NULL; 1595c3c9986SThomas Zimmermann } 1605c3c9986SThomas Zimmermann 1615c3c9986SThomas Zimmermann static const struct drm_format_info *cirrus_format(struct drm_framebuffer *fb) 1625c3c9986SThomas Zimmermann { 1635c3c9986SThomas Zimmermann const struct drm_format_info *format = cirrus_convert_to(fb); 1645c3c9986SThomas Zimmermann 1655c3c9986SThomas Zimmermann if (format) 1665c3c9986SThomas Zimmermann return format; 1675c3c9986SThomas Zimmermann return fb->format; 1685c3c9986SThomas Zimmermann } 1695c3c9986SThomas Zimmermann 1705c3c9986SThomas Zimmermann static int cirrus_pitch(struct drm_framebuffer *fb) 1715c3c9986SThomas Zimmermann { 1725c3c9986SThomas Zimmermann const struct drm_format_info *format = cirrus_convert_to(fb); 1735c3c9986SThomas Zimmermann 1745c3c9986SThomas Zimmermann if (format) 1755c3c9986SThomas Zimmermann return drm_format_info_min_pitch(format, 0, fb->width); 1765c3c9986SThomas Zimmermann return fb->pitches[0]; 1775c3c9986SThomas Zimmermann } 1785c3c9986SThomas Zimmermann 1795c3c9986SThomas Zimmermann static void cirrus_set_start_address(struct cirrus_device *cirrus, u32 offset) 1805c3c9986SThomas Zimmermann { 1815c3c9986SThomas Zimmermann u32 addr; 1825c3c9986SThomas Zimmermann u8 tmp; 1835c3c9986SThomas Zimmermann 1845c3c9986SThomas Zimmermann addr = offset >> 2; 1855c3c9986SThomas Zimmermann wreg_crt(cirrus, 0x0c, (u8)((addr >> 8) & 0xff)); 1865c3c9986SThomas Zimmermann wreg_crt(cirrus, 0x0d, (u8)(addr & 0xff)); 1875c3c9986SThomas Zimmermann 1885c3c9986SThomas Zimmermann tmp = rreg_crt(cirrus, 0x1b); 1895c3c9986SThomas Zimmermann tmp &= 0xf2; 1905c3c9986SThomas Zimmermann tmp |= (addr >> 16) & 0x01; 1915c3c9986SThomas Zimmermann tmp |= (addr >> 15) & 0x0c; 1925c3c9986SThomas Zimmermann wreg_crt(cirrus, 0x1b, tmp); 1935c3c9986SThomas Zimmermann 1945c3c9986SThomas Zimmermann tmp = rreg_crt(cirrus, 0x1d); 1955c3c9986SThomas Zimmermann tmp &= 0x7f; 1965c3c9986SThomas Zimmermann tmp |= (addr >> 12) & 0x80; 1975c3c9986SThomas Zimmermann wreg_crt(cirrus, 0x1d, tmp); 1985c3c9986SThomas Zimmermann } 1995c3c9986SThomas Zimmermann 2005c3c9986SThomas Zimmermann static void cirrus_mode_set(struct cirrus_device *cirrus, 2015c3c9986SThomas Zimmermann struct drm_display_mode *mode) 2025c3c9986SThomas Zimmermann { 2035c3c9986SThomas Zimmermann int hsyncstart, hsyncend, htotal, hdispend; 2045c3c9986SThomas Zimmermann int vtotal, vdispend; 2055c3c9986SThomas Zimmermann int tmp; 2065c3c9986SThomas Zimmermann 2075c3c9986SThomas Zimmermann htotal = mode->htotal / 8; 2085c3c9986SThomas Zimmermann hsyncend = mode->hsync_end / 8; 2095c3c9986SThomas Zimmermann hsyncstart = mode->hsync_start / 8; 2105c3c9986SThomas Zimmermann hdispend = mode->hdisplay / 8; 2115c3c9986SThomas Zimmermann 2125c3c9986SThomas Zimmermann vtotal = mode->vtotal; 2135c3c9986SThomas Zimmermann vdispend = mode->vdisplay; 2145c3c9986SThomas Zimmermann 2155c3c9986SThomas Zimmermann vdispend -= 1; 2165c3c9986SThomas Zimmermann vtotal -= 2; 2175c3c9986SThomas Zimmermann 2185c3c9986SThomas Zimmermann htotal -= 5; 2195c3c9986SThomas Zimmermann hdispend -= 1; 2205c3c9986SThomas Zimmermann hsyncstart += 1; 2215c3c9986SThomas Zimmermann hsyncend += 1; 2225c3c9986SThomas Zimmermann 2235c3c9986SThomas Zimmermann wreg_crt(cirrus, VGA_CRTC_V_SYNC_END, 0x20); 2245c3c9986SThomas Zimmermann wreg_crt(cirrus, VGA_CRTC_H_TOTAL, htotal); 2255c3c9986SThomas Zimmermann wreg_crt(cirrus, VGA_CRTC_H_DISP, hdispend); 2265c3c9986SThomas Zimmermann wreg_crt(cirrus, VGA_CRTC_H_SYNC_START, hsyncstart); 2275c3c9986SThomas Zimmermann wreg_crt(cirrus, VGA_CRTC_H_SYNC_END, hsyncend); 2285c3c9986SThomas Zimmermann wreg_crt(cirrus, VGA_CRTC_V_TOTAL, vtotal & 0xff); 2295c3c9986SThomas Zimmermann wreg_crt(cirrus, VGA_CRTC_V_DISP_END, vdispend & 0xff); 2305c3c9986SThomas Zimmermann 2315c3c9986SThomas Zimmermann tmp = 0x40; 2325c3c9986SThomas Zimmermann if ((vdispend + 1) & 512) 2335c3c9986SThomas Zimmermann tmp |= 0x20; 2345c3c9986SThomas Zimmermann wreg_crt(cirrus, VGA_CRTC_MAX_SCAN, tmp); 2355c3c9986SThomas Zimmermann 2365c3c9986SThomas Zimmermann /* 2375c3c9986SThomas Zimmermann * Overflow bits for values that don't fit in the standard registers 2385c3c9986SThomas Zimmermann */ 2395c3c9986SThomas Zimmermann tmp = 0x10; 2405c3c9986SThomas Zimmermann if (vtotal & 0x100) 2415c3c9986SThomas Zimmermann tmp |= 0x01; 2425c3c9986SThomas Zimmermann if (vdispend & 0x100) 2435c3c9986SThomas Zimmermann tmp |= 0x02; 2445c3c9986SThomas Zimmermann if ((vdispend + 1) & 0x100) 2455c3c9986SThomas Zimmermann tmp |= 0x08; 2465c3c9986SThomas Zimmermann if (vtotal & 0x200) 2475c3c9986SThomas Zimmermann tmp |= 0x20; 2485c3c9986SThomas Zimmermann if (vdispend & 0x200) 2495c3c9986SThomas Zimmermann tmp |= 0x40; 2505c3c9986SThomas Zimmermann wreg_crt(cirrus, VGA_CRTC_OVERFLOW, tmp); 2515c3c9986SThomas Zimmermann 2525c3c9986SThomas Zimmermann tmp = 0; 2535c3c9986SThomas Zimmermann 2545c3c9986SThomas Zimmermann /* More overflow bits */ 2555c3c9986SThomas Zimmermann 2565c3c9986SThomas Zimmermann if ((htotal + 5) & 0x40) 2575c3c9986SThomas Zimmermann tmp |= 0x10; 2585c3c9986SThomas Zimmermann if ((htotal + 5) & 0x80) 2595c3c9986SThomas Zimmermann tmp |= 0x20; 2605c3c9986SThomas Zimmermann if (vtotal & 0x100) 2615c3c9986SThomas Zimmermann tmp |= 0x40; 2625c3c9986SThomas Zimmermann if (vtotal & 0x200) 2635c3c9986SThomas Zimmermann tmp |= 0x80; 2645c3c9986SThomas Zimmermann 2655c3c9986SThomas Zimmermann wreg_crt(cirrus, CL_CRT1A, tmp); 2665c3c9986SThomas Zimmermann 2675c3c9986SThomas Zimmermann /* Disable Hercules/CGA compatibility */ 2685c3c9986SThomas Zimmermann wreg_crt(cirrus, VGA_CRTC_MODE, 0x03); 2695c3c9986SThomas Zimmermann } 2705c3c9986SThomas Zimmermann 2715c3c9986SThomas Zimmermann static void cirrus_format_set(struct cirrus_device *cirrus, 2725c3c9986SThomas Zimmermann const struct drm_format_info *format) 2735c3c9986SThomas Zimmermann { 2745c3c9986SThomas Zimmermann u8 sr07, hdr; 2755c3c9986SThomas Zimmermann 2765c3c9986SThomas Zimmermann sr07 = rreg_seq(cirrus, 0x07); 2775c3c9986SThomas Zimmermann sr07 &= 0xe0; 2785c3c9986SThomas Zimmermann 2795c3c9986SThomas Zimmermann switch (format->format) { 2805c3c9986SThomas Zimmermann case DRM_FORMAT_C8: 2815c3c9986SThomas Zimmermann sr07 |= 0x11; 2825c3c9986SThomas Zimmermann hdr = 0x00; 2835c3c9986SThomas Zimmermann break; 2845c3c9986SThomas Zimmermann case DRM_FORMAT_RGB565: 2855c3c9986SThomas Zimmermann sr07 |= 0x17; 2865c3c9986SThomas Zimmermann hdr = 0xc1; 2875c3c9986SThomas Zimmermann break; 2885c3c9986SThomas Zimmermann case DRM_FORMAT_RGB888: 2895c3c9986SThomas Zimmermann sr07 |= 0x15; 2905c3c9986SThomas Zimmermann hdr = 0xc5; 2915c3c9986SThomas Zimmermann break; 2925c3c9986SThomas Zimmermann case DRM_FORMAT_XRGB8888: 2935c3c9986SThomas Zimmermann sr07 |= 0x19; 2945c3c9986SThomas Zimmermann hdr = 0xc5; 2955c3c9986SThomas Zimmermann break; 2965c3c9986SThomas Zimmermann default: 2975c3c9986SThomas Zimmermann return; 2985c3c9986SThomas Zimmermann } 2995c3c9986SThomas Zimmermann 3005c3c9986SThomas Zimmermann wreg_seq(cirrus, 0x7, sr07); 3015c3c9986SThomas Zimmermann 3025c3c9986SThomas Zimmermann /* Enable high-colour modes */ 3035c3c9986SThomas Zimmermann wreg_gfx(cirrus, VGA_GFX_MODE, 0x40); 3045c3c9986SThomas Zimmermann 3055c3c9986SThomas Zimmermann /* And set graphics mode */ 3065c3c9986SThomas Zimmermann wreg_gfx(cirrus, VGA_GFX_MISC, 0x01); 3075c3c9986SThomas Zimmermann 3085c3c9986SThomas Zimmermann wreg_hdr(cirrus, hdr); 3095c3c9986SThomas Zimmermann } 3105c3c9986SThomas Zimmermann 3115c3c9986SThomas Zimmermann static void cirrus_pitch_set(struct cirrus_device *cirrus, unsigned int pitch) 3125c3c9986SThomas Zimmermann { 3135c3c9986SThomas Zimmermann u8 cr13, cr1b; 3145c3c9986SThomas Zimmermann 3155c3c9986SThomas Zimmermann /* Program the pitch */ 3165c3c9986SThomas Zimmermann cr13 = pitch / 8; 3175c3c9986SThomas Zimmermann wreg_crt(cirrus, VGA_CRTC_OFFSET, cr13); 3185c3c9986SThomas Zimmermann 3195c3c9986SThomas Zimmermann /* Enable extended blanking and pitch bits, and enable full memory */ 3205c3c9986SThomas Zimmermann cr1b = 0x22; 3215c3c9986SThomas Zimmermann cr1b |= (pitch >> 7) & 0x10; 3225c3c9986SThomas Zimmermann cr1b |= (pitch >> 6) & 0x40; 3235c3c9986SThomas Zimmermann wreg_crt(cirrus, 0x1b, cr1b); 3245c3c9986SThomas Zimmermann 3255c3c9986SThomas Zimmermann cirrus_set_start_address(cirrus, 0); 3265c3c9986SThomas Zimmermann } 3275c3c9986SThomas Zimmermann 3285c3c9986SThomas Zimmermann /* ------------------------------------------------------------------ */ 3295c3c9986SThomas Zimmermann /* cirrus display pipe */ 3305c3c9986SThomas Zimmermann 3315c3c9986SThomas Zimmermann static const uint32_t cirrus_primary_plane_formats[] = { 3325c3c9986SThomas Zimmermann DRM_FORMAT_RGB565, 3335c3c9986SThomas Zimmermann DRM_FORMAT_RGB888, 3345c3c9986SThomas Zimmermann DRM_FORMAT_XRGB8888, 3355c3c9986SThomas Zimmermann }; 3365c3c9986SThomas Zimmermann 3375c3c9986SThomas Zimmermann static const uint64_t cirrus_primary_plane_format_modifiers[] = { 3385c3c9986SThomas Zimmermann DRM_FORMAT_MOD_LINEAR, 3395c3c9986SThomas Zimmermann DRM_FORMAT_MOD_INVALID 3405c3c9986SThomas Zimmermann }; 3415c3c9986SThomas Zimmermann 3425c3c9986SThomas Zimmermann static int cirrus_primary_plane_helper_atomic_check(struct drm_plane *plane, 3435c3c9986SThomas Zimmermann struct drm_atomic_state *state) 3445c3c9986SThomas Zimmermann { 3455c3c9986SThomas Zimmermann struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); 3465c3c9986SThomas Zimmermann struct cirrus_primary_plane_state *new_primary_plane_state = 3475c3c9986SThomas Zimmermann to_cirrus_primary_plane_state(new_plane_state); 3485c3c9986SThomas Zimmermann struct drm_framebuffer *fb = new_plane_state->fb; 3495c3c9986SThomas Zimmermann struct drm_crtc *new_crtc = new_plane_state->crtc; 3505c3c9986SThomas Zimmermann struct drm_crtc_state *new_crtc_state = NULL; 3515c3c9986SThomas Zimmermann int ret; 3525c3c9986SThomas Zimmermann unsigned int pitch; 3535c3c9986SThomas Zimmermann 3545c3c9986SThomas Zimmermann if (new_crtc) 3555c3c9986SThomas Zimmermann new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); 3565c3c9986SThomas Zimmermann 3575c3c9986SThomas Zimmermann ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, 3585c3c9986SThomas Zimmermann DRM_PLANE_NO_SCALING, 3595c3c9986SThomas Zimmermann DRM_PLANE_NO_SCALING, 3605c3c9986SThomas Zimmermann false, false); 3615c3c9986SThomas Zimmermann if (ret) 3625c3c9986SThomas Zimmermann return ret; 3635c3c9986SThomas Zimmermann else if (!new_plane_state->visible) 3645c3c9986SThomas Zimmermann return 0; 3655c3c9986SThomas Zimmermann 3665c3c9986SThomas Zimmermann pitch = cirrus_pitch(fb); 3675c3c9986SThomas Zimmermann 3685c3c9986SThomas Zimmermann /* validate size constraints */ 3695c3c9986SThomas Zimmermann if (pitch > CIRRUS_MAX_PITCH) 3705c3c9986SThomas Zimmermann return -EINVAL; 3715c3c9986SThomas Zimmermann else if (pitch * fb->height > CIRRUS_VRAM_SIZE) 3725c3c9986SThomas Zimmermann return -EINVAL; 3735c3c9986SThomas Zimmermann 3745c3c9986SThomas Zimmermann new_primary_plane_state->format = cirrus_format(fb); 3755c3c9986SThomas Zimmermann new_primary_plane_state->pitch = pitch; 3765c3c9986SThomas Zimmermann 3775c3c9986SThomas Zimmermann return 0; 3785c3c9986SThomas Zimmermann } 3795c3c9986SThomas Zimmermann 3805c3c9986SThomas Zimmermann static void cirrus_primary_plane_helper_atomic_update(struct drm_plane *plane, 3815c3c9986SThomas Zimmermann struct drm_atomic_state *state) 3825c3c9986SThomas Zimmermann { 3835c3c9986SThomas Zimmermann struct cirrus_device *cirrus = to_cirrus(plane->dev); 3845c3c9986SThomas Zimmermann struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 3855c3c9986SThomas Zimmermann struct cirrus_primary_plane_state *primary_plane_state = 3865c3c9986SThomas Zimmermann to_cirrus_primary_plane_state(plane_state); 3875c3c9986SThomas Zimmermann struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 3885c3c9986SThomas Zimmermann struct drm_framebuffer *fb = plane_state->fb; 3895c3c9986SThomas Zimmermann const struct drm_format_info *format = primary_plane_state->format; 3905c3c9986SThomas Zimmermann unsigned int pitch = primary_plane_state->pitch; 3915c3c9986SThomas Zimmermann struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 3925c3c9986SThomas Zimmermann struct cirrus_primary_plane_state *old_primary_plane_state = 3935c3c9986SThomas Zimmermann to_cirrus_primary_plane_state(old_plane_state); 3945c3c9986SThomas Zimmermann struct iosys_map vaddr = IOSYS_MAP_INIT_VADDR_IOMEM(cirrus->vram); 3955c3c9986SThomas Zimmermann struct drm_atomic_helper_damage_iter iter; 3965c3c9986SThomas Zimmermann struct drm_rect damage; 3975c3c9986SThomas Zimmermann int idx; 3985c3c9986SThomas Zimmermann 3995c3c9986SThomas Zimmermann if (!fb) 4005c3c9986SThomas Zimmermann return; 4015c3c9986SThomas Zimmermann 4025c3c9986SThomas Zimmermann if (!drm_dev_enter(&cirrus->dev, &idx)) 4035c3c9986SThomas Zimmermann return; 4045c3c9986SThomas Zimmermann 4055c3c9986SThomas Zimmermann if (old_primary_plane_state->format != format) 4065c3c9986SThomas Zimmermann cirrus_format_set(cirrus, format); 4075c3c9986SThomas Zimmermann if (old_primary_plane_state->pitch != pitch) 4085c3c9986SThomas Zimmermann cirrus_pitch_set(cirrus, pitch); 4095c3c9986SThomas Zimmermann 4105c3c9986SThomas Zimmermann drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 4115c3c9986SThomas Zimmermann drm_atomic_for_each_plane_damage(&iter, &damage) { 4125c3c9986SThomas Zimmermann unsigned int offset = drm_fb_clip_offset(pitch, format, &damage); 4135c3c9986SThomas Zimmermann struct iosys_map dst = IOSYS_MAP_INIT_OFFSET(&vaddr, offset); 4145c3c9986SThomas Zimmermann 4155c3c9986SThomas Zimmermann drm_fb_blit(&dst, &pitch, format->format, shadow_plane_state->data, fb, 4165c3c9986SThomas Zimmermann &damage, &shadow_plane_state->fmtcnv_state); 4175c3c9986SThomas Zimmermann } 4185c3c9986SThomas Zimmermann 4195c3c9986SThomas Zimmermann drm_dev_exit(idx); 4205c3c9986SThomas Zimmermann } 4215c3c9986SThomas Zimmermann 4225c3c9986SThomas Zimmermann static const struct drm_plane_helper_funcs cirrus_primary_plane_helper_funcs = { 4235c3c9986SThomas Zimmermann DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, 4245c3c9986SThomas Zimmermann .atomic_check = cirrus_primary_plane_helper_atomic_check, 4255c3c9986SThomas Zimmermann .atomic_update = cirrus_primary_plane_helper_atomic_update, 4265c3c9986SThomas Zimmermann }; 4275c3c9986SThomas Zimmermann 4285c3c9986SThomas Zimmermann static struct drm_plane_state * 4295c3c9986SThomas Zimmermann cirrus_primary_plane_atomic_duplicate_state(struct drm_plane *plane) 4305c3c9986SThomas Zimmermann { 4315c3c9986SThomas Zimmermann struct drm_plane_state *plane_state = plane->state; 4325c3c9986SThomas Zimmermann struct cirrus_primary_plane_state *primary_plane_state = 4335c3c9986SThomas Zimmermann to_cirrus_primary_plane_state(plane_state); 4345c3c9986SThomas Zimmermann struct cirrus_primary_plane_state *new_primary_plane_state; 4355c3c9986SThomas Zimmermann struct drm_shadow_plane_state *new_shadow_plane_state; 4365c3c9986SThomas Zimmermann 4375c3c9986SThomas Zimmermann if (!plane_state) 4385c3c9986SThomas Zimmermann return NULL; 4395c3c9986SThomas Zimmermann 4405c3c9986SThomas Zimmermann new_primary_plane_state = kzalloc(sizeof(*new_primary_plane_state), GFP_KERNEL); 4415c3c9986SThomas Zimmermann if (!new_primary_plane_state) 4425c3c9986SThomas Zimmermann return NULL; 4435c3c9986SThomas Zimmermann new_shadow_plane_state = &new_primary_plane_state->base; 4445c3c9986SThomas Zimmermann 4455c3c9986SThomas Zimmermann __drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state); 4465c3c9986SThomas Zimmermann new_primary_plane_state->format = primary_plane_state->format; 4475c3c9986SThomas Zimmermann new_primary_plane_state->pitch = primary_plane_state->pitch; 4485c3c9986SThomas Zimmermann 4495c3c9986SThomas Zimmermann return &new_shadow_plane_state->base; 4505c3c9986SThomas Zimmermann } 4515c3c9986SThomas Zimmermann 4525c3c9986SThomas Zimmermann static void cirrus_primary_plane_atomic_destroy_state(struct drm_plane *plane, 4535c3c9986SThomas Zimmermann struct drm_plane_state *plane_state) 4545c3c9986SThomas Zimmermann { 4555c3c9986SThomas Zimmermann struct cirrus_primary_plane_state *primary_plane_state = 4565c3c9986SThomas Zimmermann to_cirrus_primary_plane_state(plane_state); 4575c3c9986SThomas Zimmermann 4585c3c9986SThomas Zimmermann __drm_gem_destroy_shadow_plane_state(&primary_plane_state->base); 4595c3c9986SThomas Zimmermann kfree(primary_plane_state); 4605c3c9986SThomas Zimmermann } 4615c3c9986SThomas Zimmermann 4625c3c9986SThomas Zimmermann static void cirrus_reset_primary_plane(struct drm_plane *plane) 4635c3c9986SThomas Zimmermann { 4645c3c9986SThomas Zimmermann struct cirrus_primary_plane_state *primary_plane_state; 4655c3c9986SThomas Zimmermann 4665c3c9986SThomas Zimmermann if (plane->state) { 4675c3c9986SThomas Zimmermann cirrus_primary_plane_atomic_destroy_state(plane, plane->state); 4685c3c9986SThomas Zimmermann plane->state = NULL; /* must be set to NULL here */ 4695c3c9986SThomas Zimmermann } 4705c3c9986SThomas Zimmermann 4715c3c9986SThomas Zimmermann primary_plane_state = kzalloc(sizeof(*primary_plane_state), GFP_KERNEL); 4725c3c9986SThomas Zimmermann if (!primary_plane_state) 4735c3c9986SThomas Zimmermann return; 4745c3c9986SThomas Zimmermann __drm_gem_reset_shadow_plane(plane, &primary_plane_state->base); 4755c3c9986SThomas Zimmermann } 4765c3c9986SThomas Zimmermann 4775c3c9986SThomas Zimmermann static const struct drm_plane_funcs cirrus_primary_plane_funcs = { 4785c3c9986SThomas Zimmermann .update_plane = drm_atomic_helper_update_plane, 4795c3c9986SThomas Zimmermann .disable_plane = drm_atomic_helper_disable_plane, 4805c3c9986SThomas Zimmermann .destroy = drm_plane_cleanup, 4815c3c9986SThomas Zimmermann .reset = cirrus_reset_primary_plane, 4825c3c9986SThomas Zimmermann .atomic_duplicate_state = cirrus_primary_plane_atomic_duplicate_state, 4835c3c9986SThomas Zimmermann .atomic_destroy_state = cirrus_primary_plane_atomic_destroy_state, 4845c3c9986SThomas Zimmermann }; 4855c3c9986SThomas Zimmermann 4865c3c9986SThomas Zimmermann static int cirrus_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) 4875c3c9986SThomas Zimmermann { 4885c3c9986SThomas Zimmermann struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 4895c3c9986SThomas Zimmermann int ret; 4905c3c9986SThomas Zimmermann 4915c3c9986SThomas Zimmermann if (!crtc_state->enable) 4925c3c9986SThomas Zimmermann return 0; 4935c3c9986SThomas Zimmermann 4945c3c9986SThomas Zimmermann ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state); 4955c3c9986SThomas Zimmermann if (ret) 4965c3c9986SThomas Zimmermann return ret; 4975c3c9986SThomas Zimmermann 4985c3c9986SThomas Zimmermann return 0; 4995c3c9986SThomas Zimmermann } 5005c3c9986SThomas Zimmermann 5015c3c9986SThomas Zimmermann static void cirrus_crtc_helper_atomic_enable(struct drm_crtc *crtc, 5025c3c9986SThomas Zimmermann struct drm_atomic_state *state) 5035c3c9986SThomas Zimmermann { 5045c3c9986SThomas Zimmermann struct cirrus_device *cirrus = to_cirrus(crtc->dev); 5055c3c9986SThomas Zimmermann struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 5065c3c9986SThomas Zimmermann int idx; 5075c3c9986SThomas Zimmermann 5085c3c9986SThomas Zimmermann if (!drm_dev_enter(&cirrus->dev, &idx)) 5095c3c9986SThomas Zimmermann return; 5105c3c9986SThomas Zimmermann 5115c3c9986SThomas Zimmermann cirrus_mode_set(cirrus, &crtc_state->mode); 5125c3c9986SThomas Zimmermann 513*3aba2ebaSMaxime Ripard #ifdef CONFIG_HAS_IOPORT 5145c3c9986SThomas Zimmermann /* Unblank (needed on S3 resume, vgabios doesn't do it then) */ 5155c3c9986SThomas Zimmermann outb(VGA_AR_ENABLE_DISPLAY, VGA_ATT_W); 516*3aba2ebaSMaxime Ripard #endif 5175c3c9986SThomas Zimmermann 5185c3c9986SThomas Zimmermann drm_dev_exit(idx); 5195c3c9986SThomas Zimmermann } 5205c3c9986SThomas Zimmermann 5215c3c9986SThomas Zimmermann static const struct drm_crtc_helper_funcs cirrus_crtc_helper_funcs = { 5225c3c9986SThomas Zimmermann .atomic_check = cirrus_crtc_helper_atomic_check, 5235c3c9986SThomas Zimmermann .atomic_enable = cirrus_crtc_helper_atomic_enable, 5245c3c9986SThomas Zimmermann }; 5255c3c9986SThomas Zimmermann 5265c3c9986SThomas Zimmermann static const struct drm_crtc_funcs cirrus_crtc_funcs = { 5275c3c9986SThomas Zimmermann .reset = drm_atomic_helper_crtc_reset, 5285c3c9986SThomas Zimmermann .destroy = drm_crtc_cleanup, 5295c3c9986SThomas Zimmermann .set_config = drm_atomic_helper_set_config, 5305c3c9986SThomas Zimmermann .page_flip = drm_atomic_helper_page_flip, 5315c3c9986SThomas Zimmermann .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 5325c3c9986SThomas Zimmermann .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 5335c3c9986SThomas Zimmermann }; 5345c3c9986SThomas Zimmermann 5355c3c9986SThomas Zimmermann static const struct drm_encoder_funcs cirrus_encoder_funcs = { 5365c3c9986SThomas Zimmermann .destroy = drm_encoder_cleanup, 5375c3c9986SThomas Zimmermann }; 5385c3c9986SThomas Zimmermann 5395c3c9986SThomas Zimmermann static int cirrus_connector_helper_get_modes(struct drm_connector *connector) 5405c3c9986SThomas Zimmermann { 5415c3c9986SThomas Zimmermann int count; 5425c3c9986SThomas Zimmermann 5435c3c9986SThomas Zimmermann count = drm_add_modes_noedid(connector, 5445c3c9986SThomas Zimmermann connector->dev->mode_config.max_width, 5455c3c9986SThomas Zimmermann connector->dev->mode_config.max_height); 5465c3c9986SThomas Zimmermann drm_set_preferred_mode(connector, 1024, 768); 5475c3c9986SThomas Zimmermann return count; 5485c3c9986SThomas Zimmermann } 5495c3c9986SThomas Zimmermann 5505c3c9986SThomas Zimmermann static const struct drm_connector_helper_funcs cirrus_connector_helper_funcs = { 5515c3c9986SThomas Zimmermann .get_modes = cirrus_connector_helper_get_modes, 5525c3c9986SThomas Zimmermann }; 5535c3c9986SThomas Zimmermann 5545c3c9986SThomas Zimmermann static const struct drm_connector_funcs cirrus_connector_funcs = { 5555c3c9986SThomas Zimmermann .fill_modes = drm_helper_probe_single_connector_modes, 5565c3c9986SThomas Zimmermann .destroy = drm_connector_cleanup, 5575c3c9986SThomas Zimmermann .reset = drm_atomic_helper_connector_reset, 5585c3c9986SThomas Zimmermann .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 5595c3c9986SThomas Zimmermann .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 5605c3c9986SThomas Zimmermann }; 5615c3c9986SThomas Zimmermann 5625c3c9986SThomas Zimmermann static int cirrus_pipe_init(struct cirrus_device *cirrus) 5635c3c9986SThomas Zimmermann { 5645c3c9986SThomas Zimmermann struct drm_device *dev = &cirrus->dev; 5655c3c9986SThomas Zimmermann struct drm_plane *primary_plane; 5665c3c9986SThomas Zimmermann struct drm_crtc *crtc; 5675c3c9986SThomas Zimmermann struct drm_encoder *encoder; 5685c3c9986SThomas Zimmermann struct drm_connector *connector; 5695c3c9986SThomas Zimmermann int ret; 5705c3c9986SThomas Zimmermann 5715c3c9986SThomas Zimmermann primary_plane = &cirrus->primary_plane; 5725c3c9986SThomas Zimmermann ret = drm_universal_plane_init(dev, primary_plane, 0, 5735c3c9986SThomas Zimmermann &cirrus_primary_plane_funcs, 5745c3c9986SThomas Zimmermann cirrus_primary_plane_formats, 5755c3c9986SThomas Zimmermann ARRAY_SIZE(cirrus_primary_plane_formats), 5765c3c9986SThomas Zimmermann cirrus_primary_plane_format_modifiers, 5775c3c9986SThomas Zimmermann DRM_PLANE_TYPE_PRIMARY, NULL); 5785c3c9986SThomas Zimmermann if (ret) 5795c3c9986SThomas Zimmermann return ret; 5805c3c9986SThomas Zimmermann drm_plane_helper_add(primary_plane, &cirrus_primary_plane_helper_funcs); 5815c3c9986SThomas Zimmermann drm_plane_enable_fb_damage_clips(primary_plane); 5825c3c9986SThomas Zimmermann 5835c3c9986SThomas Zimmermann crtc = &cirrus->crtc; 5845c3c9986SThomas Zimmermann ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL, 5855c3c9986SThomas Zimmermann &cirrus_crtc_funcs, NULL); 5865c3c9986SThomas Zimmermann if (ret) 5875c3c9986SThomas Zimmermann return ret; 5885c3c9986SThomas Zimmermann drm_crtc_helper_add(crtc, &cirrus_crtc_helper_funcs); 5895c3c9986SThomas Zimmermann 5905c3c9986SThomas Zimmermann encoder = &cirrus->encoder; 5915c3c9986SThomas Zimmermann ret = drm_encoder_init(dev, encoder, &cirrus_encoder_funcs, 5925c3c9986SThomas Zimmermann DRM_MODE_ENCODER_VIRTUAL, NULL); 5935c3c9986SThomas Zimmermann if (ret) 5945c3c9986SThomas Zimmermann return ret; 5955c3c9986SThomas Zimmermann encoder->possible_crtcs = drm_crtc_mask(crtc); 5965c3c9986SThomas Zimmermann 5975c3c9986SThomas Zimmermann connector = &cirrus->connector; 5985c3c9986SThomas Zimmermann ret = drm_connector_init(dev, connector, &cirrus_connector_funcs, 5995c3c9986SThomas Zimmermann DRM_MODE_CONNECTOR_VIRTUAL); 6005c3c9986SThomas Zimmermann if (ret) 6015c3c9986SThomas Zimmermann return ret; 6025c3c9986SThomas Zimmermann drm_connector_helper_add(connector, &cirrus_connector_helper_funcs); 6035c3c9986SThomas Zimmermann 6045c3c9986SThomas Zimmermann ret = drm_connector_attach_encoder(connector, encoder); 6055c3c9986SThomas Zimmermann if (ret) 6065c3c9986SThomas Zimmermann return ret; 6075c3c9986SThomas Zimmermann 6085c3c9986SThomas Zimmermann return 0; 6095c3c9986SThomas Zimmermann } 6105c3c9986SThomas Zimmermann 6115c3c9986SThomas Zimmermann /* ------------------------------------------------------------------ */ 6125c3c9986SThomas Zimmermann /* cirrus framebuffers & mode config */ 6135c3c9986SThomas Zimmermann 6145c3c9986SThomas Zimmermann static enum drm_mode_status cirrus_mode_config_mode_valid(struct drm_device *dev, 6155c3c9986SThomas Zimmermann const struct drm_display_mode *mode) 6165c3c9986SThomas Zimmermann { 6175c3c9986SThomas Zimmermann const struct drm_format_info *format = drm_format_info(DRM_FORMAT_XRGB8888); 6185c3c9986SThomas Zimmermann uint64_t pitch = drm_format_info_min_pitch(format, 0, mode->hdisplay); 6195c3c9986SThomas Zimmermann 6205c3c9986SThomas Zimmermann if (pitch * mode->vdisplay > CIRRUS_VRAM_SIZE) 6215c3c9986SThomas Zimmermann return MODE_MEM; 6225c3c9986SThomas Zimmermann 6235c3c9986SThomas Zimmermann return MODE_OK; 6245c3c9986SThomas Zimmermann } 6255c3c9986SThomas Zimmermann 6265c3c9986SThomas Zimmermann static const struct drm_mode_config_funcs cirrus_mode_config_funcs = { 6275c3c9986SThomas Zimmermann .fb_create = drm_gem_fb_create_with_dirty, 6285c3c9986SThomas Zimmermann .mode_valid = cirrus_mode_config_mode_valid, 6295c3c9986SThomas Zimmermann .atomic_check = drm_atomic_helper_check, 6305c3c9986SThomas Zimmermann .atomic_commit = drm_atomic_helper_commit, 6315c3c9986SThomas Zimmermann }; 6325c3c9986SThomas Zimmermann 6335c3c9986SThomas Zimmermann static int cirrus_mode_config_init(struct cirrus_device *cirrus) 6345c3c9986SThomas Zimmermann { 6355c3c9986SThomas Zimmermann struct drm_device *dev = &cirrus->dev; 6365c3c9986SThomas Zimmermann int ret; 6375c3c9986SThomas Zimmermann 6385c3c9986SThomas Zimmermann ret = drmm_mode_config_init(dev); 6395c3c9986SThomas Zimmermann if (ret) 6405c3c9986SThomas Zimmermann return ret; 6415c3c9986SThomas Zimmermann 6425c3c9986SThomas Zimmermann dev->mode_config.min_width = 0; 6435c3c9986SThomas Zimmermann dev->mode_config.min_height = 0; 6445c3c9986SThomas Zimmermann dev->mode_config.max_width = CIRRUS_MAX_PITCH / 2; 6455c3c9986SThomas Zimmermann dev->mode_config.max_height = 1024; 6465c3c9986SThomas Zimmermann dev->mode_config.preferred_depth = 16; 6475c3c9986SThomas Zimmermann dev->mode_config.prefer_shadow = 0; 6485c3c9986SThomas Zimmermann dev->mode_config.funcs = &cirrus_mode_config_funcs; 6495c3c9986SThomas Zimmermann 6505c3c9986SThomas Zimmermann return 0; 6515c3c9986SThomas Zimmermann } 6525c3c9986SThomas Zimmermann 6535c3c9986SThomas Zimmermann /* ------------------------------------------------------------------ */ 6545c3c9986SThomas Zimmermann 6555c3c9986SThomas Zimmermann DEFINE_DRM_GEM_FOPS(cirrus_fops); 6565c3c9986SThomas Zimmermann 6575c3c9986SThomas Zimmermann static const struct drm_driver cirrus_driver = { 6585c3c9986SThomas Zimmermann .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 6595c3c9986SThomas Zimmermann 6605c3c9986SThomas Zimmermann .name = DRIVER_NAME, 6615c3c9986SThomas Zimmermann .desc = DRIVER_DESC, 6625c3c9986SThomas Zimmermann .date = DRIVER_DATE, 6635c3c9986SThomas Zimmermann .major = DRIVER_MAJOR, 6645c3c9986SThomas Zimmermann .minor = DRIVER_MINOR, 6655c3c9986SThomas Zimmermann 6665c3c9986SThomas Zimmermann .fops = &cirrus_fops, 6675c3c9986SThomas Zimmermann DRM_GEM_SHMEM_DRIVER_OPS, 6685c3c9986SThomas Zimmermann DRM_FBDEV_SHMEM_DRIVER_OPS, 6695c3c9986SThomas Zimmermann }; 6705c3c9986SThomas Zimmermann 6715c3c9986SThomas Zimmermann static int cirrus_pci_probe(struct pci_dev *pdev, 6725c3c9986SThomas Zimmermann const struct pci_device_id *ent) 6735c3c9986SThomas Zimmermann { 6745c3c9986SThomas Zimmermann struct drm_device *dev; 6755c3c9986SThomas Zimmermann struct cirrus_device *cirrus; 6765c3c9986SThomas Zimmermann int ret; 6775c3c9986SThomas Zimmermann 6785c3c9986SThomas Zimmermann ret = aperture_remove_conflicting_pci_devices(pdev, cirrus_driver.name); 6795c3c9986SThomas Zimmermann if (ret) 6805c3c9986SThomas Zimmermann return ret; 6815c3c9986SThomas Zimmermann 6825c3c9986SThomas Zimmermann ret = pcim_enable_device(pdev); 6835c3c9986SThomas Zimmermann if (ret) 6845c3c9986SThomas Zimmermann return ret; 6855c3c9986SThomas Zimmermann 6865c3c9986SThomas Zimmermann ret = pci_request_regions(pdev, DRIVER_NAME); 6875c3c9986SThomas Zimmermann if (ret) 6885c3c9986SThomas Zimmermann return ret; 6895c3c9986SThomas Zimmermann 6905c3c9986SThomas Zimmermann ret = -ENOMEM; 6915c3c9986SThomas Zimmermann cirrus = devm_drm_dev_alloc(&pdev->dev, &cirrus_driver, 6925c3c9986SThomas Zimmermann struct cirrus_device, dev); 6935c3c9986SThomas Zimmermann if (IS_ERR(cirrus)) 6945c3c9986SThomas Zimmermann return PTR_ERR(cirrus); 6955c3c9986SThomas Zimmermann 6965c3c9986SThomas Zimmermann dev = &cirrus->dev; 6975c3c9986SThomas Zimmermann 6985c3c9986SThomas Zimmermann cirrus->vram = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0), 6995c3c9986SThomas Zimmermann pci_resource_len(pdev, 0)); 7005c3c9986SThomas Zimmermann if (cirrus->vram == NULL) 7015c3c9986SThomas Zimmermann return -ENOMEM; 7025c3c9986SThomas Zimmermann 7035c3c9986SThomas Zimmermann cirrus->mmio = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 1), 7045c3c9986SThomas Zimmermann pci_resource_len(pdev, 1)); 7055c3c9986SThomas Zimmermann if (cirrus->mmio == NULL) 7065c3c9986SThomas Zimmermann return -ENOMEM; 7075c3c9986SThomas Zimmermann 7085c3c9986SThomas Zimmermann ret = cirrus_mode_config_init(cirrus); 7095c3c9986SThomas Zimmermann if (ret) 7105c3c9986SThomas Zimmermann return ret; 7115c3c9986SThomas Zimmermann 7125c3c9986SThomas Zimmermann ret = cirrus_pipe_init(cirrus); 7135c3c9986SThomas Zimmermann if (ret < 0) 7145c3c9986SThomas Zimmermann return ret; 7155c3c9986SThomas Zimmermann 7165c3c9986SThomas Zimmermann drm_mode_config_reset(dev); 7175c3c9986SThomas Zimmermann 7185c3c9986SThomas Zimmermann pci_set_drvdata(pdev, dev); 7195c3c9986SThomas Zimmermann ret = drm_dev_register(dev, 0); 7205c3c9986SThomas Zimmermann if (ret) 7215c3c9986SThomas Zimmermann return ret; 7225c3c9986SThomas Zimmermann 7235c3c9986SThomas Zimmermann drm_client_setup(dev, NULL); 7245c3c9986SThomas Zimmermann return 0; 7255c3c9986SThomas Zimmermann } 7265c3c9986SThomas Zimmermann 7275c3c9986SThomas Zimmermann static void cirrus_pci_remove(struct pci_dev *pdev) 7285c3c9986SThomas Zimmermann { 7295c3c9986SThomas Zimmermann struct drm_device *dev = pci_get_drvdata(pdev); 7305c3c9986SThomas Zimmermann 7315c3c9986SThomas Zimmermann drm_dev_unplug(dev); 7325c3c9986SThomas Zimmermann drm_atomic_helper_shutdown(dev); 7335c3c9986SThomas Zimmermann } 7345c3c9986SThomas Zimmermann 7355c3c9986SThomas Zimmermann static void cirrus_pci_shutdown(struct pci_dev *pdev) 7365c3c9986SThomas Zimmermann { 7375c3c9986SThomas Zimmermann drm_atomic_helper_shutdown(pci_get_drvdata(pdev)); 7385c3c9986SThomas Zimmermann } 7395c3c9986SThomas Zimmermann 7405c3c9986SThomas Zimmermann static const struct pci_device_id pciidlist[] = { 7415c3c9986SThomas Zimmermann { 7425c3c9986SThomas Zimmermann .vendor = PCI_VENDOR_ID_CIRRUS, 7435c3c9986SThomas Zimmermann .device = PCI_DEVICE_ID_CIRRUS_5446, 7445c3c9986SThomas Zimmermann /* only bind to the cirrus chip in qemu */ 7455c3c9986SThomas Zimmermann .subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET, 7465c3c9986SThomas Zimmermann .subdevice = PCI_SUBDEVICE_ID_QEMU, 7475c3c9986SThomas Zimmermann }, { 7485c3c9986SThomas Zimmermann .vendor = PCI_VENDOR_ID_CIRRUS, 7495c3c9986SThomas Zimmermann .device = PCI_DEVICE_ID_CIRRUS_5446, 7505c3c9986SThomas Zimmermann .subvendor = PCI_VENDOR_ID_XEN, 7515c3c9986SThomas Zimmermann .subdevice = 0x0001, 7525c3c9986SThomas Zimmermann }, 7535c3c9986SThomas Zimmermann { /* end if list */ } 7545c3c9986SThomas Zimmermann }; 7555c3c9986SThomas Zimmermann 7565c3c9986SThomas Zimmermann static struct pci_driver cirrus_pci_driver = { 7575c3c9986SThomas Zimmermann .name = DRIVER_NAME, 7585c3c9986SThomas Zimmermann .id_table = pciidlist, 7595c3c9986SThomas Zimmermann .probe = cirrus_pci_probe, 7605c3c9986SThomas Zimmermann .remove = cirrus_pci_remove, 7615c3c9986SThomas Zimmermann .shutdown = cirrus_pci_shutdown, 7625c3c9986SThomas Zimmermann }; 7635c3c9986SThomas Zimmermann 7645c3c9986SThomas Zimmermann drm_module_pci_driver(cirrus_pci_driver) 7655c3c9986SThomas Zimmermann 7665c3c9986SThomas Zimmermann MODULE_DEVICE_TABLE(pci, pciidlist); 7675c3c9986SThomas Zimmermann MODULE_DESCRIPTION("Cirrus driver for QEMU emulated device"); 7685c3c9986SThomas Zimmermann MODULE_LICENSE("GPL"); 769