|
@@ -0,0 +1,450 @@
|
|
|
+/*
|
|
|
+ * Copyright © 2011 Intel Corporation
|
|
|
+ *
|
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
|
+ *
|
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
|
+ * Software.
|
|
|
+ *
|
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
+ * SOFTWARE.
|
|
|
+ *
|
|
|
+ * Authors:
|
|
|
+ * Jesse Barnes <jbarnes@virtuousgeek.org>
|
|
|
+ *
|
|
|
+ * New plane/sprite handling.
|
|
|
+ *
|
|
|
+ * The older chips had a separate interface for programming plane related
|
|
|
+ * registers; newer ones are much simpler and we can use the new DRM plane
|
|
|
+ * support.
|
|
|
+ */
|
|
|
+#include "drmP.h"
|
|
|
+#include "drm_crtc.h"
|
|
|
+#include "drm_fourcc.h"
|
|
|
+#include "intel_drv.h"
|
|
|
+#include "i915_drm.h"
|
|
|
+#include "i915_drv.h"
|
|
|
+
|
|
|
+static void
|
|
|
+ivb_update_plane(struct drm_plane *plane, 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 = plane->dev;
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ struct intel_plane *intel_plane = to_intel_plane(plane);
|
|
|
+ int pipe = intel_plane->pipe;
|
|
|
+ u32 sprctl, sprscale = 0;
|
|
|
+ int pixel_size;
|
|
|
+
|
|
|
+ sprctl = I915_READ(SPRCTL(pipe));
|
|
|
+
|
|
|
+ /* Mask out pixel format bits in case we change it */
|
|
|
+ sprctl &= ~SPRITE_PIXFORMAT_MASK;
|
|
|
+ sprctl &= ~SPRITE_RGB_ORDER_RGBX;
|
|
|
+ sprctl &= ~SPRITE_YUV_BYTE_ORDER_MASK;
|
|
|
+
|
|
|
+ switch (fb->pixel_format) {
|
|
|
+ case DRM_FORMAT_XBGR8888:
|
|
|
+ sprctl |= SPRITE_FORMAT_RGBX888;
|
|
|
+ pixel_size = 4;
|
|
|
+ break;
|
|
|
+ case DRM_FORMAT_XRGB8888:
|
|
|
+ sprctl |= SPRITE_FORMAT_RGBX888 | SPRITE_RGB_ORDER_RGBX;
|
|
|
+ pixel_size = 4;
|
|
|
+ break;
|
|
|
+ case DRM_FORMAT_YUYV:
|
|
|
+ sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YUYV;
|
|
|
+ pixel_size = 2;
|
|
|
+ break;
|
|
|
+ case DRM_FORMAT_YVYU:
|
|
|
+ sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YVYU;
|
|
|
+ pixel_size = 2;
|
|
|
+ break;
|
|
|
+ case DRM_FORMAT_UYVY:
|
|
|
+ sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_UYVY;
|
|
|
+ pixel_size = 2;
|
|
|
+ break;
|
|
|
+ case DRM_FORMAT_VYUY:
|
|
|
+ sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_VYUY;
|
|
|
+ pixel_size = 2;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ DRM_DEBUG_DRIVER("bad pixel format, assuming RGBX888\n");
|
|
|
+ sprctl |= DVS_FORMAT_RGBX888;
|
|
|
+ pixel_size = 4;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (obj->tiling_mode != I915_TILING_NONE)
|
|
|
+ sprctl |= SPRITE_TILED;
|
|
|
+
|
|
|
+ /* must disable */
|
|
|
+ sprctl |= SPRITE_TRICKLE_FEED_DISABLE;
|
|
|
+ sprctl |= SPRITE_ENABLE;
|
|
|
+
|
|
|
+ /* Sizes are 0 based */
|
|
|
+ src_w--;
|
|
|
+ src_h--;
|
|
|
+ crtc_w--;
|
|
|
+ crtc_h--;
|
|
|
+
|
|
|
+ intel_update_sprite_watermarks(dev, pipe, crtc_w, pixel_size);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * IVB workaround: must disable low power watermarks for at least
|
|
|
+ * one frame before enabling scaling. LP watermarks can be re-enabled
|
|
|
+ * when scaling is disabled.
|
|
|
+ */
|
|
|
+ if (crtc_w != src_w || crtc_h != src_h) {
|
|
|
+ dev_priv->sprite_scaling_enabled = true;
|
|
|
+ sandybridge_update_wm(dev);
|
|
|
+ intel_wait_for_vblank(dev, pipe);
|
|
|
+ sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h;
|
|
|
+ } else {
|
|
|
+ dev_priv->sprite_scaling_enabled = false;
|
|
|
+ /* potentially re-enable LP watermarks */
|
|
|
+ sandybridge_update_wm(dev);
|
|
|
+ }
|
|
|
+
|
|
|
+ I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]);
|
|
|
+ I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x);
|
|
|
+ if (obj->tiling_mode != I915_TILING_NONE) {
|
|
|
+ I915_WRITE(SPRTILEOFF(pipe), (y << 16) | x);
|
|
|
+ } else {
|
|
|
+ unsigned long offset;
|
|
|
+
|
|
|
+ offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8);
|
|
|
+ I915_WRITE(SPRLINOFF(pipe), offset);
|
|
|
+ }
|
|
|
+ I915_WRITE(SPRSIZE(pipe), (crtc_h << 16) | crtc_w);
|
|
|
+ I915_WRITE(SPRSCALE(pipe), sprscale);
|
|
|
+ I915_WRITE(SPRCTL(pipe), sprctl);
|
|
|
+ I915_WRITE(SPRSURF(pipe), obj->gtt_offset);
|
|
|
+ POSTING_READ(SPRSURF(pipe));
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+ivb_disable_plane(struct drm_plane *plane)
|
|
|
+{
|
|
|
+ struct drm_device *dev = plane->dev;
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ struct intel_plane *intel_plane = to_intel_plane(plane);
|
|
|
+ int pipe = intel_plane->pipe;
|
|
|
+
|
|
|
+ I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE);
|
|
|
+ /* Can't leave the scaler enabled... */
|
|
|
+ I915_WRITE(SPRSCALE(pipe), 0);
|
|
|
+ /* Activate double buffered register update */
|
|
|
+ I915_WRITE(SPRSURF(pipe), 0);
|
|
|
+ POSTING_READ(SPRSURF(pipe));
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+snb_update_plane(struct drm_plane *plane, 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 = plane->dev;
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ struct intel_plane *intel_plane = to_intel_plane(plane);
|
|
|
+ int pipe = intel_plane->pipe, pixel_size;
|
|
|
+ u32 dvscntr, dvsscale = 0;
|
|
|
+
|
|
|
+ dvscntr = I915_READ(DVSCNTR(pipe));
|
|
|
+
|
|
|
+ /* Mask out pixel format bits in case we change it */
|
|
|
+ dvscntr &= ~DVS_PIXFORMAT_MASK;
|
|
|
+ dvscntr &= ~DVS_RGB_ORDER_RGBX;
|
|
|
+ dvscntr &= ~DVS_YUV_BYTE_ORDER_MASK;
|
|
|
+
|
|
|
+ switch (fb->pixel_format) {
|
|
|
+ case DRM_FORMAT_XBGR8888:
|
|
|
+ dvscntr |= DVS_FORMAT_RGBX888;
|
|
|
+ pixel_size = 4;
|
|
|
+ break;
|
|
|
+ case DRM_FORMAT_XRGB8888:
|
|
|
+ dvscntr |= DVS_FORMAT_RGBX888 | DVS_RGB_ORDER_RGBX;
|
|
|
+ pixel_size = 4;
|
|
|
+ break;
|
|
|
+ case DRM_FORMAT_YUYV:
|
|
|
+ dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YUYV;
|
|
|
+ pixel_size = 2;
|
|
|
+ break;
|
|
|
+ case DRM_FORMAT_YVYU:
|
|
|
+ dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YVYU;
|
|
|
+ pixel_size = 2;
|
|
|
+ break;
|
|
|
+ case DRM_FORMAT_UYVY:
|
|
|
+ dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_UYVY;
|
|
|
+ pixel_size = 2;
|
|
|
+ break;
|
|
|
+ case DRM_FORMAT_VYUY:
|
|
|
+ dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_VYUY;
|
|
|
+ pixel_size = 2;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ DRM_DEBUG_DRIVER("bad pixel format, assuming RGBX888\n");
|
|
|
+ dvscntr |= DVS_FORMAT_RGBX888;
|
|
|
+ pixel_size = 4;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (obj->tiling_mode != I915_TILING_NONE)
|
|
|
+ dvscntr |= DVS_TILED;
|
|
|
+
|
|
|
+ /* must disable */
|
|
|
+ dvscntr |= DVS_TRICKLE_FEED_DISABLE;
|
|
|
+ dvscntr |= DVS_ENABLE;
|
|
|
+
|
|
|
+ /* Sizes are 0 based */
|
|
|
+ src_w--;
|
|
|
+ src_h--;
|
|
|
+ crtc_w--;
|
|
|
+ crtc_h--;
|
|
|
+
|
|
|
+ intel_update_sprite_watermarks(dev, pipe, crtc_w, pixel_size);
|
|
|
+
|
|
|
+ if (crtc_w != src_w || crtc_h != src_h)
|
|
|
+ dvsscale = DVS_SCALE_ENABLE | (src_w << 16) | src_h;
|
|
|
+
|
|
|
+ I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]);
|
|
|
+ I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x);
|
|
|
+ if (obj->tiling_mode != I915_TILING_NONE) {
|
|
|
+ I915_WRITE(DVSTILEOFF(pipe), (y << 16) | x);
|
|
|
+ } else {
|
|
|
+ unsigned long offset;
|
|
|
+
|
|
|
+ offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8);
|
|
|
+ I915_WRITE(DVSLINOFF(pipe), offset);
|
|
|
+ }
|
|
|
+ I915_WRITE(DVSSIZE(pipe), (crtc_h << 16) | crtc_w);
|
|
|
+ I915_WRITE(DVSSCALE(pipe), dvsscale);
|
|
|
+ I915_WRITE(DVSCNTR(pipe), dvscntr);
|
|
|
+ I915_WRITE(DVSSURF(pipe), obj->gtt_offset);
|
|
|
+ POSTING_READ(DVSSURF(pipe));
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+snb_disable_plane(struct drm_plane *plane)
|
|
|
+{
|
|
|
+ struct drm_device *dev = plane->dev;
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ struct intel_plane *intel_plane = to_intel_plane(plane);
|
|
|
+ int pipe = intel_plane->pipe;
|
|
|
+
|
|
|
+ I915_WRITE(DVSCNTR(pipe), I915_READ(DVSCNTR(pipe)) & ~DVS_ENABLE);
|
|
|
+ /* Disable the scaler */
|
|
|
+ I915_WRITE(DVSSCALE(pipe), 0);
|
|
|
+ /* Flush double buffered register updates */
|
|
|
+ I915_WRITE(DVSSURF(pipe), 0);
|
|
|
+ POSTING_READ(DVSSURF(pipe));
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
|
|
|
+ struct drm_framebuffer *fb, int crtc_x, int crtc_y,
|
|
|
+ unsigned int crtc_w, unsigned int crtc_h,
|
|
|
+ uint32_t src_x, uint32_t src_y,
|
|
|
+ uint32_t src_w, uint32_t src_h)
|
|
|
+{
|
|
|
+ struct drm_device *dev = plane->dev;
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ struct intel_crtc *intel_crtc = to_intel_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 ret = 0;
|
|
|
+ int x = src_x >> 16, y = src_y >> 16;
|
|
|
+ int primary_w = crtc->mode.hdisplay, primary_h = crtc->mode.vdisplay;
|
|
|
+ bool disable_primary = false;
|
|
|
+
|
|
|
+ intel_fb = to_intel_framebuffer(fb);
|
|
|
+ obj = intel_fb->obj;
|
|
|
+
|
|
|
+ old_obj = intel_plane->obj;
|
|
|
+
|
|
|
+ /* Pipe must be running... */
|
|
|
+ if (!(I915_READ(PIPECONF(pipe)) & PIPECONF_ENABLE))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (crtc_x >= primary_w || crtc_y >= primary_h)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* Don't modify another pipe's plane */
|
|
|
+ if (intel_plane->pipe != intel_crtc->pipe)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Clamp the width & height into the visible area. Note we don't
|
|
|
+ * try to scale the source if part of the visible region is offscreen.
|
|
|
+ * The caller must handle that by adjusting source offset and size.
|
|
|
+ */
|
|
|
+ if ((crtc_x < 0) && ((crtc_x + crtc_w) > 0)) {
|
|
|
+ crtc_w += crtc_x;
|
|
|
+ crtc_x = 0;
|
|
|
+ }
|
|
|
+ if ((crtc_x + crtc_w) <= 0) /* Nothing to display */
|
|
|
+ goto out;
|
|
|
+ if ((crtc_x + crtc_w) > primary_w)
|
|
|
+ crtc_w = primary_w - crtc_x;
|
|
|
+
|
|
|
+ if ((crtc_y < 0) && ((crtc_y + crtc_h) > 0)) {
|
|
|
+ crtc_h += crtc_y;
|
|
|
+ crtc_y = 0;
|
|
|
+ }
|
|
|
+ if ((crtc_y + crtc_h) <= 0) /* Nothing to display */
|
|
|
+ goto out;
|
|
|
+ if (crtc_y + crtc_h > primary_h)
|
|
|
+ crtc_h = primary_h - crtc_y;
|
|
|
+
|
|
|
+ if (!crtc_w || !crtc_h) /* Again, nothing to display */
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We can take a larger source and scale it down, but
|
|
|
+ * only so much... 16x is the max on SNB.
|
|
|
+ */
|
|
|
+ if (((src_w * src_h) / (crtc_w * crtc_h)) > intel_plane->max_downscale)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the sprite is completely covering the primary plane,
|
|
|
+ * we can disable the primary and save power.
|
|
|
+ */
|
|
|
+ if ((crtc_x == 0) && (crtc_y == 0) &&
|
|
|
+ (crtc_w == primary_w) && (crtc_h == primary_h))
|
|
|
+ disable_primary = true;
|
|
|
+
|
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
|
+
|
|
|
+ ret = intel_pin_and_fence_fb_obj(dev, obj, NULL);
|
|
|
+ if (ret) {
|
|
|
+ DRM_ERROR("failed to pin object\n");
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ intel_plane->obj = obj;
|
|
|
+
|
|
|
+ intel_plane->update_plane(plane, fb, obj, crtc_x, crtc_y,
|
|
|
+ crtc_w, crtc_h, x, y, src_w, src_h);
|
|
|
+
|
|
|
+ /* Unpin old obj after new one is active to avoid ugliness */
|
|
|
+ if (old_obj) {
|
|
|
+ /*
|
|
|
+ * It's fairly common to simply update the position of
|
|
|
+ * an existing object. In that case, we don't need to
|
|
|
+ * wait for vblank to avoid ugliness, we only need to
|
|
|
+ * do the pin & ref bookkeeping.
|
|
|
+ */
|
|
|
+ if (old_obj != obj) {
|
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
|
+ intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe);
|
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
|
+ }
|
|
|
+ i915_gem_object_unpin(old_obj);
|
|
|
+ }
|
|
|
+
|
|
|
+out_unlock:
|
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
|
+out:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+intel_disable_plane(struct drm_plane *plane)
|
|
|
+{
|
|
|
+ struct drm_device *dev = plane->dev;
|
|
|
+ struct intel_plane *intel_plane = to_intel_plane(plane);
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ intel_plane->disable_plane(plane);
|
|
|
+
|
|
|
+ if (!intel_plane->obj)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
|
+ i915_gem_object_unpin(intel_plane->obj);
|
|
|
+ intel_plane->obj = NULL;
|
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
|
+out:
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void intel_destroy_plane(struct drm_plane *plane)
|
|
|
+{
|
|
|
+ struct intel_plane *intel_plane = to_intel_plane(plane);
|
|
|
+ intel_disable_plane(plane);
|
|
|
+ drm_plane_cleanup(plane);
|
|
|
+ kfree(intel_plane);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct drm_plane_funcs intel_plane_funcs = {
|
|
|
+ .update_plane = intel_update_plane,
|
|
|
+ .disable_plane = intel_disable_plane,
|
|
|
+ .destroy = intel_destroy_plane,
|
|
|
+};
|
|
|
+
|
|
|
+static uint32_t snb_plane_formats[] = {
|
|
|
+ DRM_FORMAT_XBGR8888,
|
|
|
+ DRM_FORMAT_XRGB8888,
|
|
|
+ DRM_FORMAT_YUYV,
|
|
|
+ DRM_FORMAT_YVYU,
|
|
|
+ DRM_FORMAT_UYVY,
|
|
|
+ DRM_FORMAT_VYUY,
|
|
|
+};
|
|
|
+
|
|
|
+int
|
|
|
+intel_plane_init(struct drm_device *dev, enum pipe pipe)
|
|
|
+{
|
|
|
+ struct intel_plane *intel_plane;
|
|
|
+ unsigned long possible_crtcs;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!(IS_GEN6(dev) || IS_GEN7(dev))) {
|
|
|
+ DRM_ERROR("new plane code only for SNB+\n");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+
|
|
|
+ intel_plane = kzalloc(sizeof(struct intel_plane), GFP_KERNEL);
|
|
|
+ if (!intel_plane)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ if (IS_GEN6(dev)) {
|
|
|
+ intel_plane->max_downscale = 16;
|
|
|
+ intel_plane->update_plane = snb_update_plane;
|
|
|
+ intel_plane->disable_plane = snb_disable_plane;
|
|
|
+ } else if (IS_GEN7(dev)) {
|
|
|
+ intel_plane->max_downscale = 2;
|
|
|
+ intel_plane->update_plane = ivb_update_plane;
|
|
|
+ intel_plane->disable_plane = ivb_disable_plane;
|
|
|
+ }
|
|
|
+
|
|
|
+ intel_plane->pipe = pipe;
|
|
|
+ possible_crtcs = (1 << pipe);
|
|
|
+ ret = drm_plane_init(dev, &intel_plane->base, possible_crtcs,
|
|
|
+ &intel_plane_funcs, snb_plane_formats,
|
|
|
+ ARRAY_SIZE(snb_plane_formats));
|
|
|
+ if (ret)
|
|
|
+ kfree(intel_plane);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|