/*
*/
/*
* Copyright (c) 2012, 2013, Intel Corporation. All rights reserved.
*/
/*
* Copyright © 2009
*
* 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,
* 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:
* Daniel Vetter <daniel@ffwll.ch>
*
* Derived from Xorg ddx, xf86-video-intel, src/i830_video.c
*/
#include "drmP.h"
#include "drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
#include "i915_reg.h"
#include "intel_drv.h"
/* Limits for overlay size. According to intel doc, the real limits are:
* Y width: 4095, UV width (planar): 2047, Y height: 2047,
* UV width (planar): * 1023. But the xorg thinks 2048 for height and width. Use
* the mininum of both. */
/* on 830 and 845 these large limits result in the card hanging */
/* overlay register definitions */
/* OCMD register */
/* OCONFIG register */
/* DCLRKM (dst-key) register */
#define RGB16_TO_COLORKEY(c) \
(((c & 0xF800) << 8) | ((c & 0x07E0) << 5) | ((c & 0x001F) << 3))
#define RGB15_TO_COLORKEY(c) \
(((c & 0x7c00) << 9) | ((c & 0x03E0) << 6) | ((c & 0x001F) << 3))
/* overlay flip addr flag */
/* polyphase filter coefficients */
/* memory bufferd overlay registers */
struct overlay_registers {
};
struct intel_overlay {
int active;
int pfit_active;
/* register access */
/* flip handling */
};
static struct overlay_registers *
{
else
return regs;
}
struct overlay_registers *regs)
{
}
void (*tail)(struct intel_overlay *))
{
int ret;
if (ret)
return ret;
if (ret)
return ret;
overlay->last_flip_req = 0;
return 0;
}
/* overlay needs to be disable in OCMD reg */
{
int ret;
if (ret)
return ret;
}
/* overlay needs to be enabled in OCMD reg */
bool load_polyphase_filter)
{
int ret;
flip_addr |= OFC_UPDATE;
/* check for underruns */
if (ret)
return ret;
}
{
}
{
/* never have the overlay hw on without showing a frame */
}
/* overlay needs to be disabled in OCMD reg */
{
int ret;
/* According to intel docs the overlay hw may hang (when switching
* off) without loading the filter coeffs. It is however unclear whether
* this applies to the disabling of the overlay or to the switching off
* of the hw. Do it in both cases */
flip_addr |= OFC_UPDATE;
if (ret)
return ret;
/* wait for overlay to go idle */
/* turn overlay off */
/* Workaround: Don't disable the overlay fully, since otherwise
* it dies on the next OVERLAY_ON cmd. */
} else {
}
}
/* recover from an interruption due to a signal
* We have to be careful not to repeat work forever an make forward progess. */
{
int ret;
if (overlay->last_flip_req == 0)
return 0;
if (ret)
return ret;
overlay->last_flip_req = 0;
return 0;
}
/* Wait for pending overlay flip and release old frame.
* Needs to be called before the overlay register are changed
* via intel_overlay_(un)map_regs
*/
{
int ret;
/* Only wait if there is actually an old frame to release to
* guarantee forward progress.
*/
if (!overlay->old_vid_bo)
return 0;
/* synchronous slowpath */
if (ret)
return ret;
if (ret)
return ret;
}
return 0;
}
struct put_image_params {
int format;
short dst_x;
short dst_y;
short dst_w;
short dst_h;
short src_w;
short src_scan_h;
short src_scan_w;
short src_h;
short stride_Y;
short stride_UV;
int offset_Y;
int offset_U;
int offset_V;
};
{
switch (format & I915_OVERLAY_DEPTH_MASK) {
case I915_OVERLAY_YUV422:
return 4;
case I915_OVERLAY_YUV411:
/* return 6; not implemented */
default:
return -EINVAL;
}
}
{
switch (format & I915_OVERLAY_DEPTH_MASK) {
case I915_OVERLAY_YUV422:
return width << 1;
default:
return -EINVAL;
}
}
{
switch (format & I915_OVERLAY_DEPTH_MASK) {
case I915_OVERLAY_YUV422:
case I915_OVERLAY_YUV420:
return 2;
case I915_OVERLAY_YUV411:
case I915_OVERLAY_YUV410:
return 4;
default:
return -EINVAL;
}
}
{
switch (format & I915_OVERLAY_DEPTH_MASK) {
case I915_OVERLAY_YUV420:
case I915_OVERLAY_YUV410:
return 2;
case I915_OVERLAY_YUV422:
case I915_OVERLAY_YUV411:
return 1;
default:
return -EINVAL;
}
}
{
mask = 0x1f;
shift = 5;
} else {
mask = 0x3f;
shift = 6;
}
ret <<= 1;
ret -=1;
return ret << 2;
}
0x3000, 0xb4a0, 0x1930, 0x1920, 0xb4a0,
0x3000, 0xb500, 0x19d0, 0x1880, 0xb440,
0x3000, 0xb540, 0x1a88, 0x2f80, 0xb3e0,
0x3000, 0xb580, 0x1b30, 0x2e20, 0xb380,
0x3000, 0xb5c0, 0x1bd8, 0x2cc0, 0xb320,
0x3020, 0xb5e0, 0x1c60, 0x2b80, 0xb2c0,
0x3020, 0xb5e0, 0x1cf8, 0x2a20, 0xb260,
0x3020, 0xb5e0, 0x1d80, 0x28e0, 0xb200,
0x3020, 0xb5c0, 0x1e08, 0x3f40, 0xb1c0,
0x3020, 0xb580, 0x1e78, 0x3ce0, 0xb160,
0x3040, 0xb520, 0x1ed8, 0x3aa0, 0xb120,
0x3040, 0xb4a0, 0x1f30, 0x3880, 0xb0e0,
0x3040, 0xb400, 0x1f78, 0x3680, 0xb0a0,
0x3020, 0xb340, 0x1fb8, 0x34a0, 0xb060,
0x3020, 0xb240, 0x1fe0, 0x32e0, 0xb040,
0x3020, 0xb140, 0x1ff8, 0x3160, 0xb020,
0xb000, 0x3000, 0x0800, 0x3000, 0xb000
};
0x3000, 0x1800, 0x1800, 0xb000, 0x18d0, 0x2e60,
0xb000, 0x1990, 0x2ce0, 0xb020, 0x1a68, 0x2b40,
0xb040, 0x1b20, 0x29e0, 0xb060, 0x1bd8, 0x2880,
0xb080, 0x1c88, 0x3e60, 0xb0a0, 0x1d28, 0x3c00,
0xb0c0, 0x1db8, 0x39e0, 0xb0e0, 0x1e40, 0x37e0,
0xb100, 0x1eb8, 0x3620, 0xb100, 0x1f18, 0x34a0,
0xb100, 0x1f68, 0x3360, 0xb0e0, 0x1fa8, 0x3240,
0xb0c0, 0x1fe0, 0x3140, 0xb060, 0x1ff0, 0x30a0,
0x3000, 0x0800, 0x3000
};
{
}
struct overlay_registers *regs,
struct put_image_params *params)
{
/* fixed point with a 12 bit shift */
bool scale_changed = false;
else
else
/*if (params->format & I915_OVERLAY_YUV_PLANAR) {*/
/* make the Y scale to UV scale ratio an exact multiply */
/*} else {
xscale_UV = 0;
yscale_UV = 0;
}*/
scale_changed = true;
if (scale_changed)
return scale_changed;
}
struct overlay_registers *regs)
{
case 8:
break;
case 16:
} else {
}
break;
case 24:
case 32:
break;
}
}
{
case I915_OVERLAY_YUV422:
break;
case I915_OVERLAY_YUV420:
break;
case I915_OVERLAY_YUV411:
case I915_OVERLAY_YUV410:
break;
}
} else { /* YUV packed */
case I915_OVERLAY_YUV422:
break;
case I915_OVERLAY_YUV411:
break;
}
case I915_OVERLAY_NO_SWAP:
break;
case I915_OVERLAY_UV_SWAP:
cmd |= OCMD_UV_SWAP;
break;
case I915_OVERLAY_Y_SWAP:
cmd |= OCMD_Y_SWAP;
break;
break;
}
}
return cmd;
}
struct drm_i915_gem_object *new_bo,
struct put_image_params *params)
{
bool scale_changed = false;
/* LINTED */
if (ret != 0)
return ret;
if (ret != 0)
return ret;
if (ret)
goto out_unpin;
if (!regs) {
goto out_unpin;
}
if (ret != 0)
goto out_unpin;
}
if (!regs) {
goto out_unpin;
}
else
}
if (ret)
goto out_unpin;
return 0;
return ret;
}
{
/* LINTED */
int ret;
if (ret != 0)
return ret;
return 0;
if (ret != 0)
return ret;
if (ret != 0)
return ret;
return 0;
}
struct intel_crtc *crtc)
{
return -EINVAL;
/* can't use the overlay with double wide pipe */
return -EINVAL;
return 0;
}
{
/* XXX: This is not the same logic as in the xorg driver, but more in
* line with the intel documentation for the i965
*/
/* on i965 use the PGM reg to read out the autoscaler values */
} else {
if (pfit_control & VERT_AUTO_SCALE)
else
}
}
struct drm_intel_overlay_put_image *rec)
{
return 0;
else
return -EINVAL;
}
{
/* downscaling limit is 8.0 */
if (tmp > 7)
return -EINVAL;
if (tmp > 7)
return -EINVAL;
return 0;
}
struct drm_intel_overlay_put_image *rec,
struct drm_i915_gem_object *new_bo)
{
int depth;
/* check src dimensions */
return -EINVAL;
} else {
return -EINVAL;
}
/* better safe than sorry, use 4 as the maximal subsampling ratio */
return -EINVAL;
/* check alignment constraints */
case I915_OVERLAY_RGB:
/* not implemented */
return -EINVAL;
case I915_OVERLAY_YUV_PACKED:
if (uv_vscale != 1)
return -EINVAL;
if (depth < 0)
return depth;
/* ignore UV planes */
/* check pixel alignment */
return -EINVAL;
break;
case I915_OVERLAY_YUV_PLANAR:
return -EINVAL;
/* no offset restrictions for planar formats */
break;
default:
return -EINVAL;
}
return -EINVAL;
/* stride checking */
stride_mask = 255;
else
stride_mask = 63;
return -EINVAL;
return -EINVAL;
4096 : 8192;
return -EINVAL;
/* check buffer dimensions */
case I915_OVERLAY_RGB:
case I915_OVERLAY_YUV_PACKED:
/* always 4 Y values per depth pixels */
return -EINVAL;
return -EINVAL;
break;
case I915_OVERLAY_YUV_PLANAR:
return -EINVAL;
return -EINVAL;
return -EINVAL;
return -EINVAL;
break;
}
return 0;
}
/**
* Return the pipe currently connected to the panel fitter,
* or -1 if the panel fitter is not present or not in use
*/
{
/* i830 doesn't have a panel fitter */
return -1;
/* See if the panel fitter is in use */
if ((pfit_control & PFIT_ENABLE) == 0)
return -1;
/* 965 can place panel fitter on either pipe */
/* older chips can only use pipe 1 */
return 1;
}
{
int ret;
/* No need to check for DRIVER_MODESET - we don't set it up then. */
if (!overlay) {
DRM_DEBUG("userspace bug: no overlay\n");
return -ENODEV;
}
return ret;
}
if (!params)
return -ENOMEM;
if (!drmmode_obj) {
goto out_free;
}
goto out_free;
}
if (new_bo->tiling_mode) {
DRM_ERROR("buffer used for overlay image can not be tiled\n");
goto out_unlock;
}
if (ret != 0)
goto out_unlock;
if (ret != 0)
goto out_unlock;
if (ret != 0)
goto out_unlock;
/* line too wide, i.e. one-line-mode */
} else
overlay->pfit_active = 0;
}
if (ret != 0)
goto out_unlock;
if (overlay->pfit_active) {
/* shifting right rounds downwards, so add 1 */
} else {
}
goto out_unlock;
}
if (ret != 0)
goto out_unlock;
/* Check scaling after src size to prevent a divide-by-zero. */
if (ret != 0)
goto out_unlock;
if (ret != 0)
goto out_unlock;
return 0;
return ret;
}
struct overlay_registers *regs)
{
}
{
int i;
return false;
for (i = 0; i < 3; i++) {
return false;
}
return true;
}
{
int i;
for (i = 0; i < 3; i++) {
return false;
}
return true;
}
{
return -EINVAL;
return -EINVAL;
return 0;
}
{
int ret;
/* No need to check for DRIVER_MODESET - we don't set it up then. */
if (!overlay) {
DRM_DEBUG("userspace bug: no overlay\n");
return -ENODEV;
}
}
} else {
goto out_unlock;
goto out_unlock;
goto out_unlock;
if (!regs) {
goto out_unlock;
}
goto out_unlock;
goto out_unlock;
}
if (ret)
goto out_unlock;
}
}
ret = 0;
return ret;
}
{
int ret;
if (!HAS_OVERLAY(dev))
return;
if (!overlay)
return;
DRM_ERROR("BUG");
goto out_free;
}
goto out_free;
if (OVERLAY_NEEDS_PHYSICAL(dev)) {
if (ret) {
DRM_ERROR("failed to attach phys overlay regs\n");
goto out_free_bo;
}
} else {
if (ret) {
DRM_ERROR("failed to pin overlay register bo\n");
goto out_free_bo;
}
if (ret) {
DRM_ERROR("failed to move overlay register bo into the GTT\n");
goto out_unpin_bo;
}
}
/* init all values */
if (!regs)
goto out_unpin_bo;
DRM_INFO("initialized overlay support\n");
return;
if (!OVERLAY_NEEDS_PHYSICAL(dev))
return;
}
{
return;
/* The bo's should be free'd by the generic code already.
* Furthermore modesetting teardown happens beforehand so the
* hardware should be off already */
}
#ifdef CONFIG_DEBUG_FS
struct intel_overlay_error_state {
unsigned long base;
};
static struct overlay_registers *
{
else
return regs;
}
struct overlay_registers *regs)
{
}
struct intel_overlay_error_state *
{
return NULL;
return NULL;
else
if (!regs)
goto err;
return error;
err:
return NULL;
}
void
{
DRM_ERROR("Overlay, status: 0x%08x, interrupt: 0x%08x\n",
DRM_ERROR(" Register file at 0x%08lx:\n",
P(OBUF_0Y);
P(OBUF_1Y);
P(OBUF_0U);
P(OBUF_0V);
P(OBUF_1U);
P(OBUF_1V);
P(OSTRIDE);
P(YRGB_VPH);
P(UV_VPH);
P(HORZ_PH);
P(INIT_PHS);
P(DWINPOS);
P(DWINSZ);
P(SWIDTH);
P(SWIDTHSW);
P(SHEIGHT);
P(YRGBSCALE);
P(UVSCALE);
P(OCLRC0);
P(OCLRC1);
P(DCLRKV);
P(DCLRKM);
P(SCLRKVH);
P(SCLRKVL);
P(SCLRKEN);
P(OCONFIG);
P(OCMD);
P(OSTART_0Y);
P(OSTART_1Y);
P(OSTART_0U);
P(OSTART_0V);
P(OSTART_1U);
P(OSTART_1V);
P(OTILEOFF_0Y);
P(OTILEOFF_1Y);
P(OTILEOFF_0U);
P(OTILEOFF_0V);
P(OTILEOFF_1U);
P(OTILEOFF_1V);
P(FASTHSCALE);
P(UVSCALEV);
#undef P
}
#endif