Skip to content

Commit

Permalink
staging: drm/imx: add drm plane support
Browse files Browse the repository at this point in the history
This patch adds support for a drm overlay plane on DI0 using the DP.
In principle, the overlay plane could also be used on DI1, but to switch
the overlay plane between display interfaces, the base planes would have
to be exchanged transparently while both display interfaces are inactive.

Signed-off-by: Philipp Zabel <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
pH5 authored and gregkh committed Oct 11, 2013
1 parent 6ee4d7f commit b8d181e
Show file tree
Hide file tree
Showing 8 changed files with 484 additions and 156 deletions.
2 changes: 1 addition & 1 deletion drivers/staging/imx-drm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o
obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
obj-$(CONFIG_DRM_IMX_FB_HELPER) += imx-fbdev.o
obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/
obj-$(CONFIG_DRM_IMX_IPUV3) += ipuv3-crtc.o
obj-$(CONFIG_DRM_IMX_IPUV3) += ipuv3-crtc.o ipuv3-plane.o
1 change: 0 additions & 1 deletion drivers/staging/imx-drm/TODO
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ TODO:

Missing features (not necessarily for moving out of staging):

- Add KMS plane support for CRTC driver
- Add i.MX6 HDMI support
- Add support for IC (Image converter)
- Add support for CSI (CMOS Sensor interface)
Expand Down
5 changes: 5 additions & 0 deletions drivers/staging/imx-drm/imx-drm-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ struct imx_drm_connector {
struct module *owner;
};

int imx_drm_crtc_id(struct imx_drm_crtc *crtc)
{
return crtc->pipe;
}

static void imx_drm_driver_lastclose(struct drm_device *drm)
{
struct imx_drm_device *imxdrm = drm->dev_private;
Expand Down
2 changes: 2 additions & 0 deletions drivers/staging/imx-drm/imx-drm.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ struct drm_fbdev_cma;
struct drm_framebuffer;
struct platform_device;

int imx_drm_crtc_id(struct imx_drm_crtc *crtc);

struct imx_drm_crtc_helper_funcs {
int (*enable_vblank)(struct drm_crtc *crtc);
void (*disable_vblank)(struct drm_crtc *crtc);
Expand Down
2 changes: 1 addition & 1 deletion drivers/staging/imx-drm/ipu-v3/ipu-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ static const struct ipu_platform_reg client_reg[] = {
.dc = 5,
.dp = IPU_DP_FLOW_SYNC_BG,
.dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
.dma[1] = -EINVAL,
.dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
},
.name = "imx-ipuv3-crtc",
}, {
Expand Down
198 changes: 45 additions & 153 deletions drivers/staging/imx-drm/ipuv3-crtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,25 @@
#include <drm/drm_crtc_helper.h>
#include <linux/fb.h>
#include <linux/clk.h>
#include <linux/errno.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_fb_cma_helper.h>

#include "ipu-v3/imx-ipu-v3.h"
#include "imx-drm.h"
#include "ipuv3-plane.h"

#define DRIVER_DESC "i.MX IPUv3 Graphics"

struct ipu_crtc {
struct device *dev;
struct drm_crtc base;
struct imx_drm_crtc *imx_crtc;
struct ipuv3_channel *ipu_ch;

/* plane[0] is the full plane, plane[1] is the partial plane */
struct ipu_plane *plane[2];

struct ipu_dc *dc;
struct ipu_dp *dp;
struct dmfc_channel *dmfc;
struct ipu_di *di;
int enabled;
struct drm_pending_vblank_event *page_flip_event;
Expand All @@ -54,35 +57,14 @@ struct ipu_crtc {

#define to_ipu_crtc(x) container_of(x, struct ipu_crtc, base)

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 mode->clock * 1000 / vtotal / htotal;
}

static int calc_bandwidth(struct drm_display_mode *mode, unsigned int vref)
{
return mode->hdisplay * mode->vdisplay * vref;
}

static void ipu_fb_enable(struct ipu_crtc *ipu_crtc)
{
if (ipu_crtc->enabled)
return;

ipu_di_enable(ipu_crtc->di);
ipu_dmfc_enable_channel(ipu_crtc->dmfc);
ipu_idmac_enable_channel(ipu_crtc->ipu_ch);
ipu_dc_enable_channel(ipu_crtc->dc);
if (ipu_crtc->dp)
ipu_dp_enable_channel(ipu_crtc->dp);
ipu_plane_enable(ipu_crtc->plane[0]);

ipu_crtc->enabled = 1;
}
Expand All @@ -92,12 +74,8 @@ static void ipu_fb_disable(struct ipu_crtc *ipu_crtc)
if (!ipu_crtc->enabled)
return;

if (ipu_crtc->dp)
ipu_dp_disable_channel(ipu_crtc->dp);
ipu_plane_disable(ipu_crtc->plane[0]);
ipu_dc_disable_channel(ipu_crtc->dc);
ipu_idmac_wait_busy(ipu_crtc->ipu_ch, 50);
ipu_idmac_disable_channel(ipu_crtc->ipu_ch);
ipu_dmfc_disable_channel(ipu_crtc->dmfc);
ipu_di_disable(ipu_crtc->di);

ipu_crtc->enabled = 0;
Expand Down Expand Up @@ -153,79 +131,22 @@ static const struct drm_crtc_funcs ipu_crtc_funcs = {
.page_flip = ipu_page_flip,
};

static int ipu_drm_set_base(struct drm_crtc *crtc, int x, int y)
{
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
struct drm_gem_cma_object *cma_obj;
struct drm_framebuffer *fb = crtc->fb;
unsigned long phys;

cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
if (!cma_obj) {
DRM_LOG_KMS("entry is null.\n");
return -EFAULT;
}

phys = cma_obj->paddr;
phys += x * (fb->bits_per_pixel >> 3);
phys += y * fb->pitches[0];

dev_dbg(ipu_crtc->dev, "%s: phys: 0x%lx\n", __func__, phys);
dev_dbg(ipu_crtc->dev, "%s: xy: %dx%d\n", __func__, x, y);

ipu_cpmem_set_stride(ipu_get_cpmem(ipu_crtc->ipu_ch), fb->pitches[0]);
ipu_cpmem_set_buffer(ipu_get_cpmem(ipu_crtc->ipu_ch),
0, phys);

return 0;
}

static int ipu_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *orig_mode,
struct drm_display_mode *mode,
int x, int y,
struct drm_framebuffer *old_fb)
{
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
struct drm_framebuffer *fb = ipu_crtc->base.fb;
int ret;
struct ipu_di_signal_cfg sig_cfg = {};
u32 out_pixel_fmt;
struct ipu_ch_param __iomem *cpmem = ipu_get_cpmem(ipu_crtc->ipu_ch);
int bpp;
u32 v4l2_fmt;

dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__,
mode->hdisplay);
dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__,
mode->vdisplay);

ipu_ch_param_zero(cpmem);

switch (fb->pixel_format) {
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_ARGB8888:
v4l2_fmt = V4L2_PIX_FMT_RGB32;
bpp = 32;
break;
case DRM_FORMAT_RGB565:
v4l2_fmt = V4L2_PIX_FMT_RGB565;
bpp = 16;
break;
case DRM_FORMAT_RGB888:
v4l2_fmt = V4L2_PIX_FMT_RGB24;
bpp = 24;
break;
case DRM_FORMAT_BGR888:
v4l2_fmt = V4L2_PIX_FMT_BGR24;
bpp = 24;
break;
default:
dev_err(ipu_crtc->dev, "unsupported pixel format 0x%08x\n",
fb->pixel_format);
return -EINVAL;
}

out_pixel_fmt = ipu_crtc->interface_pix_fmt;

if (mode->flags & DRM_MODE_FLAG_INTERLACE)
Expand Down Expand Up @@ -255,18 +176,6 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc,
sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin;
sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin;

if (ipu_crtc->dp) {
ret = ipu_dp_setup_channel(ipu_crtc->dp, IPUV3_COLORSPACE_RGB,
IPUV3_COLORSPACE_RGB);
if (ret) {
dev_err(ipu_crtc->dev,
"initializing display processor failed with %d\n",
ret);
return ret;
}
ipu_dp_set_global_alpha(ipu_crtc->dp, 1, 0, 1);
}

ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, sig_cfg.interlaced,
out_pixel_fmt, mode->hdisplay);
if (ret) {
Expand All @@ -283,30 +192,9 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc,
return ret;
}

ipu_cpmem_set_resolution(cpmem, mode->hdisplay, mode->vdisplay);
ipu_cpmem_set_fmt(cpmem, v4l2_fmt);
ipu_cpmem_set_high_priority(ipu_crtc->ipu_ch);

ret = ipu_dmfc_init_channel(ipu_crtc->dmfc, mode->hdisplay);
if (ret) {
dev_err(ipu_crtc->dev,
"initializing dmfc channel failed with %d\n",
ret);
return ret;
}

ret = ipu_dmfc_alloc_bandwidth(ipu_crtc->dmfc,
calc_bandwidth(mode, calc_vref(mode)), 64);
if (ret) {
dev_err(ipu_crtc->dev,
"allocating dmfc bandwidth failed with %d\n",
ret);
return ret;
}

ipu_drm_set_base(crtc, x, y);

return 0;
return ipu_plane_mode_set(ipu_crtc->plane[0], crtc, mode, crtc->fb,
0, 0, mode->hdisplay, mode->vdisplay,
x, y, mode->hdisplay, mode->vdisplay);
}

static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc)
Expand All @@ -330,7 +218,7 @@ static irqreturn_t ipu_irq_handler(int irq, void *dev_id)

if (ipu_crtc->newfb) {
ipu_crtc->newfb = NULL;
ipu_drm_set_base(&ipu_crtc->base, 0, 0);
ipu_plane_set_base(ipu_crtc->plane[0], ipu_crtc->base.fb, 0, 0);
ipu_crtc_handle_pageflip(ipu_crtc);
}

Expand Down Expand Up @@ -413,12 +301,8 @@ static const struct imx_drm_crtc_helper_funcs ipu_crtc_helper_funcs = {

static void ipu_put_resources(struct ipu_crtc *ipu_crtc)
{
if (!IS_ERR_OR_NULL(ipu_crtc->ipu_ch))
ipu_idmac_put(ipu_crtc->ipu_ch);
if (!IS_ERR_OR_NULL(ipu_crtc->dmfc))
ipu_dmfc_put(ipu_crtc->dmfc);
if (!IS_ERR_OR_NULL(ipu_crtc->dp))
ipu_dp_put(ipu_crtc->dp);
if (!IS_ERR_OR_NULL(ipu_crtc->dc))
ipu_dc_put(ipu_crtc->dc);
if (!IS_ERR_OR_NULL(ipu_crtc->di))
ipu_di_put(ipu_crtc->di);
}
Expand All @@ -429,32 +313,12 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc,
struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
int ret;

ipu_crtc->ipu_ch = ipu_idmac_get(ipu, pdata->dma[0]);
if (IS_ERR(ipu_crtc->ipu_ch)) {
ret = PTR_ERR(ipu_crtc->ipu_ch);
goto err_out;
}

ipu_crtc->dc = ipu_dc_get(ipu, pdata->dc);
if (IS_ERR(ipu_crtc->dc)) {
ret = PTR_ERR(ipu_crtc->dc);
goto err_out;
}

ipu_crtc->dmfc = ipu_dmfc_get(ipu, pdata->dma[0]);
if (IS_ERR(ipu_crtc->dmfc)) {
ret = PTR_ERR(ipu_crtc->dmfc);
goto err_out;
}

if (pdata->dp >= 0) {
ipu_crtc->dp = ipu_dp_get(ipu, pdata->dp);
if (IS_ERR(ipu_crtc->dp)) {
ret = PTR_ERR(ipu_crtc->dp);
goto err_out;
}
}

ipu_crtc->di = ipu_di_get(ipu, pdata->di);
if (IS_ERR(ipu_crtc->di)) {
ret = PTR_ERR(ipu_crtc->di);
Expand All @@ -472,7 +336,9 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
struct ipu_client_platformdata *pdata)
{
struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
int dp = -EINVAL;
int ret;
int id;

ret = ipu_get_resources(ipu_crtc, pdata);
if (ret) {
Expand All @@ -490,17 +356,42 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
goto err_put_resources;
}

ipu_crtc->irq = ipu_idmac_channel_irq(ipu, ipu_crtc->ipu_ch,
IPU_IRQ_EOF);
if (pdata->dp >= 0)
dp = IPU_DP_FLOW_SYNC_BG;
id = imx_drm_crtc_id(ipu_crtc->imx_crtc);
ipu_crtc->plane[0] = ipu_plane_init(ipu_crtc->base.dev, ipu,
pdata->dma[0], dp, BIT(id), true);
ret = ipu_plane_get_resources(ipu_crtc->plane[0]);
if (ret) {
dev_err(ipu_crtc->dev, "getting plane 0 resources failed with %d.\n",
ret);
goto err_remove_crtc;
}

/* If this crtc is using the DP, add an overlay plane */
if (pdata->dp >= 0 && pdata->dma[1] > 0) {
ipu_crtc->plane[1] = ipu_plane_init(ipu_crtc->base.dev, ipu,
pdata->dma[1],
IPU_DP_FLOW_SYNC_FG,
BIT(id), false);
if (IS_ERR(ipu_crtc->plane[1]))
ipu_crtc->plane[1] = NULL;
}

ipu_crtc->irq = ipu_plane_irq(ipu_crtc->plane[0]);
ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0,
"imx_drm", ipu_crtc);
if (ret < 0) {
dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret);
goto err_put_resources;
goto err_put_plane_res;
}

return 0;

err_put_plane_res:
ipu_plane_put_resources(ipu_crtc->plane[0]);
err_remove_crtc:
imx_drm_remove_crtc(ipu_crtc->imx_crtc);
err_put_resources:
ipu_put_resources(ipu_crtc);

Expand Down Expand Up @@ -539,6 +430,7 @@ static int ipu_drm_remove(struct platform_device *pdev)

imx_drm_remove_crtc(ipu_crtc->imx_crtc);

ipu_plane_put_resources(ipu_crtc->plane[0]);
ipu_put_resources(ipu_crtc);

return 0;
Expand Down
Loading

0 comments on commit b8d181e

Please sign in to comment.