From: Ville Syrj?l? <ville.syrjala at linux.intel.com> Gen2-4 have a non-scalable sprite C, which can be assigned to either pipe A or pipe B dynamically. Expose sprite C as a drm_plame. I've tested this only on 85x (8086:3582) and 946 (8086:2972). Gen2/3 chipsets should also be able to re-assign the primary plane B to act as a sprite on pipe A. But in order to expose that, we should actually expose all primary planes as drm_planes. We should make that change eventually, but currently we're not yet ready for it. So for now only sprite C is exposed, and sprite B can't be used yet. Based on the gen2 docs, it seems that the sprite C support is usually present only on mobile platforms. The exception is SPG-B, which I'm assuming means i865g for our driver. Signed-off-by: Ville Syrj?l? <ville.syrjala at linux.intel.com> --- drivers/gpu/drm/i915/i915_reg.h | 27 +++ drivers/gpu/drm/i915/intel_display.c | 35 +++- drivers/gpu/drm/i915/intel_sprite.c | 339 +++++++++++++++++++++++++++++++++-- 3 files changed, 381 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 31de7e4..7a0037d 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3085,15 +3085,24 @@ #define DISPPLANE_SRC_KEY_DISABLE 0 #define DISPPLANE_LINE_DOUBLE (1<<20) #define DISPPLANE_NO_LINE_DOUBLE 0 +#define DISPPLANE_PLANE_CSC_DISABLE (1<<19) #define DISPPLANE_STEREO_POLARITY_FIRST 0 #define DISPPLANE_STEREO_POLARITY_SECOND (1<<18) +#define DISPPLANE_YUV_BYTE_ORDER_MASK (3<<16) +#define DISPPLANE_YUV_ORDER_YUYV (0<<16) +#define DISPPLANE_YUV_ORDER_UYVY (1<<16) +#define DISPPLANE_YUV_ORDER_YVYU (2<<16) +#define DISPPLANE_YUV_ORDER_VYUY (3<<16) #define DISPPLANE_TRICKLE_FEED_DISABLE (1<<14) /* Ironlake */ #define DISPPLANE_TILED (1<<10) #define _DSPAADDR (dev_priv->info->display_mmio_offset + 0x70184) #define _DSPASTRIDE (dev_priv->info->display_mmio_offset + 0x70188) #define _DSPAPOS (dev_priv->info->display_mmio_offset + 0x7018C) /* reserved */ #define _DSPASIZE (dev_priv->info->display_mmio_offset + 0x70190) +#define _DSPAKEYMINVAL (dev_priv->info->display_mmio_offset + 0x70194) +#define _DSPAKEYMSK (dev_priv->info->display_mmio_offset + 0x70198) #define _DSPASURF (dev_priv->info->display_mmio_offset + 0x7019C) /* 965+ only */ +#define _DSPAKEYMAXVAL (dev_priv->info->display_mmio_offset + 0x701A0) #define _DSPATILEOFF (dev_priv->info->display_mmio_offset + 0x701A4) /* 965+ only */ #define _DSPAOFFSET (dev_priv->info->display_mmio_offset + 0x701A4) /* HSW */ #define _DSPASURFLIVE (dev_priv->info->display_mmio_offset + 0x701AC) @@ -3103,7 +3112,10 @@ #define DSPSTRIDE(plane) _PIPE(plane, _DSPASTRIDE, _DSPBSTRIDE) #define DSPPOS(plane) _PIPE(plane, _DSPAPOS, _DSPBPOS) #define DSPSIZE(plane) _PIPE(plane, _DSPASIZE, _DSPBSIZE) +#define DSPKEYMINVAL(plane) _PIPE(plane, _DSPAKEYMINVAL, _DSPBKEYMINVAL) +#define DSPKEYMSK(plane) _PIPE(plane, _DSPAKEYMSK, _DSPBKEYMSK) #define DSPSURF(plane) _PIPE(plane, _DSPASURF, _DSPBSURF) +#define DSPKEYMAXVAL(plane) _PIPE(plane, _DSPAKEYMAXVAL, _DSPBKEYMAXVAL) #define DSPTILEOFF(plane) _PIPE(plane, _DSPATILEOFF, _DSPBTILEOFF) #define DSPLINOFF(plane) DSPADDR(plane) #define DSPOFFSET(plane) _PIPE(plane, _DSPAOFFSET, _DSPBOFFSET) @@ -3116,6 +3128,18 @@ #define I915_MODIFY_DISPBASE(reg, gfx_addr) \ (I915_WRITE((reg), (gfx_addr) | I915_LO_DISPBASE(I915_READ(reg)))) +/* plane C color correction */ +#define DCLRC0 (dev_priv->info->display_mmio_offset + 0x721d0) +#define DCLRC1 (dev_priv->info->display_mmio_offset + 0x721d4) + +/* plane C pre-blend gamma */ +#define GAMC5 (dev_priv->info->display_mmio_offset + 0x721e0) +#define GAMC4 (dev_priv->info->display_mmio_offset + 0x721e4) +#define GAMC3 (dev_priv->info->display_mmio_offset + 0x721e8) +#define GAMC2 (dev_priv->info->display_mmio_offset + 0x721ec) +#define GAMC1 (dev_priv->info->display_mmio_offset + 0x721f0) +#define GAMC0 (dev_priv->info->display_mmio_offset + 0x721f4) + /* VBIOS flags */ #define SWF00 (dev_priv->info->display_mmio_offset + 0x71410) #define SWF01 (dev_priv->info->display_mmio_offset + 0x71414) @@ -3151,9 +3175,12 @@ #define _DSPBSTRIDE (dev_priv->info->display_mmio_offset + 0x71188) #define _DSPBPOS (dev_priv->info->display_mmio_offset + 0x7118C) #define _DSPBSIZE (dev_priv->info->display_mmio_offset + 0x71190) +#define _DSPBKEYMINVAL (dev_priv->info->display_mmio_offset + 0x71194) +#define _DSPBKEYMSK (dev_priv->info->display_mmio_offset + 0x71198) #define _DSPBSURF (dev_priv->info->display_mmio_offset + 0x7119C) #define _DSPBTILEOFF (dev_priv->info->display_mmio_offset + 0x711A4) #define _DSPBOFFSET (dev_priv->info->display_mmio_offset + 0x711A4) +#define _DSPBKEYMAXVAL (dev_priv->info->display_mmio_offset + 0x711A0) #define _DSPBSURFLIVE (dev_priv->info->display_mmio_offset + 0x711AC) /* Sprite A control */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4f4a8d1..4f3b490 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8706,7 +8706,8 @@ int intel_framebuffer_init(struct drm_device *dev, case DRM_FORMAT_UYVY: case DRM_FORMAT_YVYU: case DRM_FORMAT_VYUY: - if (INTEL_INFO(dev)->gen < 5) { + /* YUV sprite C / video sprite support? */ + if (INTEL_INFO(dev)->num_pipes == 1) { DRM_DEBUG("invalid format: 0x%08x\n", mode_cmd->pixel_format); return -EINVAL; } @@ -9054,6 +9055,10 @@ void intel_modeset_init(struct drm_device *dev) for (i = 0; i < INTEL_INFO(dev)->num_pipes; i++) { intel_crtc_init(dev, i); + + if (INTEL_INFO(dev)->gen <= 4) + continue; + for (j = 0; j < dev_priv->num_plane; j++) { ret = intel_plane_init(dev, i, j); if (ret) @@ -9062,6 +9067,34 @@ void intel_modeset_init(struct drm_device *dev) } } + /* + * Sprite C: + * the pipe we pass here doesn't matter since the + * sprite->pipe assignment can be changed dynamically. + */ + + /* one pipe and planes A+C */ + if (IS_I865G(dev)) { + /* + * Sprite C: + * the pipe we pass here doesn't matter since the + * sprite->pipe assignment can be changed dynamically. + */ + ret = intel_plane_init(dev, 0, PLANE_C); + if (ret) + DRM_DEBUG_KMS("plane %c init failed: %d\n", + plane_name(PLANE_C), ret); + } + + /* two pipes and planes A+B+C */ + if (INTEL_INFO(dev)->gen <= 4 && + INTEL_INFO(dev)->num_pipes == 2) { + ret = intel_plane_init(dev, 0, PLANE_C); + if (ret) + DRM_DEBUG_KMS("plane %c init failed: %d\n", + plane_name(PLANE_C), ret); + } + intel_cpu_pll_init(dev); intel_pch_pll_init(dev); diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index c7d25c5..026e6b8 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -36,6 +36,239 @@ #include <drm/i915_drm.h> #include "i915_drv.h" +static bool format_is_yuv(uint32_t pixel_format) +{ + switch (pixel_format) { + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YVYU: + return true; + default: + return false; + } +} + +static void +gen2_update_plane(struct drm_plane *dplane, struct drm_framebuffer *fb, + struct drm_i915_gem_object *obj, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t x, uint32_t y, + uint32_t src_w, uint32_t src_h) +{ + struct drm_device *dev = dplane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(dplane); + int pipe = intel_plane->pipe; + int plane = intel_plane->plane; + u32 dspcntr; + unsigned long dspsurf_offset, linear_offset; + int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + + dspcntr = I915_READ(DSPCNTR(plane)); + + dspcntr &= ~(DISPPLANE_GAMMA_ENABLE | + DISPPLANE_PIXFORMAT_MASK | + DISPPLANE_YUV_BYTE_ORDER_MASK | + DISPPLANE_PLANE_CSC_DISABLE | + DISPPLANE_SEL_PIPE_MASK | + DISPPLANE_TILED); + + switch (fb->pixel_format) { + case DRM_FORMAT_YUYV: + dspcntr |= DISPPLANE_YUV422 | DISPPLANE_YUV_ORDER_YUYV; + break; + case DRM_FORMAT_YVYU: + dspcntr |= DISPPLANE_YUV422 | DISPPLANE_YUV_ORDER_YVYU; + break; + case DRM_FORMAT_UYVY: + dspcntr |= DISPPLANE_YUV422 | DISPPLANE_YUV_ORDER_UYVY; + break; + case DRM_FORMAT_VYUY: + dspcntr |= DISPPLANE_YUV422 | DISPPLANE_YUV_ORDER_VYUY; + break; + case DRM_FORMAT_C8: + dspcntr |= DISPPLANE_8BPP | DISPPLANE_GAMMA_ENABLE; + break; + case DRM_FORMAT_ARGB1555: + dspcntr |= IS_GEN3(dev) ? DISPPLANE_BGRA555 : DISPPLANE_BGRX555; + break; + case DRM_FORMAT_XRGB1555: + dspcntr |= DISPPLANE_BGRX555; + break; + case DRM_FORMAT_RGB565: + dspcntr |= DISPPLANE_BGRX565; + break; + case DRM_FORMAT_XRGB8888: + dspcntr |= DISPPLANE_BGRX888; + break; + case DRM_FORMAT_ARGB8888: + dspcntr |= DISPPLANE_BGRA888; + break; + case DRM_FORMAT_XBGR2101010: + dspcntr |= DISPPLANE_RGBX101010; + break; + case DRM_FORMAT_ABGR2101010: + dspcntr |= DISPPLANE_RGBA101010; + break; + case DRM_FORMAT_XBGR8888: + dspcntr |= DISPPLANE_RGBX888; + break; + case DRM_FORMAT_ABGR8888: + dspcntr |= DISPPLANE_RGBA888; + break; + default: + /* + * If we get here one of the upper layers failed to filter + * out the unsupported plane formats + */ + BUG(); + break; + } + + if (pipe == PIPE_B) + dspcntr |= DISPPLANE_SEL_PIPE_B; + + if (plane == PLANE_C && !IS_I865G(dev) && + !format_is_yuv(fb->pixel_format)) + dspcntr |= DISPPLANE_PLANE_CSC_DISABLE; + + if (INTEL_INFO(dev)->gen >= 4 && + obj->tiling_mode != I915_TILING_NONE) + dspcntr |= DISPPLANE_TILED; + + dspcntr |= DISPLAY_PLANE_ENABLE; + + /* Sizes are 0 based */ + src_w--; + src_h--; + crtc_w--; + crtc_h--; + + intel_update_sprite_watermarks(dev, plane, crtc_w, pixel_size); + + /* + * On gen2 DCLRC1 reset value is 0. Fix it, except on + * i865g which supposedly doesn't support color correction. + */ + if (plane == PLANE_C && IS_GEN2(dev) && !IS_I865G(dev)) { + /* Pass-through for color correction */ + I915_WRITE(DCLRC0, 1 << 24); + I915_WRITE(DCLRC1, 1 << 8); + /* Plane gamma should default to linear ramp */ + } + + I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]); + I915_WRITE(DSPPOS(plane), (crtc_y << 16) | crtc_x); + I915_WRITE(DSPSIZE(plane), (crtc_h << 16) | crtc_w); + I915_WRITE(DSPCNTR(plane), dspcntr); + + linear_offset = y * fb->pitches[0] + x * pixel_size; + + if (INTEL_INFO(dev)->gen >= 4) { + dspsurf_offset = intel_gen4_compute_page_offset(&x, &y, + obj->tiling_mode, + pixel_size, + fb->pitches[0]); + linear_offset -= dspsurf_offset; + + I915_WRITE(DSPTILEOFF(plane), (y << 16) | x); + I915_WRITE(DSPLINOFF(plane), linear_offset); + I915_MODIFY_DISPBASE(DSPSURF(plane), obj->gtt_offset + dspsurf_offset); + } else { + I915_WRITE(DSPADDR(plane), obj->gtt_offset + linear_offset); + } + + intel_flush_display_plane(dev_priv, plane); + +} + +static void +gen2_disable_plane(struct drm_plane *dplane) +{ + struct drm_device *dev = dplane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(dplane); + int plane = intel_plane->plane; + + I915_WRITE(DSPCNTR(plane), I915_READ(DSPCNTR(plane)) & + ~DISPLAY_PLANE_ENABLE); + /* Activate double buffered register update */ + intel_flush_display_plane(dev_priv, plane); +} + +static int +gen2_update_colorkey(struct drm_plane *dplane, + struct drm_intel_sprite_colorkey *key) +{ + struct drm_device *dev = dplane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(dplane); + int plane = intel_plane->plane; + u32 dspcntr; + + if (key->flags & I915_SET_COLORKEY_DESTINATION) + return -EINVAL; + + I915_WRITE(DSPKEYMINVAL(plane), key->min_value); + I915_WRITE(DSPKEYMAXVAL(plane), key->max_value); + I915_WRITE(DSPKEYMSK(plane), key->channel_mask); + + dspcntr = I915_READ(DSPCNTR(plane)); + dspcntr &= ~DISPPLANE_SRC_KEY_ENABLE; + if (key->flags & I915_SET_COLORKEY_SOURCE) + dspcntr |= DISPPLANE_SRC_KEY_ENABLE; + I915_WRITE(DSPCNTR(plane), dspcntr); + + intel_flush_display_plane(dev_priv, plane); + + return 0; +} + +static void +gen2_get_colorkey(struct drm_plane *dplane, + struct drm_intel_sprite_colorkey *key) +{ + struct drm_device *dev = dplane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(dplane); + int plane = intel_plane->plane; + u32 dspcntr; + + key->min_value = I915_READ(DSPKEYMINVAL(plane)); + key->max_value = I915_READ(DSPKEYMAXVAL(plane)); + key->channel_mask = I915_READ(DSPKEYMSK(plane)); + + dspcntr = I915_READ(DSPCNTR(plane)); + if (dspcntr & DISPPLANE_SRC_KEY_ENABLE) + key->flags = I915_SET_COLORKEY_SOURCE; + else + key->flags = I915_SET_COLORKEY_NONE; +} + +static bool +gen2_check_pipe_sel(struct drm_plane *dplane, int new_pipe) +{ + struct drm_device *dev = dplane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(dplane); + int plane = intel_plane->plane; + int old_pipe; + u32 dspcntr; + + dspcntr = I915_READ(DSPCNTR(plane)); + + /* allow pipe changes only when the sprite is disabled */ + if (!(dspcntr & DISPLAY_PLANE_ENABLE)) + return true; + + old_pipe = (dspcntr & DISPPLANE_SEL_PIPE_MASK) == DISPPLANE_SEL_PIPE_B; + + return !WARN(old_pipe != new_pipe, + "sprite %c pipe assertion failure (expected %c, current %c)", + plane_name(plane), pipe_name(old_pipe), pipe_name(new_pipe)); +} + static void vlv_update_plane(struct drm_plane *dplane, struct drm_framebuffer *fb, struct drm_i915_gem_object *obj, int crtc_x, int crtc_y, @@ -596,7 +829,7 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct intel_plane *intel_plane = to_intel_plane(plane); struct intel_framebuffer *intel_fb; struct drm_i915_gem_object *obj, *old_obj; - int pipe = intel_plane->pipe; + int pipe = intel_crtc->pipe; /* the new pipe */ enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); int ret = 0; @@ -629,8 +862,13 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, return -EINVAL; /* Don't modify another pipe's plane */ - if (intel_plane->pipe != intel_crtc->pipe) - return -EINVAL; + if (INTEL_INFO(dev)->gen <= 4) { + if (!gen2_check_pipe_sel(plane, pipe)) + return -EINVAL; + } else { + if (intel_plane->pipe != pipe) + return -EINVAL; + } /* Sprite planes can be linear or x-tiled surfaces */ switch (obj->tiling_mode) { @@ -701,6 +939,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, intel_plane->obj = obj; + intel_plane->pipe = pipe; + /* * Be sure to re-enable the primary before the sprite is no longer * covering it fully. @@ -724,7 +964,7 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, */ if (old_obj != obj) { mutex_unlock(&dev->struct_mutex); - intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe); + intel_wait_for_vblank(dev, pipe); mutex_lock(&dev->struct_mutex); } intel_unpin_fb_obj(old_obj); @@ -851,6 +1091,43 @@ static const struct drm_plane_funcs intel_plane_funcs = { .destroy = intel_destroy_plane, }; +static uint32_t i865g_plane_formats[] = { + DRM_FORMAT_C8, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, +}; + +static uint32_t gen2_plane_formats[] = { + DRM_FORMAT_C8, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, +}; + +static uint32_t gen4_plane_formats[] = { + DRM_FORMAT_C8, + DRM_FORMAT_RGB565, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, +}; + static uint32_t ilk_plane_formats[] = { DRM_FORMAT_XRGB8888, DRM_FORMAT_YUYV, @@ -891,29 +1168,50 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane) int num_plane_formats; int ret; - if (INTEL_INFO(dev)->gen < 5) - return -ENODEV; - intel_plane = kzalloc(sizeof(struct intel_plane), GFP_KERNEL); if (!intel_plane) return -ENOMEM; switch (INTEL_INFO(dev)->gen) { + case 2: + case 3: + case 4: case 5: case 6: - intel_plane->can_scale = true; - intel_plane->max_downscale = 16; - intel_plane->update_plane = ilk_update_plane; - intel_plane->disable_plane = ilk_disable_plane; - intel_plane->update_colorkey = ilk_update_colorkey; - intel_plane->get_colorkey = ilk_get_colorkey; + if (INTEL_INFO(dev)->gen <= 4) { + intel_plane->can_scale = false; + intel_plane->max_downscale = 1; - if (IS_GEN6(dev)) { - plane_formats = snb_plane_formats; - num_plane_formats = ARRAY_SIZE(snb_plane_formats); + intel_plane->update_plane = gen2_update_plane; + intel_plane->disable_plane = gen2_disable_plane; + intel_plane->update_colorkey = gen2_update_colorkey; + intel_plane->get_colorkey = gen2_get_colorkey; + + if (IS_I865G(dev)) { + plane_formats = i865g_plane_formats; + num_plane_formats = ARRAY_SIZE(i865g_plane_formats); + } else if (INTEL_INFO(dev)->gen <= 3) { + plane_formats = gen2_plane_formats; + num_plane_formats = ARRAY_SIZE(gen2_plane_formats); + } else { + plane_formats = gen4_plane_formats; + num_plane_formats = ARRAY_SIZE(gen4_plane_formats); + } } else { - plane_formats = ilk_plane_formats; - num_plane_formats = ARRAY_SIZE(ilk_plane_formats); + intel_plane->can_scale = true; + intel_plane->max_downscale = 16; + intel_plane->update_plane = ilk_update_plane; + intel_plane->disable_plane = ilk_disable_plane; + intel_plane->update_colorkey = ilk_update_colorkey; + intel_plane->get_colorkey = ilk_get_colorkey; + + if (IS_GEN6(dev)) { + plane_formats = snb_plane_formats; + num_plane_formats = ARRAY_SIZE(snb_plane_formats); + } else { + plane_formats = ilk_plane_formats; + num_plane_formats = ARRAY_SIZE(ilk_plane_formats); + } } break; @@ -951,7 +1249,10 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane) intel_plane->pipe = pipe; intel_plane->plane = plane; - possible_crtcs = (1 << pipe); + if (INTEL_INFO(dev)->gen >= 5) + possible_crtcs = 1 << pipe; + else + possible_crtcs = (1 << INTEL_INFO(dev)->num_pipes) - 1; ret = drm_plane_init(dev, &intel_plane->base, possible_crtcs, &intel_plane_funcs, plane_formats, num_plane_formats, -- 1.8.1.5