Hi,
This series fixes DMFC allocation for small displays and pageflip events during device close, clarifies the RGB memory formats, adds support for the RGB565 format and KMS planes, and enables DRM PRIME using the CMA helpers.
In the next step, I would like to move the code from drivers/staging/imx-drm/ipu-v3 to drivers/gpu/ipu-v3 (right next to host1x), as these parts provide an internal API for the IPU modules to be used by both DRM and V4L2 drivers.
regards Philipp
--- drivers/staging/imx-drm/Makefile | 2 +- drivers/staging/imx-drm/TODO | 1 - drivers/staging/imx-drm/imx-drm-core.c | 29 ++- drivers/staging/imx-drm/imx-drm.h | 2 + drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h | 4 +- drivers/staging/imx-drm/ipu-v3/ipu-common.c | 169 ++++++++++--- drivers/staging/imx-drm/ipu-v3/ipu-dc.c | 9 + drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c | 8 +- drivers/staging/imx-drm/ipuv3-crtc.c | 209 ++++------------ drivers/staging/imx-drm/ipuv3-plane.c | 375 ++++++++++++++++++++++++++++ drivers/staging/imx-drm/ipuv3-plane.h | 55 ++++ 11 files changed, 655 insertions(+), 208 deletions(-)
Connecting a 320x240 parallel display on i.MX6 resulted in an invalid DRDY signal because the DC would not receive NL/EOL events on every line. Reducing the allocated DMFC space from 4 slots (256 * 128-bit) to 2 slots (128 * 128-bit) solved the problem.
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c index 2e97c33..98070dd 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c @@ -307,13 +307,13 @@ int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc, goto out; }
- /* Always allocate at least 128*4 bytes (2 slots) */ - if (slots < 2) - slots = 2; - /* For the MEM_BG channel, first try to allocate twice the slots */ if (dmfc->data->ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC) segment = dmfc_find_slots(priv, slots * 2); + else if (slots < 2) + /* Always allocate at least 128*4 bytes (2 slots) */ + slots = 2; + if (segment >= 0) slots *= 2; else
Hi Philipp,
On 10/10/2013 10:18 PM, Philipp Zabel wrote:
Connecting a 320x240 parallel display on i.MX6 resulted in an invalid DRDY signal because the DC would not receive NL/EOL events on every line. Reducing the allocated DMFC space from 4 slots (256 * 128-bit) to 2 slots (128 * 128-bit) solved the problem.
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de
drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c index 2e97c33..98070dd 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c @@ -307,13 +307,13 @@ int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc, goto out; }
- /* Always allocate at least 128*4 bytes (2 slots) */
- if (slots < 2)
slots = 2;
- /* For the MEM_BG channel, first try to allocate twice the slots */ if (dmfc->data->ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC) segment = dmfc_find_slots(priv, slots * 2);
- else if (slots < 2)
/* Always allocate at least 128*4 bytes (2 slots) */
The commit message mentions that the size of 2 slots is 128*128-bit, that is, 128*16-byte. This contradicts the above annotation(128*4 bytes (2 slots)).
slots = 2;
- if (segment >= 0) slots *= 2; else
Regards, Liu Ying
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h | 2 +- drivers/staging/imx-drm/ipu-v3/ipu-common.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h index 74c022e..872cf62 100644 --- a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h +++ b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h @@ -283,7 +283,7 @@ int ipu_cpmem_set_format_passthrough(struct ipu_ch_param __iomem *p, int width);
int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *, - struct ipu_rgb *rgb); + const struct ipu_rgb *rgb);
static inline void ipu_cpmem_interlaced_scan(struct ipu_ch_param *p, int stride) diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c index ba464e5..8abd215 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-common.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c @@ -139,7 +139,7 @@ u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs) EXPORT_SYMBOL_GPL(ipu_ch_param_read_field);
int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *p, - struct ipu_rgb *rgb) + const struct ipu_rgb *rgb) { int bpp = 0, npb = 0, ro, go, bo, to;
@@ -282,7 +282,7 @@ void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format, } EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar);
-static struct ipu_rgb def_rgb_32 = { +static const struct ipu_rgb def_rgb_32 = { .red = { .offset = 16, .length = 8, }, .green = { .offset = 8, .length = 8, }, .blue = { .offset = 0, .length = 8, }, @@ -290,7 +290,7 @@ static struct ipu_rgb def_rgb_32 = { .bits_per_pixel = 32, };
-static struct ipu_rgb def_bgr_32 = { +static const struct ipu_rgb def_bgr_32 = { .red = { .offset = 16, .length = 8, }, .green = { .offset = 8, .length = 8, }, .blue = { .offset = 0, .length = 8, }, @@ -298,7 +298,7 @@ static struct ipu_rgb def_bgr_32 = { .bits_per_pixel = 32, };
-static struct ipu_rgb def_rgb_24 = { +static const struct ipu_rgb def_rgb_24 = { .red = { .offset = 0, .length = 8, }, .green = { .offset = 8, .length = 8, }, .blue = { .offset = 16, .length = 8, }, @@ -306,7 +306,7 @@ static struct ipu_rgb def_rgb_24 = { .bits_per_pixel = 24, };
-static struct ipu_rgb def_bgr_24 = { +static const struct ipu_rgb def_bgr_24 = { .red = { .offset = 16, .length = 8, }, .green = { .offset = 8, .length = 8, }, .blue = { .offset = 0, .length = 8, }, @@ -314,7 +314,7 @@ static struct ipu_rgb def_bgr_24 = { .bits_per_pixel = 24, };
-static struct ipu_rgb def_rgb_16 = { +static const struct ipu_rgb def_rgb_16 = { .red = { .offset = 11, .length = 5, }, .green = { .offset = 5, .length = 6, }, .blue = { .offset = 0, .length = 5, },
The drm fourccs define formats not available as video4linux pixel formats, such as DRM_FORMAT_BGR565, or the DRM_FORMAT_RGBX/BGRX variants. Also, contrary to the v4l2 formats, the drm formats are well defined.
This patch also fixes the BGRA32 and RGB/RGB24 internal formats to use a common internal representation.
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h | 1 + drivers/staging/imx-drm/ipu-v3/ipu-common.c | 121 +++++++++++++++++++++++----- 2 files changed, 100 insertions(+), 22 deletions(-)
diff --git a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h index 872cf62..9bee640 100644 --- a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h +++ b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h @@ -303,6 +303,7 @@ int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 pixelformat); int ipu_cpmem_set_image(struct ipu_ch_param __iomem *cpmem, struct ipu_image *image);
+enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc); enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat);
static inline void ipu_cpmem_set_burstsize(struct ipu_ch_param __iomem *p, diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c index 8abd215..54466df 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-common.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c @@ -30,6 +30,8 @@ #include <linux/irqdomain.h> #include <linux/of_device.h>
+#include <drm/drm_fourcc.h> + #include "imx-ipu-v3.h" #include "ipu-prv.h"
@@ -291,25 +293,25 @@ static const struct ipu_rgb def_rgb_32 = { };
static const struct ipu_rgb def_bgr_32 = { - .red = { .offset = 16, .length = 8, }, + .red = { .offset = 0, .length = 8, }, .green = { .offset = 8, .length = 8, }, - .blue = { .offset = 0, .length = 8, }, + .blue = { .offset = 16, .length = 8, }, .transp = { .offset = 24, .length = 8, }, .bits_per_pixel = 32, };
static const struct ipu_rgb def_rgb_24 = { - .red = { .offset = 0, .length = 8, }, + .red = { .offset = 16, .length = 8, }, .green = { .offset = 8, .length = 8, }, - .blue = { .offset = 16, .length = 8, }, + .blue = { .offset = 0, .length = 8, }, .transp = { .offset = 0, .length = 0, }, .bits_per_pixel = 24, };
static const struct ipu_rgb def_bgr_24 = { - .red = { .offset = 16, .length = 8, }, + .red = { .offset = 0, .length = 8, }, .green = { .offset = 8, .length = 8, }, - .blue = { .offset = 0, .length = 8, }, + .blue = { .offset = 16, .length = 8, }, .transp = { .offset = 0, .length = 0, }, .bits_per_pixel = 24, }; @@ -329,17 +331,17 @@ static const struct ipu_rgb def_rgb_16 = { (pix->width * pix->height / 4) + \ (pix->width * (y) / 4) + (x) / 2)
-int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 pixelformat) +int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 drm_fourcc) { - switch (pixelformat) { - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: + switch (drm_fourcc) { + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: /* pix format */ ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 2); /* burst size */ ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 63); break; - case V4L2_PIX_FMT_UYVY: + case DRM_FORMAT_UYVY: /* bits/pixel */ ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3); /* pix format */ @@ -347,7 +349,7 @@ int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 pixelformat) /* burst size */ ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31); break; - case V4L2_PIX_FMT_YUYV: + case DRM_FORMAT_YUYV: /* bits/pixel */ ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3); /* pix format */ @@ -355,20 +357,22 @@ int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 pixelformat) /* burst size */ ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31); break; - case V4L2_PIX_FMT_RGB32: - ipu_cpmem_set_format_rgb(cpmem, &def_rgb_32); + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XBGR8888: + ipu_cpmem_set_format_rgb(cpmem, &def_bgr_32); break; - case V4L2_PIX_FMT_RGB565: - ipu_cpmem_set_format_rgb(cpmem, &def_rgb_16); + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + ipu_cpmem_set_format_rgb(cpmem, &def_rgb_32); break; - case V4L2_PIX_FMT_BGR32: - ipu_cpmem_set_format_rgb(cpmem, &def_bgr_32); + case DRM_FORMAT_BGR888: + ipu_cpmem_set_format_rgb(cpmem, &def_bgr_24); break; - case V4L2_PIX_FMT_RGB24: + case DRM_FORMAT_RGB888: ipu_cpmem_set_format_rgb(cpmem, &def_rgb_24); break; - case V4L2_PIX_FMT_BGR24: - ipu_cpmem_set_format_rgb(cpmem, &def_bgr_24); + case DRM_FORMAT_RGB565: + ipu_cpmem_set_format_rgb(cpmem, &def_rgb_16); break; default: return -EINVAL; @@ -378,6 +382,79 @@ int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 pixelformat) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt);
+/* + * The V4L2 spec defines packed RGB formats in memory byte order, which from + * point of view of the IPU corresponds to little-endian words with the first + * component in the least significant bits. + * The DRM pixel formats and IPU internal representation are ordered the other + * way around, with the first named component ordered at the most significant + * bits. Further, V4L2 formats are not well defined: + * http://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html + * We choose the interpretation which matches GStreamer behavior. + */ +static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat) +{ + switch (pixelformat) { + case V4L2_PIX_FMT_RGB565: + /* + * Here we choose the 'corrected' interpretation of RGBP, a + * little-endian 16-bit word with the red component at the most + * significant bits: + * g[2:0]b[4:0] r[4:0]g[5:3] <=> [16:0] R:G:B + */ + return DRM_FORMAT_RGB565; + case V4L2_PIX_FMT_BGR24: + /* B G R <=> [24:0] R:G:B */ + return DRM_FORMAT_RGB888; + case V4L2_PIX_FMT_RGB24: + /* R G B <=> [24:0] B:G:R */ + return DRM_FORMAT_BGR888; + case V4L2_PIX_FMT_BGR32: + /* B G R A <=> [32:0] A:B:G:R */ + return DRM_FORMAT_XRGB8888; + case V4L2_PIX_FMT_RGB32: + /* R G B A <=> [32:0] A:B:G:R */ + return DRM_FORMAT_XBGR8888; + case V4L2_PIX_FMT_UYVY: + return DRM_FORMAT_UYVY; + case V4L2_PIX_FMT_YUYV: + return DRM_FORMAT_YUYV; + case V4L2_PIX_FMT_YUV420: + return DRM_FORMAT_YUV420; + case V4L2_PIX_FMT_YVU420: + return DRM_FORMAT_YVU420; + } + + return -EINVAL; +} + +enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) +{ + switch (drm_fourcc) { + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_BGR888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRA8888: + return IPUV3_COLORSPACE_RGB; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + return IPUV3_COLORSPACE_YUV; + default: + return IPUV3_COLORSPACE_UNKNOWN; + } +} +EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); + int ipu_cpmem_set_image(struct ipu_ch_param __iomem *cpmem, struct ipu_image *image) { @@ -392,7 +469,7 @@ int ipu_cpmem_set_image(struct ipu_ch_param __iomem *cpmem, image->rect.height); ipu_cpmem_set_stride(cpmem, pix->bytesperline);
- ipu_cpmem_set_fmt(cpmem, pix->pixelformat); + ipu_cpmem_set_fmt(cpmem, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat));
switch (pix->pixelformat) { case V4L2_PIX_FMT_YUV420:
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- drivers/staging/imx-drm/ipu-v3/ipu-common.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c index 54466df..f90bdb4 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-common.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c @@ -324,6 +324,14 @@ static const struct ipu_rgb def_rgb_16 = { .bits_per_pixel = 16, };
+static const struct ipu_rgb def_bgr_16 = { + .red = { .offset = 0, .length = 5, }, + .green = { .offset = 5, .length = 6, }, + .blue = { .offset = 11, .length = 5, }, + .transp = { .offset = 0, .length = 0, }, + .bits_per_pixel = 16, +}; + #define Y_OFFSET(pix, x, y) ((x) + pix->width * (y)) #define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \ (pix->width * (y) / 4) + (x) / 2) @@ -374,6 +382,9 @@ int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 drm_fourcc) case DRM_FORMAT_RGB565: ipu_cpmem_set_format_rgb(cpmem, &def_rgb_16); break; + case DRM_FORMAT_BGR565: + ipu_cpmem_set_format_rgb(cpmem, &def_bgr_16); + break; default: return -EINVAL; }
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- drivers/staging/imx-drm/ipu-v3/ipu-dc.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c index 21bf1c8..1a6e06d 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-dc.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c @@ -91,6 +91,7 @@ enum ipu_dc_map { IPU_DC_MAP_RGB565, IPU_DC_MAP_GBR24, /* TVEv2 */ IPU_DC_MAP_BGR666, + IPU_DC_MAP_BGR24, };
struct ipu_dc { @@ -152,6 +153,8 @@ static int ipu_pixfmt_to_map(u32 fmt) return IPU_DC_MAP_GBR24; case V4L2_PIX_FMT_BGR666: return IPU_DC_MAP_BGR666; + case V4L2_PIX_FMT_BGR24: + return IPU_DC_MAP_BGR24; default: return -EINVAL; } @@ -395,6 +398,12 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */
+ /* bgr24 */ + ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24); + ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */ + ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */ + ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */ + return 0; }
From: Sascha Hauer s.hauer@pengutronix.de
Currently we wait for a channel until it's idle before actually disabling it. This is not needed for all channels though, so make waiting for idle a separate function and call it where necessary.
Signed-off-by: Sascha Hauer s.hauer@pengutronix.de Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h | 1 + drivers/staging/imx-drm/ipu-v3/ipu-common.c | 23 ++++++++++++++--------- drivers/staging/imx-drm/ipuv3-crtc.c | 1 + 3 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h index 9bee640..4826b5c 100644 --- a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h +++ b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h @@ -97,6 +97,7 @@ void ipu_idmac_put(struct ipuv3_channel *);
int ipu_idmac_enable_channel(struct ipuv3_channel *channel); int ipu_idmac_disable_channel(struct ipuv3_channel *channel); +int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms);
void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, bool doublebuffer); diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c index f90bdb4..5ca42f0 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-common.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c @@ -698,24 +698,29 @@ int ipu_idmac_enable_channel(struct ipuv3_channel *channel) } EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
-int ipu_idmac_disable_channel(struct ipuv3_channel *channel) +int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) { struct ipu_soc *ipu = channel->ipu; - u32 val; - unsigned long flags; unsigned long timeout;
- timeout = jiffies + msecs_to_jiffies(50); + timeout = jiffies + msecs_to_jiffies(ms); while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & idma_mask(channel->num)) { - if (time_after(jiffies, timeout)) { - dev_warn(ipu->dev, "disabling busy idmac channel %d\n", - channel->num); - break; - } + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; cpu_relax(); }
+ return 0; +} +EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); + +int ipu_idmac_disable_channel(struct ipuv3_channel *channel) +{ + struct ipu_soc *ipu = channel->ipu; + u32 val; + unsigned long flags; + spin_lock_irqsave(&ipu->lock, flags);
/* Disable DMA channel(s) */ diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index 6fd37a7..574633b 100644 --- a/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/drivers/staging/imx-drm/ipuv3-crtc.c @@ -102,6 +102,7 @@ static void ipu_fb_disable(struct ipu_crtc *ipu_crtc) if (ipu_crtc->dp) ipu_dp_disable_channel(ipu_crtc->dp); 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);
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- drivers/staging/imx-drm/ipuv3-crtc.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index 574633b..f63931b 100644 --- a/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/drivers/staging/imx-drm/ipuv3-crtc.c @@ -223,6 +223,10 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc, 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);
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- drivers/staging/imx-drm/ipuv3-crtc.c | 7 ------- 1 file changed, 7 deletions(-)
diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index f63931b..292abea 100644 --- a/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/drivers/staging/imx-drm/ipuv3-crtc.c @@ -33,13 +33,6 @@
#define DRIVER_DESC "i.MX IPUv3 Graphics"
-struct ipu_framebuffer { - struct drm_framebuffer base; - void *virt; - dma_addr_t phys; - size_t len; -}; - struct ipu_crtc { struct device *dev; struct drm_crtc base;
From: Sascha Hauer s.hauer@pengutronix.de
During a device close the drm core frees all pending events in drm_events_release(). If at that time a pageflip is pending the interrupt handler will try to complete the now unitialized event resulting in a NULL pointer exception. Seen on imx-drm when userspace is killed during a page flip.
Signed-off-by: Sascha Hauer s.hauer@pengutronix.de Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- drivers/staging/imx-drm/imx-drm-core.c | 13 +++++++++++++ drivers/staging/imx-drm/ipuv3-crtc.c | 9 ++------- 2 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index a2e52a0..c77f9eb 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -191,6 +191,18 @@ static void imx_drm_disable_vblank(struct drm_device *drm, int crtc) imx_drm_crtc->imx_drm_helper_funcs.disable_vblank(imx_drm_crtc->crtc); }
+static void imx_drm_driver_preclose(struct drm_device *drm, + struct drm_file *file) +{ + int i; + + if (!file->is_master) + return; + + for (i = 0; i < 4; i++) + imx_drm_disable_vblank(drm , i); +} + static const struct file_operations imx_drm_driver_fops = { .owner = THIS_MODULE, .open = drm_open, @@ -778,6 +790,7 @@ static struct drm_driver imx_drm_driver = { .load = imx_drm_driver_load, .unload = imx_drm_driver_unload, .lastclose = imx_drm_driver_lastclose, + .preclose = imx_drm_driver_preclose, .gem_free_object = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, .dumb_create = drm_gem_cma_dumb_create, diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index 292abea..ae50dd4 100644 --- a/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/drivers/staging/imx-drm/ipuv3-crtc.c @@ -368,10 +368,6 @@ static struct drm_crtc_helper_funcs ipu_helper_funcs = {
static int ipu_enable_vblank(struct drm_crtc *crtc) { - struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); - - enable_irq(ipu_crtc->irq); - return 0; }
@@ -379,7 +375,8 @@ static void ipu_disable_vblank(struct drm_crtc *crtc) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
- disable_irq(ipu_crtc->irq); + ipu_crtc->page_flip_event = NULL; + ipu_crtc->newfb = NULL; }
static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type, @@ -502,8 +499,6 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, goto err_put_resources; }
- disable_irq(ipu_crtc->irq); - return 0;
err_put_resources:
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 p.zabel@pengutronix.de --- drivers/staging/imx-drm/Makefile | 2 +- drivers/staging/imx-drm/TODO | 1 - drivers/staging/imx-drm/imx-drm-core.c | 5 + drivers/staging/imx-drm/imx-drm.h | 2 + drivers/staging/imx-drm/ipu-v3/ipu-common.c | 2 +- drivers/staging/imx-drm/ipuv3-crtc.c | 198 ++++----------- drivers/staging/imx-drm/ipuv3-plane.c | 375 ++++++++++++++++++++++++++++ drivers/staging/imx-drm/ipuv3-plane.h | 55 ++++ 8 files changed, 484 insertions(+), 156 deletions(-) create mode 100644 drivers/staging/imx-drm/ipuv3-plane.c create mode 100644 drivers/staging/imx-drm/ipuv3-plane.h
diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile index bfaf693..2c3a9e1 100644 --- a/drivers/staging/imx-drm/Makefile +++ b/drivers/staging/imx-drm/Makefile @@ -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 diff --git a/drivers/staging/imx-drm/TODO b/drivers/staging/imx-drm/TODO index 9cfa2a7..6a9da94 100644 --- a/drivers/staging/imx-drm/TODO +++ b/drivers/staging/imx-drm/TODO @@ -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) diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index c77f9eb..f931c62 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -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; diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h index f2aac91..ae90c9c 100644 --- a/drivers/staging/imx-drm/imx-drm.h +++ b/drivers/staging/imx-drm/imx-drm.h @@ -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); diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c index 5ca42f0..a0e7fc2 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-common.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c @@ -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", }, { diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index ae50dd4..f1112df 100644 --- a/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/drivers/staging/imx-drm/ipuv3-crtc.c @@ -25,11 +25,13 @@ #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"
@@ -37,10 +39,11 @@ 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; @@ -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; } @@ -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; @@ -153,33 +131,6 @@ 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, @@ -187,45 +138,15 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc, 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) @@ -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) { @@ -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) @@ -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); }
@@ -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); } @@ -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); @@ -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) { @@ -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);
@@ -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; diff --git a/drivers/staging/imx-drm/ipuv3-plane.c b/drivers/staging/imx-drm/ipuv3-plane.c new file mode 100644 index 0000000..d97454a --- /dev/null +++ b/drivers/staging/imx-drm/ipuv3-plane.c @@ -0,0 +1,375 @@ +/* + * 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 "ipu-v3/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 ipu_ch_param __iomem *cpmem; + struct drm_gem_cma_object *cma_obj; + + cma_obj = drm_fb_cma_get_gem_obj(fb, 0); + if (!cma_obj) { + DRM_LOG_KMS("entry is null.\n"); + return -EFAULT; + } + + dev_dbg(ipu_plane->base.dev->dev, "phys = 0x%x, x = %d, y = %d", + cma_obj->paddr, x, y); + + cpmem = ipu_get_cpmem(ipu_plane->ipu_ch); + ipu_cpmem_set_stride(cpmem, fb->pitches[0]); + ipu_cpmem_set_buffer(cpmem, 0, cma_obj->paddr + fb->offsets[0] + + fb->pitches[0] * y + x); + + 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 ipu_ch_param __iomem *cpmem; + 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; + + 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, 1, 0, 1); + 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); + 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; + } + + cpmem = ipu_get_cpmem(ipu_plane->ipu_ch); + ipu_ch_param_zero(cpmem); + ipu_cpmem_set_resolution(cpmem, src_w, src_h); + ret = ipu_cpmem_set_fmt(cpmem, 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); + + ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y); + if (ret < 0) + return ret; + + 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) +{ + 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); +} + +static void ipu_plane_dpms(struct ipu_plane *ipu_plane, int mode) +{ + bool enable; + + DRM_DEBUG_KMS("mode = %d", mode); + + enable = (mode == DRM_MODE_DPMS_ON); + + if (enable == ipu_plane->enabled) + return; + + if (enable) { + ipu_plane_enable(ipu_plane); + } else { + ipu_plane_disable(ipu_plane); + + ipu_idmac_put(ipu_plane->ipu_ch); + ipu_dmfc_put(ipu_plane->dmfc); + ipu_dp_put(ipu_plane->dp); + } +} + +/* + * 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; + + ipu_plane_dpms(ipu_plane, DRM_MODE_DPMS_ON); + + 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__); + + ipu_plane_dpms(ipu_plane, DRM_MODE_DPMS_OFF); + + 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; +} diff --git a/drivers/staging/imx-drm/ipuv3-plane.h b/drivers/staging/imx-drm/ipuv3-plane.h new file mode 100644 index 0000000..c0aae5b --- /dev/null +++ b/drivers/staging/imx-drm/ipuv3-plane.h @@ -0,0 +1,55 @@ +#ifndef __IPUV3_PLANE_H__ +#define __IPUV3_PLANE_H__ + +#include <drm/drm_crtc.h> /* drm_plane */ + +struct drm_plane; +struct drm_device; +struct ipu_soc; +struct drm_crtc; +struct drm_framebuffer; + +struct ipuv3_channel; +struct dmfc_channel; +struct ipu_dp; + +struct ipu_plane { + struct drm_plane base; + + struct ipu_soc *ipu; + struct ipuv3_channel *ipu_ch; + struct dmfc_channel *dmfc; + struct ipu_dp *dp; + + int dma; + int dp_flow; + + int x; + int y; + + bool enabled; +}; + +struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, + int dma, int dp, unsigned int possible_crtcs, + bool priv); + +/* Init IDMAC, DMFC, DP */ +int ipu_plane_mode_set(struct ipu_plane *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); + +void ipu_plane_enable(struct ipu_plane *plane); +void ipu_plane_disable(struct ipu_plane *plane); +int ipu_plane_set_base(struct ipu_plane *plane, struct drm_framebuffer *fb, + int x, int y); + +int ipu_plane_get_resources(struct ipu_plane *plane); +void ipu_plane_put_resources(struct ipu_plane *plane); + +int ipu_plane_irq(struct ipu_plane *plane); + +#endif
Lets the IPU driver make use of the PRIME functionality introduced by the "drm: GEM CMA: Add DRM PRIME support" patch.
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- drivers/staging/imx-drm/imx-drm-core.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index f931c62..7b25181 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -791,7 +791,7 @@ static const struct drm_ioctl_desc imx_drm_ioctls[] = { };
static struct drm_driver imx_drm_driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM, + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, .load = imx_drm_driver_load, .unload = imx_drm_driver_unload, .lastclose = imx_drm_driver_lastclose, @@ -802,6 +802,15 @@ static struct drm_driver imx_drm_driver = { .dumb_map_offset = drm_gem_cma_dumb_map_offset, .dumb_destroy = drm_gem_dumb_destroy,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, .get_vblank_counter = drm_vblank_count, .enable_vblank = imx_drm_enable_vblank, .disable_vblank = imx_drm_disable_vblank,
On Thu, Oct 10, 2013 at 04:18:35PM +0200, Philipp Zabel wrote:
Hi,
This series fixes DMFC allocation for small displays and pageflip events during device close, clarifies the RGB memory formats, adds support for the RGB565 format and KMS planes, and enables DRM PRIME using the CMA helpers.
In the next step, I would like to move the code from drivers/staging/imx-drm/ipu-v3 to drivers/gpu/ipu-v3 (right next to host1x), as these parts provide an internal API for the IPU modules to be used by both DRM and V4L2 drivers.
I would like to see this happen as well, I really don't like new features being added to staging drivers as they should be primarily worked on to clean the code up and get merged to the proper place instead.
I'll take this series, but after that, please work to move it to drivers/gpu/
thanks,
greg k-h
dri-devel@lists.freedesktop.org