forked from Minki/linux
dab363f938
Here's the big staging tree pull request for 3.19-rc1. We continued to delete more lines than were added, always a good thing, but not at a huge rate this release, only about 70k lines removed overall mostly from removing the horrid bcm driver. Lots of normal staging driver cleanups and fixes all over the place, well over a thousand of them, the shortlog shows all the horrid details. The "contentious" thing here is the movement of the Android binder code out of staging into the "real" part of the kernel. This is code that has been stable for a few years now and is working as-is in the tens of millions of devices with no issues. Yes, the code is horrid, and the userspace api leaves a lot to be desired, but it's not going to change due to legacy issues that we have no control over. Because so many devices and companies rely on this, and the code is stable, might as well promote it out of staging. This was all discussed at the Linux Plumbers conference, and everyone participating agreed that this was the best way forward. There is work happening to replace the binder code with something new that is happening right now, but I don't expect to see the results of that work for another year at the earliest. If that ever happens, and Android switches over to it, I'll gladly remove this version. As for maintainers, I'll be glad to maintain this code, I've been doing it for the past few years with no problems. I'll send a MAINTAINERS entry for it before 3.19-final is out, still need to talk to the Google developers about if they are willing to help with it or not, last I checked they were, which was good. All of these patches have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlSPICkACgkQMUfUDdst+yksdwCfSLE9VUy1o2sAPDRe+J3bQced EWEAoL3RtnejKbo5tHS2IT69pLrwiIDS =YXyM -----END PGP SIGNATURE----- Merge tag 'staging-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging driver updates from Greg KH: "Here's the big staging tree pull request for 3.19-rc1. We continued to delete more lines than were added, always a good thing, but not at a huge rate this release, only about 70k lines removed overall mostly from removing the horrid bcm driver. Lots of normal staging driver cleanups and fixes all over the place, well over a thousand of them, the shortlog shows all the horrid details. The "contentious" thing here is the movement of the Android binder code out of staging into the "real" part of the kernel. This is code that has been stable for a few years now and is working as-is in the tens of millions of devices with no issues. Yes, the code is horrid, and the userspace api leaves a lot to be desired, but it's not going to change due to legacy issues that we have no control over. Because so many devices and companies rely on this, and the code is stable, might as well promote it out of staging. This was all discussed at the Linux Plumbers conference, and everyone participating agreed that this was the best way forward. There is work happening to replace the binder code with something new that is happening right now, but I don't expect to see the results of that work for another year at the earliest. If that ever happens, and Android switches over to it, I'll gladly remove this version. As for maintainers, I'll be glad to maintain this code, I've been doing it for the past few years with no problems. I'll send a MAINTAINERS entry for it before 3.19-final is out, still need to talk to the Google developers about if they are willing to help with it or not, last I checked they were, which was good. All of these patches have been in linux-next for a while with no reported issues" * tag 'staging-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1382 commits) Staging: slicoss: Fix long line issues in slicoss.c staging: rtl8712: remove unnecessary else after return staging: comedi: change some printk calls to pr_err staging: rtl8723au: hal: Removed the extra semicolon lustre: Deletion of unnecessary checks before three function calls staging: lustre: fix sparse warnings: static function declaration staging: lustre: fixed sparse warnings related to static declarations staging: unisys: remove duplicate header staging: unisys: remove unneeded structure staging: ft1000 : replace __attribute ((__packed__) with __packed drivers: staging: rtl8192e: Include "asm/unaligned.h" instead of "access_ok.h" in "rtl819x_BAProc.c" Drivers:staging:rtl8192e: Fixed checkpatch warning Drivers:staging:clocking-wizard: Added a newline staging: clocking-wizard: check for a valid clk_name pointer staging: rtl8723au: Hal_InitPGData() avoid unnecessary typecasts staging: rtl8723au: _DisableAnalog(): Avoid zero-init variables unnecessarily staging: rtl8723au: Remove unnecessary wrapper _ResetDigitalProcedure1() staging: rtl8723au: _ResetDigitalProcedure1_92C() reduce code obfuscation staging: rtl8723au: Remove unnecessary wrapper _DisableRFAFEAndResetBB() staging: rtl8723au: _DisableRFAFEAndResetBB8192C(): Reduce code obfuscation ...
395 lines
9.6 KiB
C
395 lines
9.6 KiB
C
/*
|
|
* i.MX IPUv3 DP Overlay Planes
|
|
*
|
|
* Copyright (C) 2013 Philipp Zabel, Pengutronix
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <drm/drmP.h>
|
|
#include <drm/drm_fb_cma_helper.h>
|
|
#include <drm/drm_gem_cma_helper.h>
|
|
|
|
#include "video/imx-ipu-v3.h"
|
|
#include "ipuv3-plane.h"
|
|
|
|
#define to_ipu_plane(x) container_of(x, struct ipu_plane, base)
|
|
|
|
static const uint32_t ipu_plane_formats[] = {
|
|
DRM_FORMAT_XRGB1555,
|
|
DRM_FORMAT_XBGR1555,
|
|
DRM_FORMAT_ARGB8888,
|
|
DRM_FORMAT_XRGB8888,
|
|
DRM_FORMAT_ABGR8888,
|
|
DRM_FORMAT_XBGR8888,
|
|
DRM_FORMAT_YUYV,
|
|
DRM_FORMAT_YVYU,
|
|
DRM_FORMAT_YUV420,
|
|
DRM_FORMAT_YVU420,
|
|
};
|
|
|
|
int ipu_plane_irq(struct ipu_plane *ipu_plane)
|
|
{
|
|
return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch,
|
|
IPU_IRQ_EOF);
|
|
}
|
|
|
|
static int calc_vref(struct drm_display_mode *mode)
|
|
{
|
|
unsigned long htotal, vtotal;
|
|
|
|
htotal = mode->htotal;
|
|
vtotal = mode->vtotal;
|
|
|
|
if (!htotal || !vtotal)
|
|
return 60;
|
|
|
|
return DIV_ROUND_UP(mode->clock * 1000, vtotal * htotal);
|
|
}
|
|
|
|
static inline int calc_bandwidth(int width, int height, unsigned int vref)
|
|
{
|
|
return width * height * vref;
|
|
}
|
|
|
|
int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
|
|
int x, int y)
|
|
{
|
|
struct drm_gem_cma_object *cma_obj;
|
|
unsigned long eba;
|
|
int active;
|
|
|
|
cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
|
|
if (!cma_obj) {
|
|
DRM_DEBUG_KMS("entry is null.\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d",
|
|
&cma_obj->paddr, x, y);
|
|
|
|
eba = cma_obj->paddr + fb->offsets[0] +
|
|
fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x;
|
|
|
|
if (ipu_plane->enabled) {
|
|
active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
|
|
ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba);
|
|
ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active);
|
|
} else {
|
|
ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba);
|
|
ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba);
|
|
}
|
|
|
|
/* cache offsets for subsequent pageflips */
|
|
ipu_plane->x = x;
|
|
ipu_plane->y = y;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
|
|
struct drm_display_mode *mode,
|
|
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 device *dev = ipu_plane->base.dev->dev;
|
|
int ret;
|
|
|
|
/* no scaling */
|
|
if (src_w != crtc_w || src_h != crtc_h)
|
|
return -EINVAL;
|
|
|
|
/* clip to crtc bounds */
|
|
if (crtc_x < 0) {
|
|
if (-crtc_x > crtc_w)
|
|
return -EINVAL;
|
|
src_x += -crtc_x;
|
|
src_w -= -crtc_x;
|
|
crtc_w -= -crtc_x;
|
|
crtc_x = 0;
|
|
}
|
|
if (crtc_y < 0) {
|
|
if (-crtc_y > crtc_h)
|
|
return -EINVAL;
|
|
src_y += -crtc_y;
|
|
src_h -= -crtc_y;
|
|
crtc_h -= -crtc_y;
|
|
crtc_y = 0;
|
|
}
|
|
if (crtc_x + crtc_w > mode->hdisplay) {
|
|
if (crtc_x > mode->hdisplay)
|
|
return -EINVAL;
|
|
crtc_w = mode->hdisplay - crtc_x;
|
|
src_w = crtc_w;
|
|
}
|
|
if (crtc_y + crtc_h > mode->vdisplay) {
|
|
if (crtc_y > mode->vdisplay)
|
|
return -EINVAL;
|
|
crtc_h = mode->vdisplay - crtc_y;
|
|
src_h = crtc_h;
|
|
}
|
|
/* full plane minimum width is 13 pixels */
|
|
if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG))
|
|
return -EINVAL;
|
|
if (crtc_h < 2)
|
|
return -EINVAL;
|
|
|
|
/*
|
|
* since we cannot touch active IDMAC channels, we do not support
|
|
* resizing the enabled plane or changing its format
|
|
*/
|
|
if (ipu_plane->enabled) {
|
|
if (src_w != ipu_plane->w || src_h != ipu_plane->h ||
|
|
fb->pixel_format != ipu_plane->base.fb->pixel_format)
|
|
return -EINVAL;
|
|
|
|
return ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
|
|
}
|
|
|
|
switch (ipu_plane->dp_flow) {
|
|
case IPU_DP_FLOW_SYNC_BG:
|
|
ret = ipu_dp_setup_channel(ipu_plane->dp,
|
|
IPUV3_COLORSPACE_RGB,
|
|
IPUV3_COLORSPACE_RGB);
|
|
if (ret) {
|
|
dev_err(dev,
|
|
"initializing display processor failed with %d\n",
|
|
ret);
|
|
return ret;
|
|
}
|
|
ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
|
|
break;
|
|
case IPU_DP_FLOW_SYNC_FG:
|
|
ipu_dp_setup_channel(ipu_plane->dp,
|
|
ipu_drm_fourcc_to_colorspace(fb->pixel_format),
|
|
IPUV3_COLORSPACE_UNKNOWN);
|
|
ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y);
|
|
/* Enable local alpha on partial plane */
|
|
switch (fb->pixel_format) {
|
|
case DRM_FORMAT_ARGB8888:
|
|
case DRM_FORMAT_ABGR8888:
|
|
ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
ret = ipu_dmfc_init_channel(ipu_plane->dmfc, crtc_w);
|
|
if (ret) {
|
|
dev_err(dev, "initializing dmfc channel failed with %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc,
|
|
calc_bandwidth(crtc_w, crtc_h,
|
|
calc_vref(mode)), 64);
|
|
if (ret) {
|
|
dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ipu_cpmem_zero(ipu_plane->ipu_ch);
|
|
ipu_cpmem_set_resolution(ipu_plane->ipu_ch, src_w, src_h);
|
|
ret = ipu_cpmem_set_fmt(ipu_plane->ipu_ch, fb->pixel_format);
|
|
if (ret < 0) {
|
|
dev_err(dev, "unsupported pixel format 0x%08x\n",
|
|
fb->pixel_format);
|
|
return ret;
|
|
}
|
|
ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
|
|
ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1);
|
|
ipu_cpmem_set_stride(ipu_plane->ipu_ch, fb->pitches[0]);
|
|
|
|
ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ipu_plane->w = src_w;
|
|
ipu_plane->h = src_h;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
|
|
{
|
|
if (!IS_ERR_OR_NULL(ipu_plane->dp))
|
|
ipu_dp_put(ipu_plane->dp);
|
|
if (!IS_ERR_OR_NULL(ipu_plane->dmfc))
|
|
ipu_dmfc_put(ipu_plane->dmfc);
|
|
if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch))
|
|
ipu_idmac_put(ipu_plane->ipu_ch);
|
|
}
|
|
|
|
int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
|
|
{
|
|
int ret;
|
|
|
|
ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma);
|
|
if (IS_ERR(ipu_plane->ipu_ch)) {
|
|
ret = PTR_ERR(ipu_plane->ipu_ch);
|
|
DRM_ERROR("failed to get idmac channel: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma);
|
|
if (IS_ERR(ipu_plane->dmfc)) {
|
|
ret = PTR_ERR(ipu_plane->dmfc);
|
|
DRM_ERROR("failed to get dmfc: ret %d\n", ret);
|
|
goto err_out;
|
|
}
|
|
|
|
if (ipu_plane->dp_flow >= 0) {
|
|
ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow);
|
|
if (IS_ERR(ipu_plane->dp)) {
|
|
ret = PTR_ERR(ipu_plane->dp);
|
|
DRM_ERROR("failed to get dp flow: %d\n", ret);
|
|
goto err_out;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
err_out:
|
|
ipu_plane_put_resources(ipu_plane);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void ipu_plane_enable(struct ipu_plane *ipu_plane)
|
|
{
|
|
if (ipu_plane->dp)
|
|
ipu_dp_enable(ipu_plane->ipu);
|
|
ipu_dmfc_enable_channel(ipu_plane->dmfc);
|
|
ipu_idmac_enable_channel(ipu_plane->ipu_ch);
|
|
if (ipu_plane->dp)
|
|
ipu_dp_enable_channel(ipu_plane->dp);
|
|
|
|
ipu_plane->enabled = true;
|
|
}
|
|
|
|
void ipu_plane_disable(struct ipu_plane *ipu_plane)
|
|
{
|
|
ipu_plane->enabled = false;
|
|
|
|
ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);
|
|
|
|
if (ipu_plane->dp)
|
|
ipu_dp_disable_channel(ipu_plane->dp);
|
|
ipu_idmac_disable_channel(ipu_plane->ipu_ch);
|
|
ipu_dmfc_disable_channel(ipu_plane->dmfc);
|
|
if (ipu_plane->dp)
|
|
ipu_dp_disable(ipu_plane->ipu);
|
|
}
|
|
|
|
/*
|
|
* drm_plane API
|
|
*/
|
|
|
|
static int ipu_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 ipu_plane *ipu_plane = to_ipu_plane(plane);
|
|
int ret = 0;
|
|
|
|
DRM_DEBUG_KMS("plane - %p\n", plane);
|
|
|
|
if (!ipu_plane->enabled)
|
|
ret = ipu_plane_get_resources(ipu_plane);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = ipu_plane_mode_set(ipu_plane, crtc, &crtc->hwmode, fb,
|
|
crtc_x, crtc_y, crtc_w, crtc_h,
|
|
src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16);
|
|
if (ret < 0) {
|
|
ipu_plane_put_resources(ipu_plane);
|
|
return ret;
|
|
}
|
|
|
|
if (crtc != plane->crtc)
|
|
dev_info(plane->dev->dev, "crtc change: %p -> %p\n",
|
|
plane->crtc, crtc);
|
|
plane->crtc = crtc;
|
|
|
|
if (!ipu_plane->enabled)
|
|
ipu_plane_enable(ipu_plane);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ipu_disable_plane(struct drm_plane *plane)
|
|
{
|
|
struct ipu_plane *ipu_plane = to_ipu_plane(plane);
|
|
|
|
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
|
|
|
|
if (ipu_plane->enabled)
|
|
ipu_plane_disable(ipu_plane);
|
|
|
|
ipu_plane_put_resources(ipu_plane);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ipu_plane_destroy(struct drm_plane *plane)
|
|
{
|
|
struct ipu_plane *ipu_plane = to_ipu_plane(plane);
|
|
|
|
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
|
|
|
|
ipu_disable_plane(plane);
|
|
drm_plane_cleanup(plane);
|
|
kfree(ipu_plane);
|
|
}
|
|
|
|
static struct drm_plane_funcs ipu_plane_funcs = {
|
|
.update_plane = ipu_update_plane,
|
|
.disable_plane = ipu_disable_plane,
|
|
.destroy = ipu_plane_destroy,
|
|
};
|
|
|
|
struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
|
|
int dma, int dp, unsigned int possible_crtcs,
|
|
bool priv)
|
|
{
|
|
struct ipu_plane *ipu_plane;
|
|
int ret;
|
|
|
|
DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n",
|
|
dma, dp, possible_crtcs);
|
|
|
|
ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL);
|
|
if (!ipu_plane) {
|
|
DRM_ERROR("failed to allocate plane\n");
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
ipu_plane->ipu = ipu;
|
|
ipu_plane->dma = dma;
|
|
ipu_plane->dp_flow = dp;
|
|
|
|
ret = drm_plane_init(dev, &ipu_plane->base, possible_crtcs,
|
|
&ipu_plane_funcs, ipu_plane_formats,
|
|
ARRAY_SIZE(ipu_plane_formats),
|
|
priv);
|
|
if (ret) {
|
|
DRM_ERROR("failed to initialize plane\n");
|
|
kfree(ipu_plane);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
return ipu_plane;
|
|
}
|