diff options
Diffstat (limited to 'drivers/staging/imx-drm')
-rw-r--r-- | drivers/staging/imx-drm/Makefile | 2 | ||||
-rw-r--r-- | drivers/staging/imx-drm/TODO | 1 | ||||
-rw-r--r-- | drivers/staging/imx-drm/imx-drm-core.c | 45 | ||||
-rw-r--r-- | drivers/staging/imx-drm/imx-drm.h | 2 | ||||
-rw-r--r-- | drivers/staging/imx-drm/imx-ldb.c | 9 | ||||
-rw-r--r-- | drivers/staging/imx-drm/imx-tve.c | 6 | ||||
-rw-r--r-- | drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h | 4 | ||||
-rw-r--r-- | drivers/staging/imx-drm/ipu-v3/ipu-common.c | 175 | ||||
-rw-r--r-- | drivers/staging/imx-drm/ipu-v3/ipu-dc.c | 13 | ||||
-rw-r--r-- | drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c | 8 | ||||
-rw-r--r-- | drivers/staging/imx-drm/ipu-v3/ipu-dp.c | 2 | ||||
-rw-r--r-- | drivers/staging/imx-drm/ipuv3-crtc.c | 211 | ||||
-rw-r--r-- | drivers/staging/imx-drm/ipuv3-plane.c | 375 | ||||
-rw-r--r-- | drivers/staging/imx-drm/ipuv3-plane.h | 55 |
14 files changed, 670 insertions, 238 deletions
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 a2e52a0..4483d47 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; @@ -110,18 +115,12 @@ int imx_drm_crtc_panel_format_pins(struct drm_crtc *crtc, u32 encoder_type, struct imx_drm_crtc *imx_crtc; struct imx_drm_crtc_helper_funcs *helper; - mutex_lock(&imxdrm->mutex); - list_for_each_entry(imx_crtc, &imxdrm->crtc_list, list) if (imx_crtc->crtc == crtc) goto found; - mutex_unlock(&imxdrm->mutex); - return -EINVAL; found: - mutex_unlock(&imxdrm->mutex); - helper = &imx_crtc->imx_drm_helper_funcs; if (helper->set_interface_pix_fmt) return helper->set_interface_pix_fmt(crtc, @@ -191,6 +190,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, @@ -647,20 +658,14 @@ int imx_drm_encoder_get_mux_id(struct imx_drm_encoder *imx_drm_encoder, struct imx_drm_crtc *imx_crtc; int i = 0; - mutex_lock(&imxdrm->mutex); - list_for_each_entry(imx_crtc, &imxdrm->crtc_list, list) { if (imx_crtc->crtc == crtc) goto found; i++; } - mutex_unlock(&imxdrm->mutex); - return -EINVAL; found: - mutex_unlock(&imxdrm->mutex); - return i; } EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id); @@ -774,16 +779,26 @@ 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, + .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, .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, @@ -837,8 +852,8 @@ static int __init imx_drm_init(void) INIT_LIST_HEAD(&imx_drm_device->encoder_list); imx_drm_pdev = platform_device_register_simple("imx-drm", -1, NULL, 0); - if (!imx_drm_pdev) { - ret = -EINVAL; + if (IS_ERR(imx_drm_pdev)) { + ret = PTR_ERR(imx_drm_pdev); goto err_pdev; } 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/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c index af733ea..654bf03 100644 --- a/drivers/staging/imx-drm/imx-ldb.c +++ b/drivers/staging/imx-drm/imx-ldb.c @@ -359,10 +359,8 @@ static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno) sprintf(clkname, "di%d_pll", chno); ldb->clk_pll[chno] = devm_clk_get(ldb->dev, clkname); - if (IS_ERR(ldb->clk_pll[chno])) - return PTR_ERR(ldb->clk_pll[chno]); - return 0; + return PTR_ERR_OR_ZERO(ldb->clk_pll[chno]); } static int imx_ldb_register(struct imx_ldb_channel *imx_ldb_ch) @@ -421,7 +419,7 @@ static const char *imx_ldb_bit_mappings[] = { [LVDS_BIT_MAP_JEIDA] = "jeida", }; -const int of_get_data_mapping(struct device_node *np) +static const int of_get_data_mapping(struct device_node *np) { const char *bm; int ret, i; @@ -466,8 +464,7 @@ static int imx_ldb_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; const struct of_device_id *of_id = - of_match_device(of_match_ptr(imx_ldb_dt_ids), - &pdev->dev); + of_match_device(imx_ldb_dt_ids, &pdev->dev); struct device_node *child; const u8 *edidp; struct imx_ldb *imx_ldb; diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c index 33d6525..680f4c8 100644 --- a/drivers/staging/imx-drm/imx-tve.c +++ b/drivers/staging/imx-drm/imx-tve.c @@ -151,7 +151,7 @@ static void tve_enable(struct imx_tve *tve) spin_lock_irqsave(&tve->enable_lock, flags); if (!tve->enabled) { - tve->enabled = 1; + tve->enabled = true; clk_prepare_enable(tve->clk); ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, TVE_IPU_CLK_EN | TVE_EN, @@ -180,7 +180,7 @@ static void tve_disable(struct imx_tve *tve) spin_lock_irqsave(&tve->enable_lock, flags); if (tve->enabled) { - tve->enabled = 0; + tve->enabled = false; ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, TVE_IPU_CLK_EN | TVE_EN, 0); clk_disable_unprepare(tve->clk); @@ -696,7 +696,7 @@ static int imx_tve_probe(struct platform_device *pdev) if (val != 0x00100000) { dev_err(&pdev->dev, "configuration register default value indicates this is not a TVEv2\n"); return -ENODEV; - }; + } /* disable cable detection for VGA mode */ ret = regmap_write(tve->regmap, TVE_CD_CONT_REG, 0); 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..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); @@ -283,7 +284,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) @@ -303,6 +304,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 ba464e5..7a22ce6 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" @@ -139,7 +141,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 +284,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,31 +292,31 @@ static struct ipu_rgb def_rgb_32 = { .bits_per_pixel = 32, }; -static struct ipu_rgb def_bgr_32 = { - .red = { .offset = 16, .length = 8, }, +static const struct ipu_rgb def_bgr_32 = { + .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 struct ipu_rgb def_rgb_24 = { - .red = { .offset = 0, .length = 8, }, +static const struct ipu_rgb def_rgb_24 = { + .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 struct ipu_rgb def_bgr_24 = { - .red = { .offset = 16, .length = 8, }, +static const struct ipu_rgb def_bgr_24 = { + .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, }; -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, }, @@ -322,6 +324,14 @@ static 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) @@ -329,17 +339,17 @@ static 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 +357,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 +365,25 @@ 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; + case DRM_FORMAT_BGR565: + ipu_cpmem_set_format_rgb(cpmem, &def_bgr_16); break; default: return -EINVAL; @@ -378,6 +393,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 +480,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: @@ -476,7 +564,7 @@ struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) goto out; } - channel->busy = 1; + channel->busy = true; channel->num = num; out: @@ -494,7 +582,7 @@ void ipu_idmac_put(struct ipuv3_channel *channel) mutex_lock(&ipu->channel_lock); - channel->busy = 0; + channel->busy = false; mutex_unlock(&ipu->channel_lock); } @@ -610,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) */ @@ -888,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", }, { @@ -913,7 +1006,7 @@ static int ipu_add_subdevice_pdata(struct device *dev, pdev = platform_device_register_data(dev, reg->name, ipu_client_id++, ®->pdata, sizeof(struct ipu_platform_reg)); - return pdev ? 0 : -EINVAL; + return PTR_ERR_OR_ZERO(pdev); } static int ipu_add_client_devices(struct ipu_soc *ipu) diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c index 21bf1c8..d0e3bc3 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; } @@ -313,7 +316,7 @@ struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel) return ERR_PTR(-EBUSY); } - dc->in_use = 1; + dc->in_use = true; mutex_unlock(&priv->mutex); @@ -326,7 +329,7 @@ void ipu_dc_put(struct ipu_dc *dc) struct ipu_dc_priv *priv = dc->priv; mutex_lock(&priv->mutex); - dc->in_use = 0; + dc->in_use = false; mutex_unlock(&priv->mutex); } EXPORT_SYMBOL_GPL(ipu_dc_put); @@ -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; } 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 diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dp.c b/drivers/staging/imx-drm/ipu-v3/ipu-dp.c index 231afd6..58f87c8 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-dp.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-dp.c @@ -325,7 +325,7 @@ int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) mutex_init(&priv->mutex); for (i = 0; i < IPUV3_NUM_FLOWS; i++) { - priv->flow[i].foreground.foreground = 1; + priv->flow[i].foreground.foreground = true; priv->flow[i].base = priv->base + ipu_dp_flow_base[i]; priv->flow[i].priv = priv; } diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index 6fd37a7..670a56a 100644 --- a/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/drivers/staging/imx-drm/ipuv3-crtc.c @@ -25,29 +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_framebuffer { - struct drm_framebuffer base; - void *virt; - dma_addr_t phys; - size_t len; -}; - 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; @@ -61,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; } @@ -99,11 +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_disable_channel(ipu_crtc->ipu_ch); - ipu_dmfc_disable_channel(ipu_crtc->dmfc); ipu_di_disable(ipu_crtc->di); ipu_crtc->enabled = 0; @@ -159,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, @@ -193,41 +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; - 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) @@ -238,7 +157,7 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc, sig_cfg.Vsync_pol = 1; sig_cfg.enable_pol = 1; - sig_cfg.clk_pol = 0; + sig_cfg.clk_pol = 1; sig_cfg.width = mode->hdisplay; sig_cfg.height = mode->vdisplay; sig_cfg.pixel_fmt = out_pixel_fmt; @@ -257,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) { @@ -285,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) @@ -332,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); } @@ -370,10 +256,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; } @@ -381,7 +263,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, @@ -418,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); } @@ -434,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); @@ -477,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) { @@ -495,19 +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; } - disable_irq(ipu_crtc->irq); - 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); @@ -546,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 |