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