Am Montag, den 26.05.2014, 16:19 +0200 schrieb Philipp Zabel: > The i.MX Image Processing Unit (IPU) contains a number of image processing > blocks that sit right in the middle between DRM and V4L2. Some of the modules, > such as Display Controller, Processor, and Interface (DC, DP, DI) or CMOS > Sensor Interface (CSI) and their FIFOs could be assigned to either framework, > but others, such as the dma controller (IDMAC) and image converter (IC) can > be used by both. > The IPUv3 core driver provides an internal API to access the modules, to be > used by both DRM and V4L2 IPUv3 drivers. > >Signed-off-by: Lucas Stach <l.stach@xxxxxxxxxxxxxx> >Signed-off-by: Philipp Zabel <p.zabel@xxxxxxxxxxxxxx> And here is a complete version of the move without format-patch -M parameter: --- drivers/gpu/Makefile | 1 + drivers/gpu/ipu-v3/Kconfig | 7 + drivers/gpu/ipu-v3/Makefile | 3 + drivers/gpu/ipu-v3/ipu-common.c | 1292 +++++++++++++++++++++++++++ drivers/gpu/ipu-v3/ipu-dc.c | 460 ++++++++++ drivers/gpu/ipu-v3/ipu-di.c | 730 +++++++++++++++ drivers/gpu/ipu-v3/ipu-dmfc.c | 436 +++++++++ drivers/gpu/ipu-v3/ipu-dp.c | 363 ++++++++ drivers/gpu/ipu-v3/ipu-prv.h | 209 +++++ drivers/staging/imx-drm/Kconfig | 11 +- drivers/staging/imx-drm/Makefile | 1 - drivers/staging/imx-drm/imx-hdmi.c | 2 +- drivers/staging/imx-drm/imx-tve.c | 2 +- drivers/staging/imx-drm/ipu-v3/Makefile | 3 - drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h | 331 ------- drivers/staging/imx-drm/ipu-v3/ipu-common.c | 1292 --------------------------- drivers/staging/imx-drm/ipu-v3/ipu-dc.c | 461 ---------- drivers/staging/imx-drm/ipu-v3/ipu-di.c | 730 --------------- drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c | 436 --------- drivers/staging/imx-drm/ipu-v3/ipu-dp.c | 363 -------- drivers/staging/imx-drm/ipu-v3/ipu-prv.h | 209 ----- drivers/staging/imx-drm/ipuv3-crtc.c | 2 +- drivers/staging/imx-drm/ipuv3-plane.c | 2 +- drivers/video/Kconfig | 1 + include/video/imx-ipu-v3.h | 331 +++++++ 25 files changed, 3838 insertions(+), 3840 deletions(-) create mode 100644 drivers/gpu/ipu-v3/Kconfig create mode 100644 drivers/gpu/ipu-v3/Makefile create mode 100644 drivers/gpu/ipu-v3/ipu-common.c create mode 100644 drivers/gpu/ipu-v3/ipu-dc.c create mode 100644 drivers/gpu/ipu-v3/ipu-di.c create mode 100644 drivers/gpu/ipu-v3/ipu-dmfc.c create mode 100644 drivers/gpu/ipu-v3/ipu-dp.c create mode 100644 drivers/gpu/ipu-v3/ipu-prv.h delete mode 100644 drivers/staging/imx-drm/ipu-v3/Makefile delete mode 100644 drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h delete mode 100644 drivers/staging/imx-drm/ipu-v3/ipu-common.c delete mode 100644 drivers/staging/imx-drm/ipu-v3/ipu-dc.c delete mode 100644 drivers/staging/imx-drm/ipu-v3/ipu-di.c delete mode 100644 drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c delete mode 100644 drivers/staging/imx-drm/ipu-v3/ipu-dp.c delete mode 100644 drivers/staging/imx-drm/ipu-v3/ipu-prv.h create mode 100644 include/video/imx-ipu-v3.h diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index d8a22c2..70da9eb 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -1,2 +1,3 @@ obj-y += drm/ vga/ obj-$(CONFIG_TEGRA_HOST1X) += host1x/ +obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ diff --git a/drivers/gpu/ipu-v3/Kconfig b/drivers/gpu/ipu-v3/Kconfig new file mode 100644 index 0000000..2f228a2 --- /dev/null +++ b/drivers/gpu/ipu-v3/Kconfig @@ -0,0 +1,7 @@ +config IMX_IPUV3_CORE + tristate "IPUv3 core support" + depends on SOC_IMX5 || SOC_IMX6Q || SOC_IMX6SL || ARCH_MULTIPLATFORM + depends on RESET_CONTROLLER + help + Choose this if you have a i.MX5/6 system and want to use the Image + Processing Unit. This option only enables IPU base support. diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile new file mode 100644 index 0000000..d21cc37 --- /dev/null +++ b/drivers/gpu/ipu-v3/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o + +imx-ipu-v3-objs := ipu-common.o ipu-dc.o ipu-di.o ipu-dp.o ipu-dmfc.o diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c new file mode 100644 index 0000000..ad99477 --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -0,0 +1,1292 @@ +/* + * Copyright (c) 2010 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> + * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. + * + * 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 <linux/module.h> +#include <linux/export.h> +#include <linux/types.h> +#include <linux/reset.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/list.h> +#include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/of_device.h> + +#include <drm/drm_fourcc.h> + +#include <video/imx-ipu-v3.h> +#include "ipu-prv.h" + +static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) +{ + return readl(ipu->cm_reg + offset); +} + +static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) +{ + writel(value, ipu->cm_reg + offset); +} + +static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset) +{ + return readl(ipu->idmac_reg + offset); +} + +static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value, + unsigned offset) +{ + writel(value, ipu->idmac_reg + offset); +} + +void ipu_srm_dp_sync_update(struct ipu_soc *ipu) +{ + u32 val; + + val = ipu_cm_read(ipu, IPU_SRM_PRI2); + val |= 0x8; + ipu_cm_write(ipu, val, IPU_SRM_PRI2); +} +EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update); + +struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel) +{ + struct ipu_soc *ipu = channel->ipu; + + return ipu->cpmem_base + channel->num; +} +EXPORT_SYMBOL_GPL(ipu_get_cpmem); + +void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel) +{ + struct ipu_soc *ipu = channel->ipu; + struct ipu_ch_param __iomem *p = ipu_get_cpmem(channel); + u32 val; + + if (ipu->ipu_type == IPUV3EX) + ipu_ch_param_write_field(p, IPU_FIELD_ID, 1); + + val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(channel->num)); + val |= 1 << (channel->num % 32); + ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(channel->num)); +}; +EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority); + +void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v) +{ + u32 bit = (wbs >> 8) % 160; + u32 size = wbs & 0xff; + u32 word = (wbs >> 8) / 160; + u32 i = bit / 32; + u32 ofs = bit % 32; + u32 mask = (1 << size) - 1; + u32 val; + + pr_debug("%s %d %d %d\n", __func__, word, bit , size); + + val = readl(&base->word[word].data[i]); + val &= ~(mask << ofs); + val |= v << ofs; + writel(val, &base->word[word].data[i]); + + if ((bit + size - 1) / 32 > i) { + val = readl(&base->word[word].data[i + 1]); + val &= ~(mask >> (ofs ? (32 - ofs) : 0)); + val |= v >> (ofs ? (32 - ofs) : 0); + writel(val, &base->word[word].data[i + 1]); + } +} +EXPORT_SYMBOL_GPL(ipu_ch_param_write_field); + +u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs) +{ + u32 bit = (wbs >> 8) % 160; + u32 size = wbs & 0xff; + u32 word = (wbs >> 8) / 160; + u32 i = bit / 32; + u32 ofs = bit % 32; + u32 mask = (1 << size) - 1; + u32 val = 0; + + pr_debug("%s %d %d %d\n", __func__, word, bit , size); + + val = (readl(&base->word[word].data[i]) >> ofs) & mask; + + if ((bit + size - 1) / 32 > i) { + u32 tmp; + tmp = readl(&base->word[word].data[i + 1]); + tmp &= mask >> (ofs ? (32 - ofs) : 0); + val |= tmp << (ofs ? (32 - ofs) : 0); + } + + return val; +} +EXPORT_SYMBOL_GPL(ipu_ch_param_read_field); + +int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *p, + const struct ipu_rgb *rgb) +{ + int bpp = 0, npb = 0, ro, go, bo, to; + + ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset; + go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset; + bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset; + to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset; + + ipu_ch_param_write_field(p, IPU_FIELD_WID0, rgb->red.length - 1); + ipu_ch_param_write_field(p, IPU_FIELD_OFS0, ro); + ipu_ch_param_write_field(p, IPU_FIELD_WID1, rgb->green.length - 1); + ipu_ch_param_write_field(p, IPU_FIELD_OFS1, go); + ipu_ch_param_write_field(p, IPU_FIELD_WID2, rgb->blue.length - 1); + ipu_ch_param_write_field(p, IPU_FIELD_OFS2, bo); + + if (rgb->transp.length) { + ipu_ch_param_write_field(p, IPU_FIELD_WID3, + rgb->transp.length - 1); + ipu_ch_param_write_field(p, IPU_FIELD_OFS3, to); + } else { + ipu_ch_param_write_field(p, IPU_FIELD_WID3, 7); + ipu_ch_param_write_field(p, IPU_FIELD_OFS3, + rgb->bits_per_pixel); + } + + switch (rgb->bits_per_pixel) { + case 32: + bpp = 0; + npb = 15; + break; + case 24: + bpp = 1; + npb = 19; + break; + case 16: + bpp = 3; + npb = 31; + break; + case 8: + bpp = 5; + npb = 63; + break; + default: + return -EINVAL; + } + ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp); + ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb); + ipu_ch_param_write_field(p, IPU_FIELD_PFS, 7); /* rgb mode */ + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb); + +int ipu_cpmem_set_format_passthrough(struct ipu_ch_param __iomem *p, + int width) +{ + int bpp = 0, npb = 0; + + switch (width) { + case 32: + bpp = 0; + npb = 15; + break; + case 24: + bpp = 1; + npb = 19; + break; + case 16: + bpp = 3; + npb = 31; + break; + case 8: + bpp = 5; + npb = 63; + break; + default: + return -EINVAL; + } + + ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp); + ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb); + ipu_ch_param_write_field(p, IPU_FIELD_PFS, 6); /* raw mode */ + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough); + +void ipu_cpmem_set_yuv_interleaved(struct ipu_ch_param __iomem *p, + u32 pixel_format) +{ + switch (pixel_format) { + case V4L2_PIX_FMT_UYVY: + ipu_ch_param_write_field(p, IPU_FIELD_BPP, 3); /* bits/pixel */ + ipu_ch_param_write_field(p, IPU_FIELD_PFS, 0xA); /* pix format */ + ipu_ch_param_write_field(p, IPU_FIELD_NPB, 31); /* burst size */ + break; + case V4L2_PIX_FMT_YUYV: + ipu_ch_param_write_field(p, IPU_FIELD_BPP, 3); /* bits/pixel */ + ipu_ch_param_write_field(p, IPU_FIELD_PFS, 0x8); /* pix format */ + ipu_ch_param_write_field(p, IPU_FIELD_NPB, 31); /* burst size */ + break; + } +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved); + +void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p, + u32 pixel_format, int stride, int u_offset, int v_offset) +{ + switch (pixel_format) { + case V4L2_PIX_FMT_YUV420: + ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1); + ipu_ch_param_write_field(p, IPU_FIELD_UBO, u_offset / 8); + ipu_ch_param_write_field(p, IPU_FIELD_VBO, v_offset / 8); + break; + case V4L2_PIX_FMT_YVU420: + ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1); + ipu_ch_param_write_field(p, IPU_FIELD_UBO, v_offset / 8); + ipu_ch_param_write_field(p, IPU_FIELD_VBO, u_offset / 8); + break; + } +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full); + +void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format, + int stride, int height) +{ + int u_offset, v_offset; + int uv_stride = 0; + + switch (pixel_format) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + uv_stride = stride / 2; + u_offset = stride * height; + v_offset = u_offset + (uv_stride * height / 2); + ipu_cpmem_set_yuv_planar_full(p, pixel_format, stride, + u_offset, v_offset); + break; + } +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar); + +static const struct ipu_rgb def_rgb_32 = { + .red = { .offset = 16, .length = 8, }, + .green = { .offset = 8, .length = 8, }, + .blue = { .offset = 0, .length = 8, }, + .transp = { .offset = 24, .length = 8, }, + .bits_per_pixel = 32, +}; + +static const struct ipu_rgb def_bgr_32 = { + .red = { .offset = 0, .length = 8, }, + .green = { .offset = 8, .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 = 16, .length = 8, }, + .green = { .offset = 8, .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 = 0, .length = 8, }, + .green = { .offset = 8, .length = 8, }, + .blue = { .offset = 16, .length = 8, }, + .transp = { .offset = 0, .length = 0, }, + .bits_per_pixel = 24, +}; + +static const struct ipu_rgb def_rgb_16 = { + .red = { .offset = 11, .length = 5, }, + .green = { .offset = 5, .length = 6, }, + .blue = { .offset = 0, .length = 5, }, + .transp = { .offset = 0, .length = 0, }, + .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) +#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \ + (pix->width * pix->height / 4) + \ + (pix->width * (y) / 4) + (x) / 2) + +int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 drm_fourcc) +{ + 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 DRM_FORMAT_UYVY: + /* bits/pixel */ + ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3); + /* pix format */ + ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0xA); + /* burst size */ + ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31); + break; + case DRM_FORMAT_YUYV: + /* bits/pixel */ + ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3); + /* pix format */ + ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0x8); + /* burst size */ + ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31); + break; + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XBGR8888: + ipu_cpmem_set_format_rgb(cpmem, &def_bgr_32); + break; + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + ipu_cpmem_set_format_rgb(cpmem, &def_rgb_32); + break; + case DRM_FORMAT_BGR888: + ipu_cpmem_set_format_rgb(cpmem, &def_bgr_24); + break; + case DRM_FORMAT_RGB888: + ipu_cpmem_set_format_rgb(cpmem, &def_rgb_24); + break; + 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; + } + + return 0; +} +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) +{ + struct v4l2_pix_format *pix = &image->pix; + int y_offset, u_offset, v_offset; + + pr_debug("%s: resolution: %dx%d stride: %d\n", + __func__, pix->width, pix->height, + pix->bytesperline); + + ipu_cpmem_set_resolution(cpmem, image->rect.width, + image->rect.height); + ipu_cpmem_set_stride(cpmem, pix->bytesperline); + + ipu_cpmem_set_fmt(cpmem, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat)); + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + y_offset = Y_OFFSET(pix, image->rect.left, image->rect.top); + u_offset = U_OFFSET(pix, image->rect.left, + image->rect.top) - y_offset; + v_offset = V_OFFSET(pix, image->rect.left, + image->rect.top) - y_offset; + + ipu_cpmem_set_yuv_planar_full(cpmem, pix->pixelformat, + pix->bytesperline, u_offset, v_offset); + ipu_cpmem_set_buffer(cpmem, 0, image->phys + y_offset); + break; + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + ipu_cpmem_set_buffer(cpmem, 0, image->phys + + image->rect.left * 2 + + image->rect.top * image->pix.bytesperline); + break; + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + ipu_cpmem_set_buffer(cpmem, 0, image->phys + + image->rect.left * 4 + + image->rect.top * image->pix.bytesperline); + break; + case V4L2_PIX_FMT_RGB565: + ipu_cpmem_set_buffer(cpmem, 0, image->phys + + image->rect.left * 2 + + image->rect.top * image->pix.bytesperline); + break; + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + ipu_cpmem_set_buffer(cpmem, 0, image->phys + + image->rect.left * 3 + + image->rect.top * image->pix.bytesperline); + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_image); + +enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) +{ + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + return IPUV3_COLORSPACE_YUV; + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB565: + return IPUV3_COLORSPACE_RGB; + default: + return IPUV3_COLORSPACE_UNKNOWN; + } +} +EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); + +struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) +{ + struct ipuv3_channel *channel; + + dev_dbg(ipu->dev, "%s %d\n", __func__, num); + + if (num > 63) + return ERR_PTR(-ENODEV); + + mutex_lock(&ipu->channel_lock); + + channel = &ipu->channel[num]; + + if (channel->busy) { + channel = ERR_PTR(-EBUSY); + goto out; + } + + channel->busy = true; + channel->num = num; + +out: + mutex_unlock(&ipu->channel_lock); + + return channel; +} +EXPORT_SYMBOL_GPL(ipu_idmac_get); + +void ipu_idmac_put(struct ipuv3_channel *channel) +{ + struct ipu_soc *ipu = channel->ipu; + + dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); + + mutex_lock(&ipu->channel_lock); + + channel->busy = false; + + mutex_unlock(&ipu->channel_lock); +} +EXPORT_SYMBOL_GPL(ipu_idmac_put); + +#define idma_mask(ch) (1 << (ch & 0x1f)) + +void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, + bool doublebuffer) +{ + struct ipu_soc *ipu = channel->ipu; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&ipu->lock, flags); + + reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); + if (doublebuffer) + reg |= idma_mask(channel->num); + else + reg &= ~idma_mask(channel->num); + ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); + + spin_unlock_irqrestore(&ipu->lock, flags); +} +EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); + +int ipu_module_enable(struct ipu_soc *ipu, u32 mask) +{ + unsigned long lock_flags; + u32 val; + + spin_lock_irqsave(&ipu->lock, lock_flags); + + val = ipu_cm_read(ipu, IPU_DISP_GEN); + + if (mask & IPU_CONF_DI0_EN) + val |= IPU_DI0_COUNTER_RELEASE; + if (mask & IPU_CONF_DI1_EN) + val |= IPU_DI1_COUNTER_RELEASE; + + ipu_cm_write(ipu, val, IPU_DISP_GEN); + + val = ipu_cm_read(ipu, IPU_CONF); + val |= mask; + ipu_cm_write(ipu, val, IPU_CONF); + + spin_unlock_irqrestore(&ipu->lock, lock_flags); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_module_enable); + +int ipu_module_disable(struct ipu_soc *ipu, u32 mask) +{ + unsigned long lock_flags; + u32 val; + + spin_lock_irqsave(&ipu->lock, lock_flags); + + val = ipu_cm_read(ipu, IPU_CONF); + val &= ~mask; + ipu_cm_write(ipu, val, IPU_CONF); + + val = ipu_cm_read(ipu, IPU_DISP_GEN); + + if (mask & IPU_CONF_DI0_EN) + val &= ~IPU_DI0_COUNTER_RELEASE; + if (mask & IPU_CONF_DI1_EN) + val &= ~IPU_DI1_COUNTER_RELEASE; + + ipu_cm_write(ipu, val, IPU_DISP_GEN); + + spin_unlock_irqrestore(&ipu->lock, lock_flags); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_module_disable); + +void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) +{ + struct ipu_soc *ipu = channel->ipu; + unsigned int chno = channel->num; + unsigned long flags; + + spin_lock_irqsave(&ipu->lock, flags); + + /* Mark buffer as ready. */ + if (buf_num == 0) + ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); + else + ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); + + spin_unlock_irqrestore(&ipu->lock, flags); +} +EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); + +int ipu_idmac_enable_channel(struct ipuv3_channel *channel) +{ + struct ipu_soc *ipu = channel->ipu; + u32 val; + unsigned long flags; + + spin_lock_irqsave(&ipu->lock, flags); + + val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); + val |= idma_mask(channel->num); + ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); + + spin_unlock_irqrestore(&ipu->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); + +bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) +{ + return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); +} +EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); + +int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) +{ + struct ipu_soc *ipu = channel->ipu; + unsigned long timeout; + + 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)) + return -ETIMEDOUT; + cpu_relax(); + } + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); + +int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(ms); + ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32)); + while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_wait_interrupt); + +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) */ + val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); + val &= ~idma_mask(channel->num); + ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); + + /* Set channel buffers NOT to be ready */ + ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ + + if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & + idma_mask(channel->num)) { + ipu_cm_write(ipu, idma_mask(channel->num), + IPU_CHA_BUF0_RDY(channel->num)); + } + + if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & + idma_mask(channel->num)) { + ipu_cm_write(ipu, idma_mask(channel->num), + IPU_CHA_BUF1_RDY(channel->num)); + } + + ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ + + /* Reset the double buffer */ + val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); + val &= ~idma_mask(channel->num); + ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); + + spin_unlock_irqrestore(&ipu->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); + +static int ipu_memory_reset(struct ipu_soc *ipu) +{ + unsigned long timeout; + + ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); + + timeout = jiffies + msecs_to_jiffies(1000); + while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { + if (time_after(jiffies, timeout)) + return -ETIME; + cpu_relax(); + } + + return 0; +} + +struct ipu_devtype { + const char *name; + unsigned long cm_ofs; + unsigned long cpmem_ofs; + unsigned long srm_ofs; + unsigned long tpm_ofs; + unsigned long disp0_ofs; + unsigned long disp1_ofs; + unsigned long dc_tmpl_ofs; + unsigned long vdi_ofs; + enum ipuv3_type type; +}; + +static struct ipu_devtype ipu_type_imx51 = { + .name = "IPUv3EX", + .cm_ofs = 0x1e000000, + .cpmem_ofs = 0x1f000000, + .srm_ofs = 0x1f040000, + .tpm_ofs = 0x1f060000, + .disp0_ofs = 0x1e040000, + .disp1_ofs = 0x1e048000, + .dc_tmpl_ofs = 0x1f080000, + .vdi_ofs = 0x1e068000, + .type = IPUV3EX, +}; + +static struct ipu_devtype ipu_type_imx53 = { + .name = "IPUv3M", + .cm_ofs = 0x06000000, + .cpmem_ofs = 0x07000000, + .srm_ofs = 0x07040000, + .tpm_ofs = 0x07060000, + .disp0_ofs = 0x06040000, + .disp1_ofs = 0x06048000, + .dc_tmpl_ofs = 0x07080000, + .vdi_ofs = 0x06068000, + .type = IPUV3M, +}; + +static struct ipu_devtype ipu_type_imx6q = { + .name = "IPUv3H", + .cm_ofs = 0x00200000, + .cpmem_ofs = 0x00300000, + .srm_ofs = 0x00340000, + .tpm_ofs = 0x00360000, + .disp0_ofs = 0x00240000, + .disp1_ofs = 0x00248000, + .dc_tmpl_ofs = 0x00380000, + .vdi_ofs = 0x00268000, + .type = IPUV3H, +}; + +static const struct of_device_id imx_ipu_dt_ids[] = { + { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, + { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, + { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); + +static int ipu_submodules_init(struct ipu_soc *ipu, + struct platform_device *pdev, unsigned long ipu_base, + struct clk *ipu_clk) +{ + char *unit; + int ret; + struct device *dev = &pdev->dev; + const struct ipu_devtype *devtype = ipu->devtype; + + ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, + IPU_CONF_DI0_EN, ipu_clk); + if (ret) { + unit = "di0"; + goto err_di_0; + } + + ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, + IPU_CONF_DI1_EN, ipu_clk); + if (ret) { + unit = "di1"; + goto err_di_1; + } + + ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + + IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); + if (ret) { + unit = "dc_template"; + goto err_dc; + } + + ret = ipu_dmfc_init(ipu, dev, ipu_base + + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); + if (ret) { + unit = "dmfc"; + goto err_dmfc; + } + + ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); + if (ret) { + unit = "dp"; + goto err_dp; + } + + return 0; + +err_dp: + ipu_dmfc_exit(ipu); +err_dmfc: + ipu_dc_exit(ipu); +err_dc: + ipu_di_exit(ipu, 1); +err_di_1: + ipu_di_exit(ipu, 0); +err_di_0: + dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); + return ret; +} + +static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) +{ + unsigned long status; + int i, bit, irq; + + for (i = 0; i < num_regs; i++) { + + status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); + status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); + + for_each_set_bit(bit, &status, 32) { + irq = irq_linear_revmap(ipu->domain, + regs[i] * 32 + bit); + if (irq) + generic_handle_irq(irq); + } + } +} + +static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct ipu_soc *ipu = irq_desc_get_handler_data(desc); + const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; + struct irq_chip *chip = irq_get_chip(irq); + + chained_irq_enter(chip, desc); + + ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); + + chained_irq_exit(chip, desc); +} + +static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct ipu_soc *ipu = irq_desc_get_handler_data(desc); + const int int_reg[] = { 4, 5, 8, 9}; + struct irq_chip *chip = irq_get_chip(irq); + + chained_irq_enter(chip, desc); + + ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); + + chained_irq_exit(chip, desc); +} + +int ipu_map_irq(struct ipu_soc *ipu, int irq) +{ + int virq; + + virq = irq_linear_revmap(ipu->domain, irq); + if (!virq) + virq = irq_create_mapping(ipu->domain, irq); + + return virq; +} +EXPORT_SYMBOL_GPL(ipu_map_irq); + +int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, + enum ipu_channel_irq irq_type) +{ + return ipu_map_irq(ipu, irq_type + channel->num); +} +EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); + +static void ipu_submodules_exit(struct ipu_soc *ipu) +{ + ipu_dp_exit(ipu); + ipu_dmfc_exit(ipu); + ipu_dc_exit(ipu); + ipu_di_exit(ipu, 1); + ipu_di_exit(ipu, 0); +} + +static int platform_remove_devices_fn(struct device *dev, void *unused) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + +static void platform_device_unregister_children(struct platform_device *pdev) +{ + device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); +} + +struct ipu_platform_reg { + struct ipu_client_platformdata pdata; + const char *name; +}; + +static const struct ipu_platform_reg client_reg[] = { + { + .pdata = { + .di = 0, + .dc = 5, + .dp = IPU_DP_FLOW_SYNC_BG, + .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, + .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, + }, + .name = "imx-ipuv3-crtc", + }, { + .pdata = { + .di = 1, + .dc = 1, + .dp = -EINVAL, + .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, + .dma[1] = -EINVAL, + }, + .name = "imx-ipuv3-crtc", + }, +}; + +static DEFINE_MUTEX(ipu_client_id_mutex); +static int ipu_client_id; + +static int ipu_add_client_devices(struct ipu_soc *ipu) +{ + struct device *dev = ipu->dev; + unsigned i; + int id, ret; + + mutex_lock(&ipu_client_id_mutex); + id = ipu_client_id; + ipu_client_id += ARRAY_SIZE(client_reg); + mutex_unlock(&ipu_client_id_mutex); + + for (i = 0; i < ARRAY_SIZE(client_reg); i++) { + const struct ipu_platform_reg *reg = &client_reg[i]; + struct platform_device *pdev; + + pdev = platform_device_register_data(dev, reg->name, + id++, ®->pdata, sizeof(reg->pdata)); + + if (IS_ERR(pdev)) + goto err_register; + } + + return 0; + +err_register: + platform_device_unregister_children(to_platform_device(dev)); + + return ret; +} + + +static int ipu_irq_init(struct ipu_soc *ipu) +{ + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + unsigned long unused[IPU_NUM_IRQS / 32] = { + 0x400100d0, 0xffe000fd, + 0x400100d0, 0xffe000fd, + 0x400100d0, 0xffe000fd, + 0x4077ffff, 0xffe7e1fd, + 0x23fffffe, 0x8880fff0, + 0xf98fe7d0, 0xfff81fff, + 0x400100d0, 0xffe000fd, + 0x00000000, + }; + int ret, i; + + ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, + &irq_generic_chip_ops, ipu); + if (!ipu->domain) { + dev_err(ipu->dev, "failed to add irq domain\n"); + return -ENODEV; + } + + ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", + handle_level_irq, 0, + IRQF_VALID, 0); + if (ret < 0) { + dev_err(ipu->dev, "failed to alloc generic irq chips\n"); + irq_domain_remove(ipu->domain); + return ret; + } + + for (i = 0; i < IPU_NUM_IRQS; i += 32) { + gc = irq_get_domain_generic_chip(ipu->domain, i); + gc->reg_base = ipu->cm_reg; + gc->unused = unused[i / 32]; + ct = gc->chip_types; + ct->chip.irq_ack = irq_gc_ack_set_bit; + ct->chip.irq_mask = irq_gc_mask_clr_bit; + ct->chip.irq_unmask = irq_gc_mask_set_bit; + ct->regs.ack = IPU_INT_STAT(i / 32); + ct->regs.mask = IPU_INT_CTRL(i / 32); + } + + irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler); + irq_set_handler_data(ipu->irq_sync, ipu); + irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler); + irq_set_handler_data(ipu->irq_err, ipu); + + return 0; +} + +static void ipu_irq_exit(struct ipu_soc *ipu) +{ + int i, irq; + + irq_set_chained_handler(ipu->irq_err, NULL); + irq_set_handler_data(ipu->irq_err, NULL); + irq_set_chained_handler(ipu->irq_sync, NULL); + irq_set_handler_data(ipu->irq_sync, NULL); + + /* TODO: remove irq_domain_generic_chips */ + + for (i = 0; i < IPU_NUM_IRQS; i++) { + irq = irq_linear_revmap(ipu->domain, i); + if (irq) + irq_dispose_mapping(irq); + } + + irq_domain_remove(ipu->domain); +} + +static int ipu_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(imx_ipu_dt_ids, &pdev->dev); + struct ipu_soc *ipu; + struct resource *res; + unsigned long ipu_base; + int i, ret, irq_sync, irq_err; + const struct ipu_devtype *devtype; + + devtype = of_id->data; + + irq_sync = platform_get_irq(pdev, 0); + irq_err = platform_get_irq(pdev, 1); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", + irq_sync, irq_err); + + if (!res || irq_sync < 0 || irq_err < 0) + return -ENODEV; + + ipu_base = res->start; + + ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); + if (!ipu) + return -ENODEV; + + for (i = 0; i < 64; i++) + ipu->channel[i].ipu = ipu; + ipu->devtype = devtype; + ipu->ipu_type = devtype->type; + + spin_lock_init(&ipu->lock); + mutex_init(&ipu->channel_lock); + + dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", + ipu_base + devtype->cm_ofs); + dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", + ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); + dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", + ipu_base + devtype->cpmem_ofs); + dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", + ipu_base + devtype->disp0_ofs); + dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", + ipu_base + devtype->disp1_ofs); + dev_dbg(&pdev->dev, "srm: 0x%08lx\n", + ipu_base + devtype->srm_ofs); + dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", + ipu_base + devtype->tpm_ofs); + dev_dbg(&pdev->dev, "dc: 0x%08lx\n", + ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); + dev_dbg(&pdev->dev, "ic: 0x%08lx\n", + ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); + dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", + ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); + dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", + ipu_base + devtype->vdi_ofs); + + ipu->cm_reg = devm_ioremap(&pdev->dev, + ipu_base + devtype->cm_ofs, PAGE_SIZE); + ipu->idmac_reg = devm_ioremap(&pdev->dev, + ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, + PAGE_SIZE); + ipu->cpmem_base = devm_ioremap(&pdev->dev, + ipu_base + devtype->cpmem_ofs, PAGE_SIZE); + + if (!ipu->cm_reg || !ipu->idmac_reg || !ipu->cpmem_base) + return -ENOMEM; + + ipu->clk = devm_clk_get(&pdev->dev, "bus"); + if (IS_ERR(ipu->clk)) { + ret = PTR_ERR(ipu->clk); + dev_err(&pdev->dev, "clk_get failed with %d", ret); + return ret; + } + + platform_set_drvdata(pdev, ipu); + + ret = clk_prepare_enable(ipu->clk); + if (ret) { + dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); + return ret; + } + + ipu->dev = &pdev->dev; + ipu->irq_sync = irq_sync; + ipu->irq_err = irq_err; + + ret = ipu_irq_init(ipu); + if (ret) + goto out_failed_irq; + + ret = device_reset(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "failed to reset: %d\n", ret); + goto out_failed_reset; + } + ret = ipu_memory_reset(ipu); + if (ret) + goto out_failed_reset; + + /* Set MCU_T to divide MCU access window into 2 */ + ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), + IPU_DISP_GEN); + + ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); + if (ret) + goto failed_submodules_init; + + ret = ipu_add_client_devices(ipu); + if (ret) { + dev_err(&pdev->dev, "adding client devices failed with %d\n", + ret); + goto failed_add_clients; + } + + dev_info(&pdev->dev, "%s probed\n", devtype->name); + + return 0; + +failed_add_clients: + ipu_submodules_exit(ipu); +failed_submodules_init: +out_failed_reset: + ipu_irq_exit(ipu); +out_failed_irq: + clk_disable_unprepare(ipu->clk); + return ret; +} + +static int ipu_remove(struct platform_device *pdev) +{ + struct ipu_soc *ipu = platform_get_drvdata(pdev); + + platform_device_unregister_children(pdev); + ipu_submodules_exit(ipu); + ipu_irq_exit(ipu); + + clk_disable_unprepare(ipu->clk); + + return 0; +} + +static struct platform_driver imx_ipu_driver = { + .driver = { + .name = "imx-ipuv3", + .of_match_table = imx_ipu_dt_ids, + }, + .probe = ipu_probe, + .remove = ipu_remove, +}; + +module_platform_driver(imx_ipu_driver); + +MODULE_ALIAS("platform:imx-ipuv3"); +MODULE_DESCRIPTION("i.MX IPU v3 driver"); +MODULE_AUTHOR("Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c new file mode 100644 index 0000000..2326c75 --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-dc.c @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2010 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> + * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. + * + * 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 <linux/export.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> + +#include <video/imx-ipu-v3.h> +#include "ipu-prv.h" + +#define DC_MAP_CONF_PTR(n) (0x108 + ((n) & ~0x1) * 2) +#define DC_MAP_CONF_VAL(n) (0x144 + ((n) & ~0x1) * 2) + +#define DC_EVT_NF 0 +#define DC_EVT_NL 1 +#define DC_EVT_EOF 2 +#define DC_EVT_NFIELD 3 +#define DC_EVT_EOL 4 +#define DC_EVT_EOFIELD 5 +#define DC_EVT_NEW_ADDR 6 +#define DC_EVT_NEW_CHAN 7 +#define DC_EVT_NEW_DATA 8 + +#define DC_EVT_NEW_ADDR_W_0 0 +#define DC_EVT_NEW_ADDR_W_1 1 +#define DC_EVT_NEW_CHAN_W_0 2 +#define DC_EVT_NEW_CHAN_W_1 3 +#define DC_EVT_NEW_DATA_W_0 4 +#define DC_EVT_NEW_DATA_W_1 5 +#define DC_EVT_NEW_ADDR_R_0 6 +#define DC_EVT_NEW_ADDR_R_1 7 +#define DC_EVT_NEW_CHAN_R_0 8 +#define DC_EVT_NEW_CHAN_R_1 9 +#define DC_EVT_NEW_DATA_R_0 10 +#define DC_EVT_NEW_DATA_R_1 11 + +#define DC_WR_CH_CONF 0x0 +#define DC_WR_CH_ADDR 0x4 +#define DC_RL_CH(evt) (8 + ((evt) & ~0x1) * 2) + +#define DC_GEN 0xd4 +#define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4) +#define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4) +#define DC_STAT 0x1c8 + +#define WROD(lf) (0x18 | ((lf) << 1)) +#define WRG 0x01 +#define WCLK 0xc9 + +#define SYNC_WAVE 0 +#define NULL_WAVE (-1) + +#define DC_GEN_SYNC_1_6_SYNC (2 << 1) +#define DC_GEN_SYNC_PRIORITY_1 (1 << 7) + +#define DC_WR_CH_CONF_WORD_SIZE_8 (0 << 0) +#define DC_WR_CH_CONF_WORD_SIZE_16 (1 << 0) +#define DC_WR_CH_CONF_WORD_SIZE_24 (2 << 0) +#define DC_WR_CH_CONF_WORD_SIZE_32 (3 << 0) +#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i) (((i) & 0x1) << 3) +#define DC_WR_CH_CONF_DISP_ID_SERIAL (2 << 3) +#define DC_WR_CH_CONF_DISP_ID_ASYNC (3 << 4) +#define DC_WR_CH_CONF_FIELD_MODE (1 << 9) +#define DC_WR_CH_CONF_PROG_TYPE_NORMAL (4 << 5) +#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5) +#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2) +#define DC_WR_CH_CONF_PROG_DISP_ID(i) (((i) & 0x1) << 3) + +#define IPU_DC_NUM_CHANNELS 10 + +struct ipu_dc_priv; + +enum ipu_dc_map { + IPU_DC_MAP_RGB24, + IPU_DC_MAP_RGB565, + IPU_DC_MAP_GBR24, /* TVEv2 */ + IPU_DC_MAP_BGR666, + IPU_DC_MAP_LVDS666, + IPU_DC_MAP_BGR24, +}; + +struct ipu_dc { + /* The display interface number assigned to this dc channel */ + unsigned int di; + void __iomem *base; + struct ipu_dc_priv *priv; + int chno; + bool in_use; +}; + +struct ipu_dc_priv { + void __iomem *dc_reg; + void __iomem *dc_tmpl_reg; + struct ipu_soc *ipu; + struct device *dev; + struct ipu_dc channels[IPU_DC_NUM_CHANNELS]; + struct mutex mutex; + struct completion comp; + int dc_irq; + int dp_irq; +}; + +static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority) +{ + u32 reg; + + reg = readl(dc->base + DC_RL_CH(event)); + reg &= ~(0xffff << (16 * (event & 0x1))); + reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); + writel(reg, dc->base + DC_RL_CH(event)); +} + +static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand, + int map, int wave, int glue, int sync, int stop) +{ + struct ipu_dc_priv *priv = dc->priv; + u32 reg1, reg2; + + if (opcode == WCLK) { + reg1 = (operand << 20) & 0xfff00000; + reg2 = operand >> 12 | opcode << 1 | stop << 9; + } else if (opcode == WRG) { + reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000); + reg2 = operand >> 17 | opcode << 7 | stop << 9; + } else { + reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000); + reg2 = operand >> 12 | opcode << 4 | stop << 9; + } + writel(reg1, priv->dc_tmpl_reg + word * 8); + writel(reg2, priv->dc_tmpl_reg + word * 8 + 4); +} + +static int ipu_pixfmt_to_map(u32 fmt) +{ + switch (fmt) { + case V4L2_PIX_FMT_RGB24: + return IPU_DC_MAP_RGB24; + case V4L2_PIX_FMT_RGB565: + return IPU_DC_MAP_RGB565; + case IPU_PIX_FMT_GBR24: + return IPU_DC_MAP_GBR24; + case V4L2_PIX_FMT_BGR666: + return IPU_DC_MAP_BGR666; + case v4l2_fourcc('L', 'V', 'D', '6'): + return IPU_DC_MAP_LVDS666; + case V4L2_PIX_FMT_BGR24: + return IPU_DC_MAP_BGR24; + default: + return -EINVAL; + } +} + +int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, + u32 pixel_fmt, u32 width) +{ + struct ipu_dc_priv *priv = dc->priv; + u32 reg = 0; + int map; + + dc->di = ipu_di_get_num(di); + + map = ipu_pixfmt_to_map(pixel_fmt); + if (map < 0) { + dev_dbg(priv->dev, "IPU_DISP: No MAP\n"); + return map; + } + + if (interlaced) { + dc_link_event(dc, DC_EVT_NL, 0, 3); + dc_link_event(dc, DC_EVT_EOL, 0, 2); + dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1); + + /* Init template microcode */ + dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1); + } else { + if (dc->di) { + dc_link_event(dc, DC_EVT_NL, 2, 3); + dc_link_event(dc, DC_EVT_EOL, 3, 2); + dc_link_event(dc, DC_EVT_NEW_DATA, 1, 1); + /* Init template microcode */ + dc_write_tmpl(dc, 2, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1); + dc_write_tmpl(dc, 3, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0); + dc_write_tmpl(dc, 4, WRG, 0, map, NULL_WAVE, 0, 0, 1); + dc_write_tmpl(dc, 1, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1); + } else { + dc_link_event(dc, DC_EVT_NL, 5, 3); + dc_link_event(dc, DC_EVT_EOL, 6, 2); + dc_link_event(dc, DC_EVT_NEW_DATA, 8, 1); + /* Init template microcode */ + dc_write_tmpl(dc, 5, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1); + dc_write_tmpl(dc, 6, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0); + dc_write_tmpl(dc, 7, WRG, 0, map, NULL_WAVE, 0, 0, 1); + dc_write_tmpl(dc, 8, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1); + } + } + dc_link_event(dc, DC_EVT_NF, 0, 0); + dc_link_event(dc, DC_EVT_NFIELD, 0, 0); + dc_link_event(dc, DC_EVT_EOF, 0, 0); + dc_link_event(dc, DC_EVT_EOFIELD, 0, 0); + dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0); + dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0); + + reg = readl(dc->base + DC_WR_CH_CONF); + if (interlaced) + reg |= DC_WR_CH_CONF_FIELD_MODE; + else + reg &= ~DC_WR_CH_CONF_FIELD_MODE; + writel(reg, dc->base + DC_WR_CH_CONF); + + writel(0x0, dc->base + DC_WR_CH_ADDR); + writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di)); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_dc_init_sync); + +void ipu_dc_enable(struct ipu_soc *ipu) +{ + ipu_module_enable(ipu, IPU_CONF_DC_EN); +} +EXPORT_SYMBOL_GPL(ipu_dc_enable); + +void ipu_dc_enable_channel(struct ipu_dc *dc) +{ + int di; + u32 reg; + + di = dc->di; + + reg = readl(dc->base + DC_WR_CH_CONF); + reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL; + writel(reg, dc->base + DC_WR_CH_CONF); +} +EXPORT_SYMBOL_GPL(ipu_dc_enable_channel); + +static irqreturn_t dc_irq_handler(int irq, void *dev_id) +{ + struct ipu_dc *dc = dev_id; + u32 reg; + + reg = readl(dc->base + DC_WR_CH_CONF); + reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; + writel(reg, dc->base + DC_WR_CH_CONF); + + /* The Freescale BSP kernel clears DIx_COUNTER_RELEASE here */ + + complete(&dc->priv->comp); + return IRQ_HANDLED; +} + +void ipu_dc_disable_channel(struct ipu_dc *dc) +{ + struct ipu_dc_priv *priv = dc->priv; + int irq, ret; + u32 val; + + /* TODO: Handle MEM_FG_SYNC differently from MEM_BG_SYNC */ + if (dc->chno == 1) + irq = priv->dc_irq; + else if (dc->chno == 5) + irq = priv->dp_irq; + else + return; + + init_completion(&priv->comp); + enable_irq(irq); + ret = wait_for_completion_timeout(&priv->comp, msecs_to_jiffies(50)); + disable_irq(irq); + if (ret <= 0) { + dev_warn(priv->dev, "DC stop timeout after 50 ms\n"); + + val = readl(dc->base + DC_WR_CH_CONF); + val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; + writel(val, dc->base + DC_WR_CH_CONF); + } +} +EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); + +void ipu_dc_disable(struct ipu_soc *ipu) +{ + ipu_module_disable(ipu, IPU_CONF_DC_EN); +} +EXPORT_SYMBOL_GPL(ipu_dc_disable); + +static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map, + int byte_num, int offset, int mask) +{ + int ptr = map * 3 + byte_num; + u32 reg; + + reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr)); + reg &= ~(0xffff << (16 * (ptr & 0x1))); + reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); + writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr)); + + reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); + reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num))); + reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); + writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map)); +} + +static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map) +{ + u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); + + writel(reg & ~(0xffff << (16 * (map & 0x1))), + priv->dc_reg + DC_MAP_CONF_PTR(map)); +} + +struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel) +{ + struct ipu_dc_priv *priv = ipu->dc_priv; + struct ipu_dc *dc; + + if (channel >= IPU_DC_NUM_CHANNELS) + return ERR_PTR(-ENODEV); + + dc = &priv->channels[channel]; + + mutex_lock(&priv->mutex); + + if (dc->in_use) { + mutex_unlock(&priv->mutex); + return ERR_PTR(-EBUSY); + } + + dc->in_use = true; + + mutex_unlock(&priv->mutex); + + return dc; +} +EXPORT_SYMBOL_GPL(ipu_dc_get); + +void ipu_dc_put(struct ipu_dc *dc) +{ + struct ipu_dc_priv *priv = dc->priv; + + mutex_lock(&priv->mutex); + dc->in_use = false; + mutex_unlock(&priv->mutex); +} +EXPORT_SYMBOL_GPL(ipu_dc_put); + +int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, + unsigned long base, unsigned long template_base) +{ + struct ipu_dc_priv *priv; + static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, + 0x78, 0, 0x94, 0xb4}; + int i, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->mutex); + + priv->dev = dev; + priv->ipu = ipu; + priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE); + priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE); + if (!priv->dc_reg || !priv->dc_tmpl_reg) + return -ENOMEM; + + for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) { + priv->channels[i].chno = i; + priv->channels[i].priv = priv; + priv->channels[i].base = priv->dc_reg + channel_offsets[i]; + } + + priv->dc_irq = ipu_map_irq(ipu, IPU_IRQ_DC_FC_1); + if (!priv->dc_irq) + return -EINVAL; + ret = devm_request_irq(dev, priv->dc_irq, dc_irq_handler, 0, NULL, + &priv->channels[1]); + if (ret < 0) + return ret; + disable_irq(priv->dc_irq); + priv->dp_irq = ipu_map_irq(ipu, IPU_IRQ_DP_SF_END); + if (!priv->dp_irq) + return -EINVAL; + ret = devm_request_irq(dev, priv->dp_irq, dc_irq_handler, 0, NULL, + &priv->channels[5]); + if (ret < 0) + return ret; + disable_irq(priv->dp_irq); + + writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) | + DC_WR_CH_CONF_PROG_DI_ID, + priv->channels[1].base + DC_WR_CH_CONF); + writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0), + priv->channels[5].base + DC_WR_CH_CONF); + + writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, + priv->dc_reg + DC_GEN); + + ipu->dc_priv = priv; + + dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n", + base, template_base); + + /* rgb24 */ + ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24); + ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */ + ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */ + ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */ + + /* rgb565 */ + ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565); + ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */ + ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */ + ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */ + + /* gbr24 */ + ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24); + ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */ + ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */ + ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */ + + /* bgr666 */ + ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666); + ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */ + 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 */ + + /* lvds666 */ + ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666); + ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */ + ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */ + ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 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; +} + +void ipu_dc_exit(struct ipu_soc *ipu) +{ +} diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c new file mode 100644 index 0000000..c490ba4 --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-di.c @@ -0,0 +1,730 @@ +/* + * Copyright (c) 2010 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> + * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. + * + * 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 <linux/export.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/platform_device.h> + +#include <video/imx-ipu-v3.h> +#include "ipu-prv.h" + +struct ipu_di { + void __iomem *base; + int id; + u32 module; + struct clk *clk_di; /* display input clock */ + struct clk *clk_ipu; /* IPU bus clock */ + struct clk *clk_di_pixel; /* resulting pixel clock */ + bool inuse; + struct ipu_soc *ipu; +}; + +static DEFINE_MUTEX(di_mutex); + +struct di_sync_config { + int run_count; + int run_src; + int offset_count; + int offset_src; + int repeat_count; + int cnt_clr_src; + int cnt_polarity_gen_en; + int cnt_polarity_clr_src; + int cnt_polarity_trigger_src; + int cnt_up; + int cnt_down; +}; + +enum di_pins { + DI_PIN11 = 0, + DI_PIN12 = 1, + DI_PIN13 = 2, + DI_PIN14 = 3, + DI_PIN15 = 4, + DI_PIN16 = 5, + DI_PIN17 = 6, + DI_PIN_CS = 7, + + DI_PIN_SER_CLK = 0, + DI_PIN_SER_RS = 1, +}; + +enum di_sync_wave { + DI_SYNC_NONE = 0, + DI_SYNC_CLK = 1, + DI_SYNC_INT_HSYNC = 2, + DI_SYNC_HSYNC = 3, + DI_SYNC_VSYNC = 4, + DI_SYNC_DE = 6, +}; + +#define SYNC_WAVE 0 + +#define DI_GENERAL 0x0000 +#define DI_BS_CLKGEN0 0x0004 +#define DI_BS_CLKGEN1 0x0008 +#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1)) +#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1)) +#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2)) +#define DI_SYNC_AS_GEN 0x0054 +#define DI_DW_GEN(gen) (0x0058 + 4 * (gen)) +#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set))) +#define DI_SER_CONF 0x015c +#define DI_SSC 0x0160 +#define DI_POL 0x0164 +#define DI_AW0 0x0168 +#define DI_AW1 0x016c +#define DI_SCR_CONF 0x0170 +#define DI_STAT 0x0174 + +#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19) +#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16) +#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3) +#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0) + +#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29) +#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25) +#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12) +#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9) +#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16) +#define DI_SW_GEN1_CNT_UP(x) (x) +#define DI_SW_GEN1_AUTO_RELOAD (0x10000000) + +#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24 +#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16 + +#define DI_GEN_POLARITY_1 (1 << 0) +#define DI_GEN_POLARITY_2 (1 << 1) +#define DI_GEN_POLARITY_3 (1 << 2) +#define DI_GEN_POLARITY_4 (1 << 3) +#define DI_GEN_POLARITY_5 (1 << 4) +#define DI_GEN_POLARITY_6 (1 << 5) +#define DI_GEN_POLARITY_7 (1 << 6) +#define DI_GEN_POLARITY_8 (1 << 7) +#define DI_GEN_POLARITY_DISP_CLK (1 << 17) +#define DI_GEN_DI_CLK_EXT (1 << 20) +#define DI_GEN_DI_VSYNC_EXT (1 << 21) + +#define DI_POL_DRDY_DATA_POLARITY (1 << 7) +#define DI_POL_DRDY_POLARITY_15 (1 << 4) + +#define DI_VSYNC_SEL_OFFSET 13 + +static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset) +{ + return readl(di->base + offset); +} + +static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset) +{ + writel(value, di->base + offset); +} + +static void ipu_di_data_wave_config(struct ipu_di *di, + int wave_gen, + int access_size, int component_size) +{ + u32 reg; + reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | + (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); + ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); +} + +static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin, + int set, int up, int down) +{ + u32 reg; + + reg = ipu_di_read(di, DI_DW_GEN(wave_gen)); + reg &= ~(0x3 << (di_pin * 2)); + reg |= set << (di_pin * 2); + ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); + + ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set)); +} + +static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, + int start, int count) +{ + u32 reg; + int i; + + for (i = 0; i < count; i++) { + struct di_sync_config *c = &config[i]; + int wave_gen = start + i + 1; + + if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) || + (c->repeat_count >= 0x1000) || + (c->cnt_up >= 0x400) || + (c->cnt_down >= 0x400)) { + dev_err(di->ipu->dev, "DI%d counters out of range.\n", + di->id); + return; + } + + reg = DI_SW_GEN0_RUN_COUNT(c->run_count) | + DI_SW_GEN0_RUN_SRC(c->run_src) | + DI_SW_GEN0_OFFSET_COUNT(c->offset_count) | + DI_SW_GEN0_OFFSET_SRC(c->offset_src); + ipu_di_write(di, reg, DI_SW_GEN0(wave_gen)); + + reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) | + DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) | + DI_SW_GEN1_CNT_POL_TRIGGER_SRC( + c->cnt_polarity_trigger_src) | + DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) | + DI_SW_GEN1_CNT_DOWN(c->cnt_down) | + DI_SW_GEN1_CNT_UP(c->cnt_up); + + /* Enable auto reload */ + if (c->repeat_count == 0) + reg |= DI_SW_GEN1_AUTO_RELOAD; + + ipu_di_write(di, reg, DI_SW_GEN1(wave_gen)); + + reg = ipu_di_read(di, DI_STP_REP(wave_gen)); + reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1))); + reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1)); + ipu_di_write(di, reg, DI_STP_REP(wave_gen)); + } +} + +static void ipu_di_sync_config_interlaced(struct ipu_di *di, + struct ipu_di_signal_cfg *sig) +{ + u32 h_total = sig->width + sig->h_sync_width + + sig->h_start_width + sig->h_end_width; + u32 v_total = sig->height + sig->v_sync_width + + sig->v_start_width + sig->v_end_width; + u32 reg; + struct di_sync_config cfg[] = { + { + .run_count = h_total / 2 - 1, + .run_src = DI_SYNC_CLK, + }, { + .run_count = h_total - 11, + .run_src = DI_SYNC_CLK, + .cnt_down = 4, + }, { + .run_count = v_total * 2 - 1, + .run_src = DI_SYNC_INT_HSYNC, + .offset_count = 1, + .offset_src = DI_SYNC_INT_HSYNC, + .cnt_down = 4, + }, { + .run_count = v_total / 2 - 1, + .run_src = DI_SYNC_HSYNC, + .offset_count = sig->v_start_width, + .offset_src = DI_SYNC_HSYNC, + .repeat_count = 2, + .cnt_clr_src = DI_SYNC_VSYNC, + }, { + .run_src = DI_SYNC_HSYNC, + .repeat_count = sig->height / 2, + .cnt_clr_src = 4, + }, { + .run_count = v_total - 1, + .run_src = DI_SYNC_HSYNC, + }, { + .run_count = v_total / 2 - 1, + .run_src = DI_SYNC_HSYNC, + .offset_count = 9, + .offset_src = DI_SYNC_HSYNC, + .repeat_count = 2, + .cnt_clr_src = DI_SYNC_VSYNC, + }, { + .run_src = DI_SYNC_CLK, + .offset_count = sig->h_start_width, + .offset_src = DI_SYNC_CLK, + .repeat_count = sig->width, + .cnt_clr_src = 5, + }, { + .run_count = v_total - 1, + .run_src = DI_SYNC_INT_HSYNC, + .offset_count = v_total / 2, + .offset_src = DI_SYNC_INT_HSYNC, + .cnt_clr_src = DI_SYNC_HSYNC, + .cnt_down = 4, + } + }; + + ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); + + /* set gentime select and tag sel */ + reg = ipu_di_read(di, DI_SW_GEN1(9)); + reg &= 0x1FFFFFFF; + reg |= (3 - 1) << 29 | 0x00008000; + ipu_di_write(di, reg, DI_SW_GEN1(9)); + + ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF); +} + +static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, + struct ipu_di_signal_cfg *sig, int div) +{ + u32 h_total = sig->width + sig->h_sync_width + sig->h_start_width + + sig->h_end_width; + u32 v_total = sig->height + sig->v_sync_width + sig->v_start_width + + sig->v_end_width; + struct di_sync_config cfg[] = { + { + /* 1: INT_HSYNC */ + .run_count = h_total - 1, + .run_src = DI_SYNC_CLK, + } , { + /* PIN2: HSYNC */ + .run_count = h_total - 1, + .run_src = DI_SYNC_CLK, + .offset_count = div * sig->v_to_h_sync, + .offset_src = DI_SYNC_CLK, + .cnt_polarity_gen_en = 1, + .cnt_polarity_trigger_src = DI_SYNC_CLK, + .cnt_down = sig->h_sync_width * 2, + } , { + /* PIN3: VSYNC */ + .run_count = v_total - 1, + .run_src = DI_SYNC_INT_HSYNC, + .cnt_polarity_gen_en = 1, + .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, + .cnt_down = sig->v_sync_width * 2, + } , { + /* 4: Line Active */ + .run_src = DI_SYNC_HSYNC, + .offset_count = sig->v_sync_width + sig->v_start_width, + .offset_src = DI_SYNC_HSYNC, + .repeat_count = sig->height, + .cnt_clr_src = DI_SYNC_VSYNC, + } , { + /* 5: Pixel Active, referenced by DC */ + .run_src = DI_SYNC_CLK, + .offset_count = sig->h_sync_width + sig->h_start_width, + .offset_src = DI_SYNC_CLK, + .repeat_count = sig->width, + .cnt_clr_src = 5, /* Line Active */ + } , { + /* unused */ + } , { + /* unused */ + } , { + /* unused */ + } , { + /* unused */ + }, + }; + /* can't use #7 and #8 for line active and pixel active counters */ + struct di_sync_config cfg_vga[] = { + { + /* 1: INT_HSYNC */ + .run_count = h_total - 1, + .run_src = DI_SYNC_CLK, + } , { + /* 2: VSYNC */ + .run_count = v_total - 1, + .run_src = DI_SYNC_INT_HSYNC, + } , { + /* 3: Line Active */ + .run_src = DI_SYNC_INT_HSYNC, + .offset_count = sig->v_sync_width + sig->v_start_width, + .offset_src = DI_SYNC_INT_HSYNC, + .repeat_count = sig->height, + .cnt_clr_src = 3 /* VSYNC */, + } , { + /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */ + .run_count = h_total - 1, + .run_src = DI_SYNC_CLK, + .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ + .offset_src = DI_SYNC_CLK, + .cnt_polarity_gen_en = 1, + .cnt_polarity_trigger_src = DI_SYNC_CLK, + .cnt_down = sig->h_sync_width * 2, + } , { + /* 5: Pixel Active signal to DC */ + .run_src = DI_SYNC_CLK, + .offset_count = sig->h_sync_width + sig->h_start_width, + .offset_src = DI_SYNC_CLK, + .repeat_count = sig->width, + .cnt_clr_src = 4, /* Line Active */ + } , { + /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */ + .run_count = v_total - 1, + .run_src = DI_SYNC_INT_HSYNC, + .offset_count = 1, /* magic value from Freescale TVE driver */ + .offset_src = DI_SYNC_INT_HSYNC, + .cnt_polarity_gen_en = 1, + .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, + .cnt_down = sig->v_sync_width * 2, + } , { + /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */ + .run_count = h_total - 1, + .run_src = DI_SYNC_CLK, + .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ + .offset_src = DI_SYNC_CLK, + .cnt_polarity_gen_en = 1, + .cnt_polarity_trigger_src = DI_SYNC_CLK, + .cnt_down = sig->h_sync_width * 2, + } , { + /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */ + .run_count = v_total - 1, + .run_src = DI_SYNC_INT_HSYNC, + .offset_count = 1, /* magic value from Freescale TVE driver */ + .offset_src = DI_SYNC_INT_HSYNC, + .cnt_polarity_gen_en = 1, + .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, + .cnt_down = sig->v_sync_width * 2, + } , { + /* unused */ + }, + }; + + ipu_di_write(di, v_total - 1, DI_SCR_CONF); + if (sig->hsync_pin == 2 && sig->vsync_pin == 3) + ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); + else + ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga)); +} + +static void ipu_di_config_clock(struct ipu_di *di, + const struct ipu_di_signal_cfg *sig) +{ + struct clk *clk; + unsigned clkgen0; + uint32_t val; + + if (sig->clkflags & IPU_DI_CLKMODE_EXT) { + /* + * CLKMODE_EXT means we must use the DI clock: this is + * needed for things like LVDS which needs to feed the + * DI and LDB with the same pixel clock. + */ + clk = di->clk_di; + + if (sig->clkflags & IPU_DI_CLKMODE_SYNC) { + /* + * CLKMODE_SYNC means that we want the DI to be + * clocked at the same rate as the parent clock. + * This is needed (eg) for LDB which needs to be + * fed with the same pixel clock. We assume that + * the LDB clock has already been set correctly. + */ + clkgen0 = 1 << 4; + } else { + /* + * We can use the divider. We should really have + * a flag here indicating whether the bridge can + * cope with a fractional divider or not. For the + * time being, let's go for simplicitly and + * reliability. + */ + unsigned long in_rate; + unsigned div; + + clk_set_rate(clk, sig->pixelclock); + + in_rate = clk_get_rate(clk); + div = (in_rate + sig->pixelclock / 2) / sig->pixelclock; + if (div == 0) + div = 1; + + clkgen0 = div << 4; + } + } else { + /* + * For other interfaces, we can arbitarily select between + * the DI specific clock and the internal IPU clock. See + * DI_GENERAL bit 20. We select the IPU clock if it can + * give us a clock rate within 1% of the requested frequency, + * otherwise we use the DI clock. + */ + unsigned long rate, clkrate; + unsigned div, error; + + clkrate = clk_get_rate(di->clk_ipu); + div = (clkrate + sig->pixelclock / 2) / sig->pixelclock; + rate = clkrate / div; + + error = rate / (sig->pixelclock / 1000); + + dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %d.%u%%\n", + rate, div, (signed)(error - 1000) / 10, error % 10); + + /* Allow a 1% error */ + if (error < 1010 && error >= 990) { + clk = di->clk_ipu; + + clkgen0 = div << 4; + } else { + unsigned long in_rate; + unsigned div; + + clk = di->clk_di; + + clk_set_rate(clk, sig->pixelclock); + + in_rate = clk_get_rate(clk); + div = (in_rate + sig->pixelclock / 2) / sig->pixelclock; + if (div == 0) + div = 1; + + clkgen0 = div << 4; + } + } + + di->clk_di_pixel = clk; + + /* Set the divider */ + ipu_di_write(di, clkgen0, DI_BS_CLKGEN0); + + /* + * Set the high/low periods. Bits 24:16 give us the falling edge, + * and bits 8:0 give the rising edge. LSB is fraction, and is + * based on the divider above. We want a 50% duty cycle, so set + * the falling edge to be half the divider. + */ + ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1); + + /* Finally select the input clock */ + val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT; + if (clk == di->clk_di) + val |= DI_GEN_DI_CLK_EXT; + ipu_di_write(di, val, DI_GENERAL); + + dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n", + sig->pixelclock, + clk_get_rate(di->clk_ipu), + clk_get_rate(di->clk_di), + clk == di->clk_di ? "DI" : "IPU", + clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4)); +} + +int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) +{ + u32 reg; + u32 di_gen, vsync_cnt; + u32 div; + u32 h_total, v_total; + + dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", + di->id, sig->width, sig->height); + + if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0)) + return -EINVAL; + + h_total = sig->width + sig->h_sync_width + sig->h_start_width + + sig->h_end_width; + v_total = sig->height + sig->v_sync_width + sig->v_start_width + + sig->v_end_width; + + dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", + clk_get_rate(di->clk_ipu), + clk_get_rate(di->clk_di), + sig->pixelclock); + + mutex_lock(&di_mutex); + + ipu_di_config_clock(di, sig); + + div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; + div = div / 16; /* Now divider is integer portion */ + + /* Setup pixel clock timing */ + /* Down time is half of period */ + ipu_di_write(di, (div << 16), DI_BS_CLKGEN1); + + ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1); + ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); + + di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT; + di_gen |= DI_GEN_DI_VSYNC_EXT; + + if (sig->interlaced) { + ipu_di_sync_config_interlaced(di, sig); + + /* set y_sel = 1 */ + di_gen |= 0x10000000; + di_gen |= DI_GEN_POLARITY_5; + di_gen |= DI_GEN_POLARITY_8; + + vsync_cnt = 7; + + if (sig->Hsync_pol) + di_gen |= DI_GEN_POLARITY_3; + if (sig->Vsync_pol) + di_gen |= DI_GEN_POLARITY_2; + } else { + ipu_di_sync_config_noninterlaced(di, sig, div); + + vsync_cnt = 3; + if (di->id == 1) + /* + * TODO: change only for TVEv2, parallel display + * uses pin 2 / 3 + */ + if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3)) + vsync_cnt = 6; + + if (sig->Hsync_pol) { + if (sig->hsync_pin == 2) + di_gen |= DI_GEN_POLARITY_2; + else if (sig->hsync_pin == 4) + di_gen |= DI_GEN_POLARITY_4; + else if (sig->hsync_pin == 7) + di_gen |= DI_GEN_POLARITY_7; + } + if (sig->Vsync_pol) { + if (sig->vsync_pin == 3) + di_gen |= DI_GEN_POLARITY_3; + else if (sig->vsync_pin == 6) + di_gen |= DI_GEN_POLARITY_6; + else if (sig->vsync_pin == 8) + di_gen |= DI_GEN_POLARITY_8; + } + } + + if (sig->clk_pol) + di_gen |= DI_GEN_POLARITY_DISP_CLK; + + ipu_di_write(di, di_gen, DI_GENERAL); + + ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002, + DI_SYNC_AS_GEN); + + reg = ipu_di_read(di, DI_POL); + reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); + + if (sig->enable_pol) + reg |= DI_POL_DRDY_POLARITY_15; + if (sig->data_pol) + reg |= DI_POL_DRDY_DATA_POLARITY; + + ipu_di_write(di, reg, DI_POL); + + mutex_unlock(&di_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel); + +int ipu_di_enable(struct ipu_di *di) +{ + int ret; + + WARN_ON(IS_ERR(di->clk_di_pixel)); + + ret = clk_prepare_enable(di->clk_di_pixel); + if (ret) + return ret; + + ipu_module_enable(di->ipu, di->module); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_di_enable); + +int ipu_di_disable(struct ipu_di *di) +{ + WARN_ON(IS_ERR(di->clk_di_pixel)); + + ipu_module_disable(di->ipu, di->module); + + clk_disable_unprepare(di->clk_di_pixel); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_di_disable); + +int ipu_di_get_num(struct ipu_di *di) +{ + return di->id; +} +EXPORT_SYMBOL_GPL(ipu_di_get_num); + +static DEFINE_MUTEX(ipu_di_lock); + +struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp) +{ + struct ipu_di *di; + + if (disp > 1) + return ERR_PTR(-EINVAL); + + di = ipu->di_priv[disp]; + + mutex_lock(&ipu_di_lock); + + if (di->inuse) { + di = ERR_PTR(-EBUSY); + goto out; + } + + di->inuse = true; +out: + mutex_unlock(&ipu_di_lock); + + return di; +} +EXPORT_SYMBOL_GPL(ipu_di_get); + +void ipu_di_put(struct ipu_di *di) +{ + mutex_lock(&ipu_di_lock); + + di->inuse = false; + + mutex_unlock(&ipu_di_lock); +} +EXPORT_SYMBOL_GPL(ipu_di_put); + +int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, + unsigned long base, + u32 module, struct clk *clk_ipu) +{ + struct ipu_di *di; + + if (id > 1) + return -ENODEV; + + di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); + if (!di) + return -ENOMEM; + + ipu->di_priv[id] = di; + + di->clk_di = devm_clk_get(dev, id ? "di1" : "di0"); + if (IS_ERR(di->clk_di)) + return PTR_ERR(di->clk_di); + + di->module = module; + di->id = id; + di->clk_ipu = clk_ipu; + di->base = devm_ioremap(dev, base, PAGE_SIZE); + if (!di->base) + return -ENOMEM; + + ipu_di_write(di, 0x10, DI_BS_CLKGEN0); + + dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n", + id, base, di->base); + di->inuse = false; + di->ipu = ipu; + + return 0; +} + +void ipu_di_exit(struct ipu_soc *ipu, int id) +{ +} diff --git a/drivers/gpu/ipu-v3/ipu-dmfc.c b/drivers/gpu/ipu-v3/ipu-dmfc.c new file mode 100644 index 0000000..042c395 --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-dmfc.c @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2010 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> + * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. + * + * 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 <linux/export.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/io.h> + +#include <video/imx-ipu-v3.h> +#include "ipu-prv.h" + +#define DMFC_RD_CHAN 0x0000 +#define DMFC_WR_CHAN 0x0004 +#define DMFC_WR_CHAN_DEF 0x0008 +#define DMFC_DP_CHAN 0x000c +#define DMFC_DP_CHAN_DEF 0x0010 +#define DMFC_GENERAL1 0x0014 +#define DMFC_GENERAL2 0x0018 +#define DMFC_IC_CTRL 0x001c +#define DMFC_WR_CHAN_ALT 0x0020 +#define DMFC_WR_CHAN_DEF_ALT 0x0024 +#define DMFC_DP_CHAN_ALT 0x0028 +#define DMFC_DP_CHAN_DEF_ALT 0x002c +#define DMFC_GENERAL1_ALT 0x0030 +#define DMFC_STAT 0x0034 + +#define DMFC_WR_CHAN_1_28 0 +#define DMFC_WR_CHAN_2_41 8 +#define DMFC_WR_CHAN_1C_42 16 +#define DMFC_WR_CHAN_2C_43 24 + +#define DMFC_DP_CHAN_5B_23 0 +#define DMFC_DP_CHAN_5F_27 8 +#define DMFC_DP_CHAN_6B_24 16 +#define DMFC_DP_CHAN_6F_29 24 + +#define DMFC_FIFO_SIZE_64 (3 << 3) +#define DMFC_FIFO_SIZE_128 (2 << 3) +#define DMFC_FIFO_SIZE_256 (1 << 3) +#define DMFC_FIFO_SIZE_512 (0 << 3) + +#define DMFC_SEGMENT(x) ((x & 0x7) << 0) +#define DMFC_BURSTSIZE_128 (0 << 6) +#define DMFC_BURSTSIZE_64 (1 << 6) +#define DMFC_BURSTSIZE_32 (2 << 6) +#define DMFC_BURSTSIZE_16 (3 << 6) + +struct dmfc_channel_data { + int ipu_channel; + unsigned long channel_reg; + unsigned long shift; + unsigned eot_shift; + unsigned max_fifo_lines; +}; + +static const struct dmfc_channel_data dmfcdata[] = { + { + .ipu_channel = IPUV3_CHANNEL_MEM_BG_SYNC, + .channel_reg = DMFC_DP_CHAN, + .shift = DMFC_DP_CHAN_5B_23, + .eot_shift = 20, + .max_fifo_lines = 3, + }, { + .ipu_channel = 24, + .channel_reg = DMFC_DP_CHAN, + .shift = DMFC_DP_CHAN_6B_24, + .eot_shift = 22, + .max_fifo_lines = 1, + }, { + .ipu_channel = IPUV3_CHANNEL_MEM_FG_SYNC, + .channel_reg = DMFC_DP_CHAN, + .shift = DMFC_DP_CHAN_5F_27, + .eot_shift = 21, + .max_fifo_lines = 2, + }, { + .ipu_channel = IPUV3_CHANNEL_MEM_DC_SYNC, + .channel_reg = DMFC_WR_CHAN, + .shift = DMFC_WR_CHAN_1_28, + .eot_shift = 16, + .max_fifo_lines = 2, + }, { + .ipu_channel = 29, + .channel_reg = DMFC_DP_CHAN, + .shift = DMFC_DP_CHAN_6F_29, + .eot_shift = 23, + .max_fifo_lines = 1, + }, +}; + +#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata) + +struct ipu_dmfc_priv; + +struct dmfc_channel { + unsigned slots; + unsigned slotmask; + unsigned segment; + int burstsize; + struct ipu_soc *ipu; + struct ipu_dmfc_priv *priv; + const struct dmfc_channel_data *data; +}; + +struct ipu_dmfc_priv { + struct ipu_soc *ipu; + struct device *dev; + struct dmfc_channel channels[DMFC_NUM_CHANNELS]; + struct mutex mutex; + unsigned long bandwidth_per_slot; + void __iomem *base; + int use_count; +}; + +int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc) +{ + struct ipu_dmfc_priv *priv = dmfc->priv; + mutex_lock(&priv->mutex); + + if (!priv->use_count) + ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN); + + priv->use_count++; + + mutex_unlock(&priv->mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); + +static void ipu_dmfc_wait_fifos(struct ipu_dmfc_priv *priv) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + while ((readl(priv->base + DMFC_STAT) & 0x02fff000) != 0x02fff000) { + if (time_after(jiffies, timeout)) { + dev_warn(priv->dev, + "Timeout waiting for DMFC FIFOs to clear\n"); + break; + } + cpu_relax(); + } +} + +void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) +{ + struct ipu_dmfc_priv *priv = dmfc->priv; + + mutex_lock(&priv->mutex); + + priv->use_count--; + + if (!priv->use_count) { + ipu_dmfc_wait_fifos(priv); + ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN); + } + + if (priv->use_count < 0) + priv->use_count = 0; + + mutex_unlock(&priv->mutex); +} +EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel); + +static int ipu_dmfc_setup_channel(struct dmfc_channel *dmfc, int slots, + int segment, int burstsize) +{ + struct ipu_dmfc_priv *priv = dmfc->priv; + u32 val, field; + + dev_dbg(priv->dev, + "dmfc: using %d slots starting from segment %d for IPU channel %d\n", + slots, segment, dmfc->data->ipu_channel); + + switch (slots) { + case 1: + field = DMFC_FIFO_SIZE_64; + break; + case 2: + field = DMFC_FIFO_SIZE_128; + break; + case 4: + field = DMFC_FIFO_SIZE_256; + break; + case 8: + field = DMFC_FIFO_SIZE_512; + break; + default: + return -EINVAL; + } + + switch (burstsize) { + case 16: + field |= DMFC_BURSTSIZE_16; + break; + case 32: + field |= DMFC_BURSTSIZE_32; + break; + case 64: + field |= DMFC_BURSTSIZE_64; + break; + case 128: + field |= DMFC_BURSTSIZE_128; + break; + } + + field |= DMFC_SEGMENT(segment); + + val = readl(priv->base + dmfc->data->channel_reg); + + val &= ~(0xff << dmfc->data->shift); + val |= field << dmfc->data->shift; + + writel(val, priv->base + dmfc->data->channel_reg); + + dmfc->slots = slots; + dmfc->segment = segment; + dmfc->burstsize = burstsize; + dmfc->slotmask = ((1 << slots) - 1) << segment; + + return 0; +} + +static int dmfc_bandwidth_to_slots(struct ipu_dmfc_priv *priv, + unsigned long bandwidth) +{ + int slots = 1; + + while (slots * priv->bandwidth_per_slot < bandwidth) + slots *= 2; + + return slots; +} + +static int dmfc_find_slots(struct ipu_dmfc_priv *priv, int slots) +{ + unsigned slotmask_need, slotmask_used = 0; + int i, segment = 0; + + slotmask_need = (1 << slots) - 1; + + for (i = 0; i < DMFC_NUM_CHANNELS; i++) + slotmask_used |= priv->channels[i].slotmask; + + while (slotmask_need <= 0xff) { + if (!(slotmask_used & slotmask_need)) + return segment; + + slotmask_need <<= 1; + segment++; + } + + return -EBUSY; +} + +void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc) +{ + struct ipu_dmfc_priv *priv = dmfc->priv; + int i; + + dev_dbg(priv->dev, "dmfc: freeing %d slots starting from segment %d\n", + dmfc->slots, dmfc->segment); + + mutex_lock(&priv->mutex); + + if (!dmfc->slots) + goto out; + + dmfc->slotmask = 0; + dmfc->slots = 0; + dmfc->segment = 0; + + for (i = 0; i < DMFC_NUM_CHANNELS; i++) + priv->channels[i].slotmask = 0; + + for (i = 0; i < DMFC_NUM_CHANNELS; i++) { + if (priv->channels[i].slots > 0) { + priv->channels[i].segment = + dmfc_find_slots(priv, priv->channels[i].slots); + priv->channels[i].slotmask = + ((1 << priv->channels[i].slots) - 1) << + priv->channels[i].segment; + } + } + + for (i = 0; i < DMFC_NUM_CHANNELS; i++) { + if (priv->channels[i].slots > 0) + ipu_dmfc_setup_channel(&priv->channels[i], + priv->channels[i].slots, + priv->channels[i].segment, + priv->channels[i].burstsize); + } +out: + mutex_unlock(&priv->mutex); +} +EXPORT_SYMBOL_GPL(ipu_dmfc_free_bandwidth); + +int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc, + unsigned long bandwidth_pixel_per_second, int burstsize) +{ + struct ipu_dmfc_priv *priv = dmfc->priv; + int slots = dmfc_bandwidth_to_slots(priv, bandwidth_pixel_per_second); + int segment = -1, ret = 0; + + dev_dbg(priv->dev, "dmfc: trying to allocate %ldMpixel/s for IPU channel %d\n", + bandwidth_pixel_per_second / 1000000, + dmfc->data->ipu_channel); + + ipu_dmfc_free_bandwidth(dmfc); + + mutex_lock(&priv->mutex); + + if (slots > 8) { + ret = -EBUSY; + goto out; + } + + /* 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 + segment = dmfc_find_slots(priv, slots); + if (segment < 0) { + ret = -EBUSY; + goto out; + } + + ipu_dmfc_setup_channel(dmfc, slots, segment, burstsize); + +out: + mutex_unlock(&priv->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(ipu_dmfc_alloc_bandwidth); + +int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width) +{ + struct ipu_dmfc_priv *priv = dmfc->priv; + u32 dmfc_gen1; + + dmfc_gen1 = readl(priv->base + DMFC_GENERAL1); + + if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines) + dmfc_gen1 |= 1 << dmfc->data->eot_shift; + else + dmfc_gen1 &= ~(1 << dmfc->data->eot_shift); + + writel(dmfc_gen1, priv->base + DMFC_GENERAL1); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_dmfc_init_channel); + +struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel) +{ + struct ipu_dmfc_priv *priv = ipu->dmfc_priv; + int i; + + for (i = 0; i < DMFC_NUM_CHANNELS; i++) + if (dmfcdata[i].ipu_channel == ipu_channel) + return &priv->channels[i]; + return ERR_PTR(-ENODEV); +} +EXPORT_SYMBOL_GPL(ipu_dmfc_get); + +void ipu_dmfc_put(struct dmfc_channel *dmfc) +{ + ipu_dmfc_free_bandwidth(dmfc); +} +EXPORT_SYMBOL_GPL(ipu_dmfc_put); + +int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, + struct clk *ipu_clk) +{ + struct ipu_dmfc_priv *priv; + int i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_ioremap(dev, base, PAGE_SIZE); + if (!priv->base) + return -ENOMEM; + + priv->dev = dev; + priv->ipu = ipu; + mutex_init(&priv->mutex); + + ipu->dmfc_priv = priv; + + for (i = 0; i < DMFC_NUM_CHANNELS; i++) { + priv->channels[i].priv = priv; + priv->channels[i].ipu = ipu; + priv->channels[i].data = &dmfcdata[i]; + } + + writel(0x0, priv->base + DMFC_WR_CHAN); + writel(0x0, priv->base + DMFC_DP_CHAN); + + /* + * We have a total bandwidth of clkrate * 4pixel divided + * into 8 slots. + */ + priv->bandwidth_per_slot = clk_get_rate(ipu_clk) * 4 / 8; + + dev_dbg(dev, "dmfc: 8 slots with %ldMpixel/s bandwidth each\n", + priv->bandwidth_per_slot / 1000000); + + writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF); + writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF); + writel(0x00000003, priv->base + DMFC_GENERAL1); + + return 0; +} + +void ipu_dmfc_exit(struct ipu_soc *ipu) +{ +} diff --git a/drivers/gpu/ipu-v3/ipu-dp.c b/drivers/gpu/ipu-v3/ipu-dp.c new file mode 100644 index 0000000..98686ed --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-dp.c @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2010 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> + * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. + * + * 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 <linux/export.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/err.h> + +#include <video/imx-ipu-v3.h> +#include "ipu-prv.h" + +#define DP_SYNC 0 +#define DP_ASYNC0 0x60 +#define DP_ASYNC1 0xBC + +#define DP_COM_CONF 0x0 +#define DP_GRAPH_WIND_CTRL 0x0004 +#define DP_FG_POS 0x0008 +#define DP_CSC_A_0 0x0044 +#define DP_CSC_A_1 0x0048 +#define DP_CSC_A_2 0x004C +#define DP_CSC_A_3 0x0050 +#define DP_CSC_0 0x0054 +#define DP_CSC_1 0x0058 + +#define DP_COM_CONF_FG_EN (1 << 0) +#define DP_COM_CONF_GWSEL (1 << 1) +#define DP_COM_CONF_GWAM (1 << 2) +#define DP_COM_CONF_GWCKE (1 << 3) +#define DP_COM_CONF_CSC_DEF_MASK (3 << 8) +#define DP_COM_CONF_CSC_DEF_OFFSET 8 +#define DP_COM_CONF_CSC_DEF_FG (3 << 8) +#define DP_COM_CONF_CSC_DEF_BG (2 << 8) +#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8) + +#define IPUV3_NUM_FLOWS 3 + +struct ipu_dp_priv; + +struct ipu_dp { + u32 flow; + bool in_use; + bool foreground; + enum ipu_color_space in_cs; +}; + +struct ipu_flow { + struct ipu_dp foreground; + struct ipu_dp background; + enum ipu_color_space out_cs; + void __iomem *base; + struct ipu_dp_priv *priv; +}; + +struct ipu_dp_priv { + struct ipu_soc *ipu; + struct device *dev; + void __iomem *base; + struct ipu_flow flow[IPUV3_NUM_FLOWS]; + struct mutex mutex; + int use_count; +}; + +static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1}; + +static inline struct ipu_flow *to_flow(struct ipu_dp *dp) +{ + if (dp->foreground) + return container_of(dp, struct ipu_flow, foreground); + else + return container_of(dp, struct ipu_flow, background); +} + +int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, + u8 alpha, bool bg_chan) +{ + struct ipu_flow *flow = to_flow(dp); + struct ipu_dp_priv *priv = flow->priv; + u32 reg; + + mutex_lock(&priv->mutex); + + reg = readl(flow->base + DP_COM_CONF); + if (bg_chan) + reg &= ~DP_COM_CONF_GWSEL; + else + reg |= DP_COM_CONF_GWSEL; + writel(reg, flow->base + DP_COM_CONF); + + if (enable) { + reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL; + writel(reg | ((u32) alpha << 24), + flow->base + DP_GRAPH_WIND_CTRL); + + reg = readl(flow->base + DP_COM_CONF); + writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); + } else { + reg = readl(flow->base + DP_COM_CONF); + writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); + } + + ipu_srm_dp_sync_update(priv->ipu); + + mutex_unlock(&priv->mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha); + +int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos) +{ + struct ipu_flow *flow = to_flow(dp); + struct ipu_dp_priv *priv = flow->priv; + + writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS); + + ipu_srm_dp_sync_update(priv->ipu); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos); + +static void ipu_dp_csc_init(struct ipu_flow *flow, + enum ipu_color_space in, + enum ipu_color_space out, + u32 place) +{ + u32 reg; + + reg = readl(flow->base + DP_COM_CONF); + reg &= ~DP_COM_CONF_CSC_DEF_MASK; + + if (in == out) { + writel(reg, flow->base + DP_COM_CONF); + return; + } + + if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) { + writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0); + writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1); + writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2); + writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3); + writel(0x3d6 | (0x0000 << 16) | (2 << 30), + flow->base + DP_CSC_0); + writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30), + flow->base + DP_CSC_1); + } else { + writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0); + writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1); + writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2); + writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3); + writel(0x000 | (0x3e42 << 16) | (1 << 30), + flow->base + DP_CSC_0); + writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30), + flow->base + DP_CSC_1); + } + + reg |= place; + + writel(reg, flow->base + DP_COM_CONF); +} + +int ipu_dp_setup_channel(struct ipu_dp *dp, + enum ipu_color_space in, + enum ipu_color_space out) +{ + struct ipu_flow *flow = to_flow(dp); + struct ipu_dp_priv *priv = flow->priv; + + mutex_lock(&priv->mutex); + + dp->in_cs = in; + + if (!dp->foreground) + flow->out_cs = out; + + if (flow->foreground.in_cs == flow->background.in_cs) { + /* + * foreground and background are of same colorspace, put + * colorspace converter after combining unit. + */ + ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs, + DP_COM_CONF_CSC_DEF_BOTH); + } else { + if (flow->foreground.in_cs == flow->out_cs) + /* + * foreground identical to output, apply color + * conversion on background + */ + ipu_dp_csc_init(flow, flow->background.in_cs, + flow->out_cs, DP_COM_CONF_CSC_DEF_BG); + else + ipu_dp_csc_init(flow, flow->foreground.in_cs, + flow->out_cs, DP_COM_CONF_CSC_DEF_FG); + } + + ipu_srm_dp_sync_update(priv->ipu); + + mutex_unlock(&priv->mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_dp_setup_channel); + +int ipu_dp_enable(struct ipu_soc *ipu) +{ + struct ipu_dp_priv *priv = ipu->dp_priv; + + mutex_lock(&priv->mutex); + + if (!priv->use_count) + ipu_module_enable(priv->ipu, IPU_CONF_DP_EN); + + priv->use_count++; + + mutex_unlock(&priv->mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_dp_enable); + +int ipu_dp_enable_channel(struct ipu_dp *dp) +{ + struct ipu_flow *flow = to_flow(dp); + struct ipu_dp_priv *priv = flow->priv; + u32 reg; + + if (!dp->foreground) + return 0; + + mutex_lock(&priv->mutex); + + reg = readl(flow->base + DP_COM_CONF); + reg |= DP_COM_CONF_FG_EN; + writel(reg, flow->base + DP_COM_CONF); + + ipu_srm_dp_sync_update(priv->ipu); + + mutex_unlock(&priv->mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_dp_enable_channel); + +void ipu_dp_disable_channel(struct ipu_dp *dp) +{ + struct ipu_flow *flow = to_flow(dp); + struct ipu_dp_priv *priv = flow->priv; + u32 reg, csc; + + if (!dp->foreground) + return; + + mutex_lock(&priv->mutex); + + reg = readl(flow->base + DP_COM_CONF); + csc = reg & DP_COM_CONF_CSC_DEF_MASK; + if (csc == DP_COM_CONF_CSC_DEF_FG) + reg &= ~DP_COM_CONF_CSC_DEF_MASK; + + reg &= ~DP_COM_CONF_FG_EN; + writel(reg, flow->base + DP_COM_CONF); + + writel(0, flow->base + DP_FG_POS); + ipu_srm_dp_sync_update(priv->ipu); + + if (ipu_idmac_channel_busy(priv->ipu, IPUV3_CHANNEL_MEM_BG_SYNC)) + ipu_wait_interrupt(priv->ipu, IPU_IRQ_DP_SF_END, 50); + + mutex_unlock(&priv->mutex); +} +EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); + +void ipu_dp_disable(struct ipu_soc *ipu) +{ + struct ipu_dp_priv *priv = ipu->dp_priv; + + mutex_lock(&priv->mutex); + + priv->use_count--; + + if (!priv->use_count) + ipu_module_disable(priv->ipu, IPU_CONF_DP_EN); + + if (priv->use_count < 0) + priv->use_count = 0; + + mutex_unlock(&priv->mutex); +} +EXPORT_SYMBOL_GPL(ipu_dp_disable); + +struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow) +{ + struct ipu_dp_priv *priv = ipu->dp_priv; + struct ipu_dp *dp; + + if ((flow >> 1) >= IPUV3_NUM_FLOWS) + return ERR_PTR(-EINVAL); + + if (flow & 1) + dp = &priv->flow[flow >> 1].foreground; + else + dp = &priv->flow[flow >> 1].background; + + if (dp->in_use) + return ERR_PTR(-EBUSY); + + dp->in_use = true; + + return dp; +} +EXPORT_SYMBOL_GPL(ipu_dp_get); + +void ipu_dp_put(struct ipu_dp *dp) +{ + dp->in_use = false; +} +EXPORT_SYMBOL_GPL(ipu_dp_put); + +int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) +{ + struct ipu_dp_priv *priv; + int i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->dev = dev; + priv->ipu = ipu; + + ipu->dp_priv = priv; + + priv->base = devm_ioremap(dev, base, PAGE_SIZE); + if (!priv->base) + return -ENOMEM; + + mutex_init(&priv->mutex); + + for (i = 0; i < IPUV3_NUM_FLOWS; i++) { + priv->flow[i].foreground.foreground = true; + priv->flow[i].base = priv->base + ipu_dp_flow_base[i]; + priv->flow[i].priv = priv; + } + + return 0; +} + +void ipu_dp_exit(struct ipu_soc *ipu) +{ +} diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h new file mode 100644 index 0000000..72e1158 --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-prv.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2010 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> + * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. + * + * 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. + */ +#ifndef __IPU_PRV_H__ +#define __IPU_PRV_H__ + +struct ipu_soc; + +#include <linux/types.h> +#include <linux/device.h> +#include <linux/clk.h> +#include <linux/platform_device.h> + +#include <video/imx-ipu-v3.h> + +#define IPUV3_CHANNEL_CSI0 0 +#define IPUV3_CHANNEL_CSI1 1 +#define IPUV3_CHANNEL_CSI2 2 +#define IPUV3_CHANNEL_CSI3 3 +#define IPUV3_CHANNEL_MEM_BG_SYNC 23 +#define IPUV3_CHANNEL_MEM_FG_SYNC 27 +#define IPUV3_CHANNEL_MEM_DC_SYNC 28 +#define IPUV3_CHANNEL_MEM_FG_SYNC_ALPHA 31 +#define IPUV3_CHANNEL_MEM_DC_ASYNC 41 +#define IPUV3_CHANNEL_ROT_ENC_MEM 45 +#define IPUV3_CHANNEL_ROT_VF_MEM 46 +#define IPUV3_CHANNEL_ROT_PP_MEM 47 +#define IPUV3_CHANNEL_ROT_ENC_MEM_OUT 48 +#define IPUV3_CHANNEL_ROT_VF_MEM_OUT 49 +#define IPUV3_CHANNEL_ROT_PP_MEM_OUT 50 +#define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA 51 + +#define IPU_MCU_T_DEFAULT 8 +#define IPU_CM_IDMAC_REG_OFS 0x00008000 +#define IPU_CM_IC_REG_OFS 0x00020000 +#define IPU_CM_IRT_REG_OFS 0x00028000 +#define IPU_CM_CSI0_REG_OFS 0x00030000 +#define IPU_CM_CSI1_REG_OFS 0x00038000 +#define IPU_CM_SMFC_REG_OFS 0x00050000 +#define IPU_CM_DC_REG_OFS 0x00058000 +#define IPU_CM_DMFC_REG_OFS 0x00060000 + +/* Register addresses */ +/* IPU Common registers */ +#define IPU_CM_REG(offset) (offset) + +#define IPU_CONF IPU_CM_REG(0) + +#define IPU_SRM_PRI1 IPU_CM_REG(0x00a0) +#define IPU_SRM_PRI2 IPU_CM_REG(0x00a4) +#define IPU_FS_PROC_FLOW1 IPU_CM_REG(0x00a8) +#define IPU_FS_PROC_FLOW2 IPU_CM_REG(0x00ac) +#define IPU_FS_PROC_FLOW3 IPU_CM_REG(0x00b0) +#define IPU_FS_DISP_FLOW1 IPU_CM_REG(0x00b4) +#define IPU_FS_DISP_FLOW2 IPU_CM_REG(0x00b8) +#define IPU_SKIP IPU_CM_REG(0x00bc) +#define IPU_DISP_ALT_CONF IPU_CM_REG(0x00c0) +#define IPU_DISP_GEN IPU_CM_REG(0x00c4) +#define IPU_DISP_ALT1 IPU_CM_REG(0x00c8) +#define IPU_DISP_ALT2 IPU_CM_REG(0x00cc) +#define IPU_DISP_ALT3 IPU_CM_REG(0x00d0) +#define IPU_DISP_ALT4 IPU_CM_REG(0x00d4) +#define IPU_SNOOP IPU_CM_REG(0x00d8) +#define IPU_MEM_RST IPU_CM_REG(0x00dc) +#define IPU_PM IPU_CM_REG(0x00e0) +#define IPU_GPR IPU_CM_REG(0x00e4) +#define IPU_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0150 + 4 * ((ch) / 32)) +#define IPU_ALT_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0168 + 4 * ((ch) / 32)) +#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32)) +#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244) +#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248) +#define IPU_SRM_STAT IPU_CM_REG(0x024C) +#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250) +#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254) +#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32)) +#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32)) +#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32)) +#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32)) + +#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n)) +#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n)) + +#define IPU_DI0_COUNTER_RELEASE (1 << 24) +#define IPU_DI1_COUNTER_RELEASE (1 << 25) + +#define IPU_IDMAC_REG(offset) (offset) + +#define IDMAC_CONF IPU_IDMAC_REG(0x0000) +#define IDMAC_CHA_EN(ch) IPU_IDMAC_REG(0x0004 + 4 * ((ch) / 32)) +#define IDMAC_SEP_ALPHA IPU_IDMAC_REG(0x000c) +#define IDMAC_ALT_SEP_ALPHA IPU_IDMAC_REG(0x0010) +#define IDMAC_CHA_PRI(ch) IPU_IDMAC_REG(0x0014 + 4 * ((ch) / 32)) +#define IDMAC_WM_EN(ch) IPU_IDMAC_REG(0x001c + 4 * ((ch) / 32)) +#define IDMAC_CH_LOCK_EN_1 IPU_IDMAC_REG(0x0024) +#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028) +#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c) +#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030) +#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034) +#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32)) +#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32)) + +#define IPU_NUM_IRQS (32 * 15) + +enum ipu_modules { + IPU_CONF_CSI0_EN = (1 << 0), + IPU_CONF_CSI1_EN = (1 << 1), + IPU_CONF_IC_EN = (1 << 2), + IPU_CONF_ROT_EN = (1 << 3), + IPU_CONF_ISP_EN = (1 << 4), + IPU_CONF_DP_EN = (1 << 5), + IPU_CONF_DI0_EN = (1 << 6), + IPU_CONF_DI1_EN = (1 << 7), + IPU_CONF_SMFC_EN = (1 << 8), + IPU_CONF_DC_EN = (1 << 9), + IPU_CONF_DMFC_EN = (1 << 10), + + IPU_CONF_VDI_EN = (1 << 12), + + IPU_CONF_IDMAC_DIS = (1 << 22), + + IPU_CONF_IC_DMFC_SEL = (1 << 25), + IPU_CONF_IC_DMFC_SYNC = (1 << 26), + IPU_CONF_VDI_DMFC_SYNC = (1 << 27), + + IPU_CONF_CSI0_DATA_SOURCE = (1 << 28), + IPU_CONF_CSI1_DATA_SOURCE = (1 << 29), + IPU_CONF_IC_INPUT = (1 << 30), + IPU_CONF_CSI_SEL = (1 << 31), +}; + +struct ipuv3_channel { + unsigned int num; + + bool enabled; + bool busy; + + struct ipu_soc *ipu; +}; + +struct ipu_dc_priv; +struct ipu_dmfc_priv; +struct ipu_di; +struct ipu_devtype; + +struct ipu_soc { + struct device *dev; + const struct ipu_devtype *devtype; + enum ipuv3_type ipu_type; + spinlock_t lock; + struct mutex channel_lock; + + void __iomem *cm_reg; + void __iomem *idmac_reg; + struct ipu_ch_param __iomem *cpmem_base; + + int usecount; + + struct clk *clk; + + struct ipuv3_channel channel[64]; + + int irq_sync; + int irq_err; + struct irq_domain *domain; + + struct ipu_dc_priv *dc_priv; + struct ipu_dp_priv *dp_priv; + struct ipu_dmfc_priv *dmfc_priv; + struct ipu_di *di_priv[2]; +}; + +void ipu_srm_dp_sync_update(struct ipu_soc *ipu); + +int ipu_module_enable(struct ipu_soc *ipu, u32 mask); +int ipu_module_disable(struct ipu_soc *ipu, u32 mask); + +bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno); +int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms); + +int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, + unsigned long base, u32 module, struct clk *ipu_clk); +void ipu_di_exit(struct ipu_soc *ipu, int id); + +int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, + struct clk *ipu_clk); +void ipu_dmfc_exit(struct ipu_soc *ipu); + +int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); +void ipu_dp_exit(struct ipu_soc *ipu); + +int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, + unsigned long template_base); +void ipu_dc_exit(struct ipu_soc *ipu); + +int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); +void ipu_cpmem_exit(struct ipu_soc *ipu); + +#endif /* __IPU_PRV_H__ */ diff --git a/drivers/staging/imx-drm/Kconfig b/drivers/staging/imx-drm/Kconfig index c6e8ba7..82fb758 100644 --- a/drivers/staging/imx-drm/Kconfig +++ b/drivers/staging/imx-drm/Kconfig @@ -39,19 +39,10 @@ config DRM_IMX_LDB Choose this to enable the internal LVDS Display Bridge (LDB) found on i.MX53 and i.MX6 processors. -config DRM_IMX_IPUV3_CORE - tristate "IPUv3 core support" - depends on DRM_IMX - depends on RESET_CONTROLLER - help - Choose this if you have a i.MX5/6 system and want - to use the IPU. This option only enables IPU base - support. - config DRM_IMX_IPUV3 tristate "DRM Support for i.MX IPUv3" depends on DRM_IMX - depends on DRM_IMX_IPUV3_CORE + depends on IMX_IPUV3_CORE help Choose this if you have a i.MX5 or i.MX6 processor. diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile index 129e3a3..582c438 100644 --- a/drivers/staging/imx-drm/Makefile +++ b/drivers/staging/imx-drm/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_DRM_IMX) += imxdrm.o obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += parallel-display.o obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o -obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/ imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o obj-$(CONFIG_DRM_IMX_IPUV3) += imx-ipuv3-crtc.o diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c index 1b44048..e7f3de5 100644 --- a/drivers/staging/imx-drm/imx-hdmi.c +++ b/drivers/staging/imx-drm/imx-hdmi.c @@ -27,8 +27,8 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> #include <drm/drm_encoder_slave.h> +#include <video/imx-ipu-v3.h> -#include "ipu-v3/imx-ipu-v3.h" #include "imx-hdmi.h" #include "imx-drm.h" diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c index a23f4f7..b12fb17 100644 --- a/drivers/staging/imx-drm/imx-tve.c +++ b/drivers/staging/imx-drm/imx-tve.c @@ -30,8 +30,8 @@ #include <drm/drmP.h> #include <drm/drm_fb_helper.h> #include <drm/drm_crtc_helper.h> +#include <video/imx-ipu-v3.h> -#include "ipu-v3/imx-ipu-v3.h" #include "imx-drm.h" #define TVE_COM_CONF_REG 0x00 diff --git a/drivers/staging/imx-drm/ipu-v3/Makefile b/drivers/staging/imx-drm/ipu-v3/Makefile deleted file mode 100644 index 28ed72e..0000000 --- a/drivers/staging/imx-drm/ipu-v3/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += imx-ipu-v3.o - -imx-ipu-v3-objs := ipu-common.o ipu-dc.o ipu-di.o ipu-dp.o ipu-dmfc.o diff --git a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h deleted file mode 100644 index c2c6fab..0000000 --- a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright 2005-2009 Freescale Semiconductor, Inc. - * - * The code contained herein is licensed under the GNU Lesser General - * Public License. You may obtain a copy of the GNU Lesser General - * Public License Version 2.1 or later at the following locations: - * - * http://www.opensource.org/licenses/lgpl-license.html - * http://www.gnu.org/copyleft/lgpl.html - */ - -#ifndef __DRM_IPU_H__ -#define __DRM_IPU_H__ - -#include <linux/types.h> -#include <linux/videodev2.h> -#include <linux/bitmap.h> -#include <linux/fb.h> - -struct ipu_soc; - -enum ipuv3_type { - IPUV3EX, - IPUV3M, - IPUV3H, -}; - -#define IPU_PIX_FMT_GBR24 v4l2_fourcc('G', 'B', 'R', '3') - -/* - * Bitfield of Display Interface signal polarities. - */ -struct ipu_di_signal_cfg { - unsigned datamask_en:1; - unsigned interlaced:1; - unsigned odd_field_first:1; - unsigned clksel_en:1; - unsigned clkidle_en:1; - unsigned data_pol:1; /* true = inverted */ - unsigned clk_pol:1; /* true = rising edge */ - unsigned enable_pol:1; - unsigned Hsync_pol:1; /* true = active high */ - unsigned Vsync_pol:1; - - u16 width; - u16 height; - u32 pixel_fmt; - u16 h_start_width; - u16 h_sync_width; - u16 h_end_width; - u16 v_start_width; - u16 v_sync_width; - u16 v_end_width; - u32 v_to_h_sync; - unsigned long pixelclock; -#define IPU_DI_CLKMODE_SYNC (1 << 0) -#define IPU_DI_CLKMODE_EXT (1 << 1) - unsigned long clkflags; - - u8 hsync_pin; - u8 vsync_pin; -}; - -enum ipu_color_space { - IPUV3_COLORSPACE_RGB, - IPUV3_COLORSPACE_YUV, - IPUV3_COLORSPACE_UNKNOWN, -}; - -struct ipuv3_channel; - -enum ipu_channel_irq { - IPU_IRQ_EOF = 0, - IPU_IRQ_NFACK = 64, - IPU_IRQ_NFB4EOF = 128, - IPU_IRQ_EOS = 192, -}; - -int ipu_map_irq(struct ipu_soc *ipu, int irq); -int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, - enum ipu_channel_irq irq); - -#define IPU_IRQ_DP_SF_START (448 + 2) -#define IPU_IRQ_DP_SF_END (448 + 3) -#define IPU_IRQ_BG_SF_END IPU_IRQ_DP_SF_END, -#define IPU_IRQ_DC_FC_0 (448 + 8) -#define IPU_IRQ_DC_FC_1 (448 + 9) -#define IPU_IRQ_DC_FC_2 (448 + 10) -#define IPU_IRQ_DC_FC_3 (448 + 11) -#define IPU_IRQ_DC_FC_4 (448 + 12) -#define IPU_IRQ_DC_FC_6 (448 + 13) -#define IPU_IRQ_VSYNC_PRE_0 (448 + 14) -#define IPU_IRQ_VSYNC_PRE_1 (448 + 15) - -/* - * IPU Image DMA Controller (idmac) functions - */ -struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned channel); -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); -void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num); - -/* - * IPU Display Controller (dc) functions - */ -struct ipu_dc; -struct ipu_di; -struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel); -void ipu_dc_put(struct ipu_dc *dc); -int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, - u32 pixel_fmt, u32 width); -void ipu_dc_enable(struct ipu_soc *ipu); -void ipu_dc_enable_channel(struct ipu_dc *dc); -void ipu_dc_disable_channel(struct ipu_dc *dc); -void ipu_dc_disable(struct ipu_soc *ipu); - -/* - * IPU Display Interface (di) functions - */ -struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp); -void ipu_di_put(struct ipu_di *); -int ipu_di_disable(struct ipu_di *); -int ipu_di_enable(struct ipu_di *); -int ipu_di_get_num(struct ipu_di *); -int ipu_di_init_sync_panel(struct ipu_di *, struct ipu_di_signal_cfg *sig); - -/* - * IPU Display Multi FIFO Controller (dmfc) functions - */ -struct dmfc_channel; -int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc); -void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc); -int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc, - unsigned long bandwidth_mbs, int burstsize); -void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc); -int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width); -struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipuv3_channel); -void ipu_dmfc_put(struct dmfc_channel *dmfc); - -/* - * IPU Display Processor (dp) functions - */ -#define IPU_DP_FLOW_SYNC_BG 0 -#define IPU_DP_FLOW_SYNC_FG 1 -#define IPU_DP_FLOW_ASYNC0_BG 2 -#define IPU_DP_FLOW_ASYNC0_FG 3 -#define IPU_DP_FLOW_ASYNC1_BG 4 -#define IPU_DP_FLOW_ASYNC1_FG 5 - -struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow); -void ipu_dp_put(struct ipu_dp *); -int ipu_dp_enable(struct ipu_soc *ipu); -int ipu_dp_enable_channel(struct ipu_dp *dp); -void ipu_dp_disable_channel(struct ipu_dp *dp); -void ipu_dp_disable(struct ipu_soc *ipu); -int ipu_dp_setup_channel(struct ipu_dp *dp, - enum ipu_color_space in, enum ipu_color_space out); -int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos); -int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha, - bool bg_chan); - -#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size)) - -#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22) -#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22) -#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4) -#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1) -#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1) -#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14) -#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14) - -#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10) -#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9) -#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13) -#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12) -#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1) -#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1) -#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12) -#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11) -#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10) -#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7) -#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10) -#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1) -#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1) -#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7) -#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1) -#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1) -#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3) -#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2) -#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1) -#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3) -#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2) -#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1) -#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1) -#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1) -#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1) -#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1) -#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1) -#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13) -#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12) -#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29) -#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29) -#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20) -#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7) -#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4) -#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1) -#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3) -#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2) -#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7) -#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14) -#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3) -#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3) -#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3) -#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3) -#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5) -#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5) -#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5) -#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5) -#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1) -#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1) -#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1) - -struct ipu_cpmem_word { - u32 data[5]; - u32 res[3]; -}; - -struct ipu_ch_param { - struct ipu_cpmem_word word[2]; -}; - -void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v); -u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs); -struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel); -void ipu_ch_param_dump(struct ipu_ch_param __iomem *p); - -static inline void ipu_ch_param_zero(struct ipu_ch_param __iomem *p) -{ - int i; - void __iomem *base = p; - - for (i = 0; i < sizeof(*p) / sizeof(u32); i++) - writel(0, base + i * sizeof(u32)); -} - -static inline void ipu_cpmem_set_buffer(struct ipu_ch_param __iomem *p, - int bufnum, dma_addr_t buf) -{ - if (bufnum) - ipu_ch_param_write_field(p, IPU_FIELD_EBA1, buf >> 3); - else - ipu_ch_param_write_field(p, IPU_FIELD_EBA0, buf >> 3); -} - -static inline void ipu_cpmem_set_resolution(struct ipu_ch_param __iomem *p, - int xres, int yres) -{ - ipu_ch_param_write_field(p, IPU_FIELD_FW, xres - 1); - ipu_ch_param_write_field(p, IPU_FIELD_FH, yres - 1); -} - -static inline void ipu_cpmem_set_stride(struct ipu_ch_param __iomem *p, - int stride) -{ - ipu_ch_param_write_field(p, IPU_FIELD_SLY, stride - 1); -} - -void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel); - -struct ipu_rgb { - struct fb_bitfield red; - struct fb_bitfield green; - struct fb_bitfield blue; - struct fb_bitfield transp; - int bits_per_pixel; -}; - -struct ipu_image { - struct v4l2_pix_format pix; - struct v4l2_rect rect; - dma_addr_t phys; -}; - -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 *, - const struct ipu_rgb *rgb); - -static inline void ipu_cpmem_interlaced_scan(struct ipu_ch_param *p, - int stride) -{ - ipu_ch_param_write_field(p, IPU_FIELD_SO, 1); - ipu_ch_param_write_field(p, IPU_FIELD_ILO, stride / 8); - ipu_ch_param_write_field(p, IPU_FIELD_SLY, (stride * 2) - 1); -}; - -void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format, - int stride, int height); -void ipu_cpmem_set_yuv_interleaved(struct ipu_ch_param __iomem *p, - u32 pixel_format); -void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p, - u32 pixel_format, int stride, int u_offset, int v_offset); -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, - int burstsize) -{ - ipu_ch_param_write_field(p, IPU_FIELD_NPB, burstsize - 1); -}; - -struct ipu_client_platformdata { - int di; - int dc; - int dp; - int dmfc; - int dma[2]; -}; - -#endif /* __DRM_IPU_H__ */ diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c deleted file mode 100644 index a1f7b20..0000000 --- a/drivers/staging/imx-drm/ipu-v3/ipu-common.c +++ /dev/null @@ -1,1292 +0,0 @@ -/* - * Copyright (c) 2010 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> - * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. - * - * 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 <linux/module.h> -#include <linux/export.h> -#include <linux/types.h> -#include <linux/reset.h> -#include <linux/platform_device.h> -#include <linux/err.h> -#include <linux/spinlock.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/clk.h> -#include <linux/list.h> -#include <linux/irq.h> -#include <linux/irqchip/chained_irq.h> -#include <linux/irqdomain.h> -#include <linux/of_device.h> - -#include <drm/drm_fourcc.h> - -#include "imx-ipu-v3.h" -#include "ipu-prv.h" - -static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) -{ - return readl(ipu->cm_reg + offset); -} - -static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) -{ - writel(value, ipu->cm_reg + offset); -} - -static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset) -{ - return readl(ipu->idmac_reg + offset); -} - -static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value, - unsigned offset) -{ - writel(value, ipu->idmac_reg + offset); -} - -void ipu_srm_dp_sync_update(struct ipu_soc *ipu) -{ - u32 val; - - val = ipu_cm_read(ipu, IPU_SRM_PRI2); - val |= 0x8; - ipu_cm_write(ipu, val, IPU_SRM_PRI2); -} -EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update); - -struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel) -{ - struct ipu_soc *ipu = channel->ipu; - - return ipu->cpmem_base + channel->num; -} -EXPORT_SYMBOL_GPL(ipu_get_cpmem); - -void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel) -{ - struct ipu_soc *ipu = channel->ipu; - struct ipu_ch_param __iomem *p = ipu_get_cpmem(channel); - u32 val; - - if (ipu->ipu_type == IPUV3EX) - ipu_ch_param_write_field(p, IPU_FIELD_ID, 1); - - val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(channel->num)); - val |= 1 << (channel->num % 32); - ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(channel->num)); -}; -EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority); - -void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v) -{ - u32 bit = (wbs >> 8) % 160; - u32 size = wbs & 0xff; - u32 word = (wbs >> 8) / 160; - u32 i = bit / 32; - u32 ofs = bit % 32; - u32 mask = (1 << size) - 1; - u32 val; - - pr_debug("%s %d %d %d\n", __func__, word, bit , size); - - val = readl(&base->word[word].data[i]); - val &= ~(mask << ofs); - val |= v << ofs; - writel(val, &base->word[word].data[i]); - - if ((bit + size - 1) / 32 > i) { - val = readl(&base->word[word].data[i + 1]); - val &= ~(mask >> (ofs ? (32 - ofs) : 0)); - val |= v >> (ofs ? (32 - ofs) : 0); - writel(val, &base->word[word].data[i + 1]); - } -} -EXPORT_SYMBOL_GPL(ipu_ch_param_write_field); - -u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs) -{ - u32 bit = (wbs >> 8) % 160; - u32 size = wbs & 0xff; - u32 word = (wbs >> 8) / 160; - u32 i = bit / 32; - u32 ofs = bit % 32; - u32 mask = (1 << size) - 1; - u32 val = 0; - - pr_debug("%s %d %d %d\n", __func__, word, bit , size); - - val = (readl(&base->word[word].data[i]) >> ofs) & mask; - - if ((bit + size - 1) / 32 > i) { - u32 tmp; - tmp = readl(&base->word[word].data[i + 1]); - tmp &= mask >> (ofs ? (32 - ofs) : 0); - val |= tmp << (ofs ? (32 - ofs) : 0); - } - - return val; -} -EXPORT_SYMBOL_GPL(ipu_ch_param_read_field); - -int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *p, - const struct ipu_rgb *rgb) -{ - int bpp = 0, npb = 0, ro, go, bo, to; - - ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset; - go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset; - bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset; - to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset; - - ipu_ch_param_write_field(p, IPU_FIELD_WID0, rgb->red.length - 1); - ipu_ch_param_write_field(p, IPU_FIELD_OFS0, ro); - ipu_ch_param_write_field(p, IPU_FIELD_WID1, rgb->green.length - 1); - ipu_ch_param_write_field(p, IPU_FIELD_OFS1, go); - ipu_ch_param_write_field(p, IPU_FIELD_WID2, rgb->blue.length - 1); - ipu_ch_param_write_field(p, IPU_FIELD_OFS2, bo); - - if (rgb->transp.length) { - ipu_ch_param_write_field(p, IPU_FIELD_WID3, - rgb->transp.length - 1); - ipu_ch_param_write_field(p, IPU_FIELD_OFS3, to); - } else { - ipu_ch_param_write_field(p, IPU_FIELD_WID3, 7); - ipu_ch_param_write_field(p, IPU_FIELD_OFS3, - rgb->bits_per_pixel); - } - - switch (rgb->bits_per_pixel) { - case 32: - bpp = 0; - npb = 15; - break; - case 24: - bpp = 1; - npb = 19; - break; - case 16: - bpp = 3; - npb = 31; - break; - case 8: - bpp = 5; - npb = 63; - break; - default: - return -EINVAL; - } - ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp); - ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb); - ipu_ch_param_write_field(p, IPU_FIELD_PFS, 7); /* rgb mode */ - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb); - -int ipu_cpmem_set_format_passthrough(struct ipu_ch_param __iomem *p, - int width) -{ - int bpp = 0, npb = 0; - - switch (width) { - case 32: - bpp = 0; - npb = 15; - break; - case 24: - bpp = 1; - npb = 19; - break; - case 16: - bpp = 3; - npb = 31; - break; - case 8: - bpp = 5; - npb = 63; - break; - default: - return -EINVAL; - } - - ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp); - ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb); - ipu_ch_param_write_field(p, IPU_FIELD_PFS, 6); /* raw mode */ - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough); - -void ipu_cpmem_set_yuv_interleaved(struct ipu_ch_param __iomem *p, - u32 pixel_format) -{ - switch (pixel_format) { - case V4L2_PIX_FMT_UYVY: - ipu_ch_param_write_field(p, IPU_FIELD_BPP, 3); /* bits/pixel */ - ipu_ch_param_write_field(p, IPU_FIELD_PFS, 0xA); /* pix format */ - ipu_ch_param_write_field(p, IPU_FIELD_NPB, 31); /* burst size */ - break; - case V4L2_PIX_FMT_YUYV: - ipu_ch_param_write_field(p, IPU_FIELD_BPP, 3); /* bits/pixel */ - ipu_ch_param_write_field(p, IPU_FIELD_PFS, 0x8); /* pix format */ - ipu_ch_param_write_field(p, IPU_FIELD_NPB, 31); /* burst size */ - break; - } -} -EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved); - -void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p, - u32 pixel_format, int stride, int u_offset, int v_offset) -{ - switch (pixel_format) { - case V4L2_PIX_FMT_YUV420: - ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1); - ipu_ch_param_write_field(p, IPU_FIELD_UBO, u_offset / 8); - ipu_ch_param_write_field(p, IPU_FIELD_VBO, v_offset / 8); - break; - case V4L2_PIX_FMT_YVU420: - ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1); - ipu_ch_param_write_field(p, IPU_FIELD_UBO, v_offset / 8); - ipu_ch_param_write_field(p, IPU_FIELD_VBO, u_offset / 8); - break; - } -} -EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full); - -void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format, - int stride, int height) -{ - int u_offset, v_offset; - int uv_stride = 0; - - switch (pixel_format) { - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - uv_stride = stride / 2; - u_offset = stride * height; - v_offset = u_offset + (uv_stride * height / 2); - ipu_cpmem_set_yuv_planar_full(p, pixel_format, stride, - u_offset, v_offset); - break; - } -} -EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar); - -static const struct ipu_rgb def_rgb_32 = { - .red = { .offset = 16, .length = 8, }, - .green = { .offset = 8, .length = 8, }, - .blue = { .offset = 0, .length = 8, }, - .transp = { .offset = 24, .length = 8, }, - .bits_per_pixel = 32, -}; - -static const struct ipu_rgb def_bgr_32 = { - .red = { .offset = 0, .length = 8, }, - .green = { .offset = 8, .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 = 16, .length = 8, }, - .green = { .offset = 8, .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 = 0, .length = 8, }, - .green = { .offset = 8, .length = 8, }, - .blue = { .offset = 16, .length = 8, }, - .transp = { .offset = 0, .length = 0, }, - .bits_per_pixel = 24, -}; - -static const struct ipu_rgb def_rgb_16 = { - .red = { .offset = 11, .length = 5, }, - .green = { .offset = 5, .length = 6, }, - .blue = { .offset = 0, .length = 5, }, - .transp = { .offset = 0, .length = 0, }, - .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) -#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \ - (pix->width * pix->height / 4) + \ - (pix->width * (y) / 4) + (x) / 2) - -int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 drm_fourcc) -{ - 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 DRM_FORMAT_UYVY: - /* bits/pixel */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3); - /* pix format */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0xA); - /* burst size */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31); - break; - case DRM_FORMAT_YUYV: - /* bits/pixel */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3); - /* pix format */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0x8); - /* burst size */ - ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31); - break; - case DRM_FORMAT_ABGR8888: - case DRM_FORMAT_XBGR8888: - ipu_cpmem_set_format_rgb(cpmem, &def_bgr_32); - break; - case DRM_FORMAT_ARGB8888: - case DRM_FORMAT_XRGB8888: - ipu_cpmem_set_format_rgb(cpmem, &def_rgb_32); - break; - case DRM_FORMAT_BGR888: - ipu_cpmem_set_format_rgb(cpmem, &def_bgr_24); - break; - case DRM_FORMAT_RGB888: - ipu_cpmem_set_format_rgb(cpmem, &def_rgb_24); - break; - 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; - } - - return 0; -} -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) -{ - struct v4l2_pix_format *pix = &image->pix; - int y_offset, u_offset, v_offset; - - pr_debug("%s: resolution: %dx%d stride: %d\n", - __func__, pix->width, pix->height, - pix->bytesperline); - - ipu_cpmem_set_resolution(cpmem, image->rect.width, - image->rect.height); - ipu_cpmem_set_stride(cpmem, pix->bytesperline); - - ipu_cpmem_set_fmt(cpmem, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat)); - - switch (pix->pixelformat) { - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - y_offset = Y_OFFSET(pix, image->rect.left, image->rect.top); - u_offset = U_OFFSET(pix, image->rect.left, - image->rect.top) - y_offset; - v_offset = V_OFFSET(pix, image->rect.left, - image->rect.top) - y_offset; - - ipu_cpmem_set_yuv_planar_full(cpmem, pix->pixelformat, - pix->bytesperline, u_offset, v_offset); - ipu_cpmem_set_buffer(cpmem, 0, image->phys + y_offset); - break; - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - ipu_cpmem_set_buffer(cpmem, 0, image->phys + - image->rect.left * 2 + - image->rect.top * image->pix.bytesperline); - break; - case V4L2_PIX_FMT_RGB32: - case V4L2_PIX_FMT_BGR32: - ipu_cpmem_set_buffer(cpmem, 0, image->phys + - image->rect.left * 4 + - image->rect.top * image->pix.bytesperline); - break; - case V4L2_PIX_FMT_RGB565: - ipu_cpmem_set_buffer(cpmem, 0, image->phys + - image->rect.left * 2 + - image->rect.top * image->pix.bytesperline); - break; - case V4L2_PIX_FMT_RGB24: - case V4L2_PIX_FMT_BGR24: - ipu_cpmem_set_buffer(cpmem, 0, image->phys + - image->rect.left * 3 + - image->rect.top * image->pix.bytesperline); - break; - default: - return -EINVAL; - } - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_cpmem_set_image); - -enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) -{ - switch (pixelformat) { - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - return IPUV3_COLORSPACE_YUV; - case V4L2_PIX_FMT_RGB32: - case V4L2_PIX_FMT_BGR32: - case V4L2_PIX_FMT_RGB24: - case V4L2_PIX_FMT_BGR24: - case V4L2_PIX_FMT_RGB565: - return IPUV3_COLORSPACE_RGB; - default: - return IPUV3_COLORSPACE_UNKNOWN; - } -} -EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); - -struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) -{ - struct ipuv3_channel *channel; - - dev_dbg(ipu->dev, "%s %d\n", __func__, num); - - if (num > 63) - return ERR_PTR(-ENODEV); - - mutex_lock(&ipu->channel_lock); - - channel = &ipu->channel[num]; - - if (channel->busy) { - channel = ERR_PTR(-EBUSY); - goto out; - } - - channel->busy = true; - channel->num = num; - -out: - mutex_unlock(&ipu->channel_lock); - - return channel; -} -EXPORT_SYMBOL_GPL(ipu_idmac_get); - -void ipu_idmac_put(struct ipuv3_channel *channel) -{ - struct ipu_soc *ipu = channel->ipu; - - dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); - - mutex_lock(&ipu->channel_lock); - - channel->busy = false; - - mutex_unlock(&ipu->channel_lock); -} -EXPORT_SYMBOL_GPL(ipu_idmac_put); - -#define idma_mask(ch) (1 << (ch & 0x1f)) - -void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, - bool doublebuffer) -{ - struct ipu_soc *ipu = channel->ipu; - unsigned long flags; - u32 reg; - - spin_lock_irqsave(&ipu->lock, flags); - - reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); - if (doublebuffer) - reg |= idma_mask(channel->num); - else - reg &= ~idma_mask(channel->num); - ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); - - spin_unlock_irqrestore(&ipu->lock, flags); -} -EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); - -int ipu_module_enable(struct ipu_soc *ipu, u32 mask) -{ - unsigned long lock_flags; - u32 val; - - spin_lock_irqsave(&ipu->lock, lock_flags); - - val = ipu_cm_read(ipu, IPU_DISP_GEN); - - if (mask & IPU_CONF_DI0_EN) - val |= IPU_DI0_COUNTER_RELEASE; - if (mask & IPU_CONF_DI1_EN) - val |= IPU_DI1_COUNTER_RELEASE; - - ipu_cm_write(ipu, val, IPU_DISP_GEN); - - val = ipu_cm_read(ipu, IPU_CONF); - val |= mask; - ipu_cm_write(ipu, val, IPU_CONF); - - spin_unlock_irqrestore(&ipu->lock, lock_flags); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_module_enable); - -int ipu_module_disable(struct ipu_soc *ipu, u32 mask) -{ - unsigned long lock_flags; - u32 val; - - spin_lock_irqsave(&ipu->lock, lock_flags); - - val = ipu_cm_read(ipu, IPU_CONF); - val &= ~mask; - ipu_cm_write(ipu, val, IPU_CONF); - - val = ipu_cm_read(ipu, IPU_DISP_GEN); - - if (mask & IPU_CONF_DI0_EN) - val &= ~IPU_DI0_COUNTER_RELEASE; - if (mask & IPU_CONF_DI1_EN) - val &= ~IPU_DI1_COUNTER_RELEASE; - - ipu_cm_write(ipu, val, IPU_DISP_GEN); - - spin_unlock_irqrestore(&ipu->lock, lock_flags); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_module_disable); - -void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) -{ - struct ipu_soc *ipu = channel->ipu; - unsigned int chno = channel->num; - unsigned long flags; - - spin_lock_irqsave(&ipu->lock, flags); - - /* Mark buffer as ready. */ - if (buf_num == 0) - ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); - else - ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); - - spin_unlock_irqrestore(&ipu->lock, flags); -} -EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); - -int ipu_idmac_enable_channel(struct ipuv3_channel *channel) -{ - struct ipu_soc *ipu = channel->ipu; - u32 val; - unsigned long flags; - - spin_lock_irqsave(&ipu->lock, flags); - - val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); - val |= idma_mask(channel->num); - ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); - - spin_unlock_irqrestore(&ipu->lock, flags); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); - -bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) -{ - return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); -} -EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); - -int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) -{ - struct ipu_soc *ipu = channel->ipu; - unsigned long timeout; - - 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)) - return -ETIMEDOUT; - cpu_relax(); - } - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); - -int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(ms); - ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32)); - while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) { - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - cpu_relax(); - } - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_wait_interrupt); - -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) */ - val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); - val &= ~idma_mask(channel->num); - ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); - - /* Set channel buffers NOT to be ready */ - ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ - - if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & - idma_mask(channel->num)) { - ipu_cm_write(ipu, idma_mask(channel->num), - IPU_CHA_BUF0_RDY(channel->num)); - } - - if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & - idma_mask(channel->num)) { - ipu_cm_write(ipu, idma_mask(channel->num), - IPU_CHA_BUF1_RDY(channel->num)); - } - - ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ - - /* Reset the double buffer */ - val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); - val &= ~idma_mask(channel->num); - ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); - - spin_unlock_irqrestore(&ipu->lock, flags); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); - -static int ipu_memory_reset(struct ipu_soc *ipu) -{ - unsigned long timeout; - - ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); - - timeout = jiffies + msecs_to_jiffies(1000); - while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { - if (time_after(jiffies, timeout)) - return -ETIME; - cpu_relax(); - } - - return 0; -} - -struct ipu_devtype { - const char *name; - unsigned long cm_ofs; - unsigned long cpmem_ofs; - unsigned long srm_ofs; - unsigned long tpm_ofs; - unsigned long disp0_ofs; - unsigned long disp1_ofs; - unsigned long dc_tmpl_ofs; - unsigned long vdi_ofs; - enum ipuv3_type type; -}; - -static struct ipu_devtype ipu_type_imx51 = { - .name = "IPUv3EX", - .cm_ofs = 0x1e000000, - .cpmem_ofs = 0x1f000000, - .srm_ofs = 0x1f040000, - .tpm_ofs = 0x1f060000, - .disp0_ofs = 0x1e040000, - .disp1_ofs = 0x1e048000, - .dc_tmpl_ofs = 0x1f080000, - .vdi_ofs = 0x1e068000, - .type = IPUV3EX, -}; - -static struct ipu_devtype ipu_type_imx53 = { - .name = "IPUv3M", - .cm_ofs = 0x06000000, - .cpmem_ofs = 0x07000000, - .srm_ofs = 0x07040000, - .tpm_ofs = 0x07060000, - .disp0_ofs = 0x06040000, - .disp1_ofs = 0x06048000, - .dc_tmpl_ofs = 0x07080000, - .vdi_ofs = 0x06068000, - .type = IPUV3M, -}; - -static struct ipu_devtype ipu_type_imx6q = { - .name = "IPUv3H", - .cm_ofs = 0x00200000, - .cpmem_ofs = 0x00300000, - .srm_ofs = 0x00340000, - .tpm_ofs = 0x00360000, - .disp0_ofs = 0x00240000, - .disp1_ofs = 0x00248000, - .dc_tmpl_ofs = 0x00380000, - .vdi_ofs = 0x00268000, - .type = IPUV3H, -}; - -static const struct of_device_id imx_ipu_dt_ids[] = { - { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, - { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, - { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); - -static int ipu_submodules_init(struct ipu_soc *ipu, - struct platform_device *pdev, unsigned long ipu_base, - struct clk *ipu_clk) -{ - char *unit; - int ret; - struct device *dev = &pdev->dev; - const struct ipu_devtype *devtype = ipu->devtype; - - ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, - IPU_CONF_DI0_EN, ipu_clk); - if (ret) { - unit = "di0"; - goto err_di_0; - } - - ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, - IPU_CONF_DI1_EN, ipu_clk); - if (ret) { - unit = "di1"; - goto err_di_1; - } - - ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + - IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); - if (ret) { - unit = "dc_template"; - goto err_dc; - } - - ret = ipu_dmfc_init(ipu, dev, ipu_base + - devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); - if (ret) { - unit = "dmfc"; - goto err_dmfc; - } - - ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); - if (ret) { - unit = "dp"; - goto err_dp; - } - - return 0; - -err_dp: - ipu_dmfc_exit(ipu); -err_dmfc: - ipu_dc_exit(ipu); -err_dc: - ipu_di_exit(ipu, 1); -err_di_1: - ipu_di_exit(ipu, 0); -err_di_0: - dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); - return ret; -} - -static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) -{ - unsigned long status; - int i, bit, irq; - - for (i = 0; i < num_regs; i++) { - - status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); - status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); - - for_each_set_bit(bit, &status, 32) { - irq = irq_linear_revmap(ipu->domain, - regs[i] * 32 + bit); - if (irq) - generic_handle_irq(irq); - } - } -} - -static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc) -{ - struct ipu_soc *ipu = irq_desc_get_handler_data(desc); - const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; - struct irq_chip *chip = irq_get_chip(irq); - - chained_irq_enter(chip, desc); - - ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); - - chained_irq_exit(chip, desc); -} - -static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc) -{ - struct ipu_soc *ipu = irq_desc_get_handler_data(desc); - const int int_reg[] = { 4, 5, 8, 9}; - struct irq_chip *chip = irq_get_chip(irq); - - chained_irq_enter(chip, desc); - - ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); - - chained_irq_exit(chip, desc); -} - -int ipu_map_irq(struct ipu_soc *ipu, int irq) -{ - int virq; - - virq = irq_linear_revmap(ipu->domain, irq); - if (!virq) - virq = irq_create_mapping(ipu->domain, irq); - - return virq; -} -EXPORT_SYMBOL_GPL(ipu_map_irq); - -int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, - enum ipu_channel_irq irq_type) -{ - return ipu_map_irq(ipu, irq_type + channel->num); -} -EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); - -static void ipu_submodules_exit(struct ipu_soc *ipu) -{ - ipu_dp_exit(ipu); - ipu_dmfc_exit(ipu); - ipu_dc_exit(ipu); - ipu_di_exit(ipu, 1); - ipu_di_exit(ipu, 0); -} - -static int platform_remove_devices_fn(struct device *dev, void *unused) -{ - struct platform_device *pdev = to_platform_device(dev); - - platform_device_unregister(pdev); - - return 0; -} - -static void platform_device_unregister_children(struct platform_device *pdev) -{ - device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); -} - -struct ipu_platform_reg { - struct ipu_client_platformdata pdata; - const char *name; -}; - -static const struct ipu_platform_reg client_reg[] = { - { - .pdata = { - .di = 0, - .dc = 5, - .dp = IPU_DP_FLOW_SYNC_BG, - .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, - .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, - }, - .name = "imx-ipuv3-crtc", - }, { - .pdata = { - .di = 1, - .dc = 1, - .dp = -EINVAL, - .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, - .dma[1] = -EINVAL, - }, - .name = "imx-ipuv3-crtc", - }, -}; - -static DEFINE_MUTEX(ipu_client_id_mutex); -static int ipu_client_id; - -static int ipu_add_client_devices(struct ipu_soc *ipu) -{ - struct device *dev = ipu->dev; - unsigned i; - int id, ret; - - mutex_lock(&ipu_client_id_mutex); - id = ipu_client_id; - ipu_client_id += ARRAY_SIZE(client_reg); - mutex_unlock(&ipu_client_id_mutex); - - for (i = 0; i < ARRAY_SIZE(client_reg); i++) { - const struct ipu_platform_reg *reg = &client_reg[i]; - struct platform_device *pdev; - - pdev = platform_device_register_data(dev, reg->name, - id++, ®->pdata, sizeof(reg->pdata)); - - if (IS_ERR(pdev)) - goto err_register; - } - - return 0; - -err_register: - platform_device_unregister_children(to_platform_device(dev)); - - return ret; -} - - -static int ipu_irq_init(struct ipu_soc *ipu) -{ - struct irq_chip_generic *gc; - struct irq_chip_type *ct; - unsigned long unused[IPU_NUM_IRQS / 32] = { - 0x400100d0, 0xffe000fd, - 0x400100d0, 0xffe000fd, - 0x400100d0, 0xffe000fd, - 0x4077ffff, 0xffe7e1fd, - 0x23fffffe, 0x8880fff0, - 0xf98fe7d0, 0xfff81fff, - 0x400100d0, 0xffe000fd, - 0x00000000, - }; - int ret, i; - - ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, - &irq_generic_chip_ops, ipu); - if (!ipu->domain) { - dev_err(ipu->dev, "failed to add irq domain\n"); - return -ENODEV; - } - - ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", - handle_level_irq, 0, - IRQF_VALID, 0); - if (ret < 0) { - dev_err(ipu->dev, "failed to alloc generic irq chips\n"); - irq_domain_remove(ipu->domain); - return ret; - } - - for (i = 0; i < IPU_NUM_IRQS; i += 32) { - gc = irq_get_domain_generic_chip(ipu->domain, i); - gc->reg_base = ipu->cm_reg; - gc->unused = unused[i / 32]; - ct = gc->chip_types; - ct->chip.irq_ack = irq_gc_ack_set_bit; - ct->chip.irq_mask = irq_gc_mask_clr_bit; - ct->chip.irq_unmask = irq_gc_mask_set_bit; - ct->regs.ack = IPU_INT_STAT(i / 32); - ct->regs.mask = IPU_INT_CTRL(i / 32); - } - - irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler); - irq_set_handler_data(ipu->irq_sync, ipu); - irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler); - irq_set_handler_data(ipu->irq_err, ipu); - - return 0; -} - -static void ipu_irq_exit(struct ipu_soc *ipu) -{ - int i, irq; - - irq_set_chained_handler(ipu->irq_err, NULL); - irq_set_handler_data(ipu->irq_err, NULL); - irq_set_chained_handler(ipu->irq_sync, NULL); - irq_set_handler_data(ipu->irq_sync, NULL); - - /* TODO: remove irq_domain_generic_chips */ - - for (i = 0; i < IPU_NUM_IRQS; i++) { - irq = irq_linear_revmap(ipu->domain, i); - if (irq) - irq_dispose_mapping(irq); - } - - irq_domain_remove(ipu->domain); -} - -static int ipu_probe(struct platform_device *pdev) -{ - const struct of_device_id *of_id = - of_match_device(imx_ipu_dt_ids, &pdev->dev); - struct ipu_soc *ipu; - struct resource *res; - unsigned long ipu_base; - int i, ret, irq_sync, irq_err; - const struct ipu_devtype *devtype; - - devtype = of_id->data; - - irq_sync = platform_get_irq(pdev, 0); - irq_err = platform_get_irq(pdev, 1); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", - irq_sync, irq_err); - - if (!res || irq_sync < 0 || irq_err < 0) - return -ENODEV; - - ipu_base = res->start; - - ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); - if (!ipu) - return -ENODEV; - - for (i = 0; i < 64; i++) - ipu->channel[i].ipu = ipu; - ipu->devtype = devtype; - ipu->ipu_type = devtype->type; - - spin_lock_init(&ipu->lock); - mutex_init(&ipu->channel_lock); - - dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", - ipu_base + devtype->cm_ofs); - dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", - ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); - dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", - ipu_base + devtype->cpmem_ofs); - dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", - ipu_base + devtype->disp0_ofs); - dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", - ipu_base + devtype->disp1_ofs); - dev_dbg(&pdev->dev, "srm: 0x%08lx\n", - ipu_base + devtype->srm_ofs); - dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", - ipu_base + devtype->tpm_ofs); - dev_dbg(&pdev->dev, "dc: 0x%08lx\n", - ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); - dev_dbg(&pdev->dev, "ic: 0x%08lx\n", - ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); - dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", - ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); - dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", - ipu_base + devtype->vdi_ofs); - - ipu->cm_reg = devm_ioremap(&pdev->dev, - ipu_base + devtype->cm_ofs, PAGE_SIZE); - ipu->idmac_reg = devm_ioremap(&pdev->dev, - ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, - PAGE_SIZE); - ipu->cpmem_base = devm_ioremap(&pdev->dev, - ipu_base + devtype->cpmem_ofs, PAGE_SIZE); - - if (!ipu->cm_reg || !ipu->idmac_reg || !ipu->cpmem_base) - return -ENOMEM; - - ipu->clk = devm_clk_get(&pdev->dev, "bus"); - if (IS_ERR(ipu->clk)) { - ret = PTR_ERR(ipu->clk); - dev_err(&pdev->dev, "clk_get failed with %d", ret); - return ret; - } - - platform_set_drvdata(pdev, ipu); - - ret = clk_prepare_enable(ipu->clk); - if (ret) { - dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); - return ret; - } - - ipu->dev = &pdev->dev; - ipu->irq_sync = irq_sync; - ipu->irq_err = irq_err; - - ret = ipu_irq_init(ipu); - if (ret) - goto out_failed_irq; - - ret = device_reset(&pdev->dev); - if (ret) { - dev_err(&pdev->dev, "failed to reset: %d\n", ret); - goto out_failed_reset; - } - ret = ipu_memory_reset(ipu); - if (ret) - goto out_failed_reset; - - /* Set MCU_T to divide MCU access window into 2 */ - ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), - IPU_DISP_GEN); - - ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); - if (ret) - goto failed_submodules_init; - - ret = ipu_add_client_devices(ipu); - if (ret) { - dev_err(&pdev->dev, "adding client devices failed with %d\n", - ret); - goto failed_add_clients; - } - - dev_info(&pdev->dev, "%s probed\n", devtype->name); - - return 0; - -failed_add_clients: - ipu_submodules_exit(ipu); -failed_submodules_init: -out_failed_reset: - ipu_irq_exit(ipu); -out_failed_irq: - clk_disable_unprepare(ipu->clk); - return ret; -} - -static int ipu_remove(struct platform_device *pdev) -{ - struct ipu_soc *ipu = platform_get_drvdata(pdev); - - platform_device_unregister_children(pdev); - ipu_submodules_exit(ipu); - ipu_irq_exit(ipu); - - clk_disable_unprepare(ipu->clk); - - return 0; -} - -static struct platform_driver imx_ipu_driver = { - .driver = { - .name = "imx-ipuv3", - .of_match_table = imx_ipu_dt_ids, - }, - .probe = ipu_probe, - .remove = ipu_remove, -}; - -module_platform_driver(imx_ipu_driver); - -MODULE_ALIAS("platform:imx-ipuv3"); -MODULE_DESCRIPTION("i.MX IPU v3 driver"); -MODULE_AUTHOR("Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c deleted file mode 100644 index 784a4a1..0000000 --- a/drivers/staging/imx-drm/ipu-v3/ipu-dc.c +++ /dev/null @@ -1,461 +0,0 @@ -/* - * Copyright (c) 2010 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> - * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. - * - * 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 <linux/export.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/io.h> - -#include "../imx-drm.h" -#include "imx-ipu-v3.h" -#include "ipu-prv.h" - -#define DC_MAP_CONF_PTR(n) (0x108 + ((n) & ~0x1) * 2) -#define DC_MAP_CONF_VAL(n) (0x144 + ((n) & ~0x1) * 2) - -#define DC_EVT_NF 0 -#define DC_EVT_NL 1 -#define DC_EVT_EOF 2 -#define DC_EVT_NFIELD 3 -#define DC_EVT_EOL 4 -#define DC_EVT_EOFIELD 5 -#define DC_EVT_NEW_ADDR 6 -#define DC_EVT_NEW_CHAN 7 -#define DC_EVT_NEW_DATA 8 - -#define DC_EVT_NEW_ADDR_W_0 0 -#define DC_EVT_NEW_ADDR_W_1 1 -#define DC_EVT_NEW_CHAN_W_0 2 -#define DC_EVT_NEW_CHAN_W_1 3 -#define DC_EVT_NEW_DATA_W_0 4 -#define DC_EVT_NEW_DATA_W_1 5 -#define DC_EVT_NEW_ADDR_R_0 6 -#define DC_EVT_NEW_ADDR_R_1 7 -#define DC_EVT_NEW_CHAN_R_0 8 -#define DC_EVT_NEW_CHAN_R_1 9 -#define DC_EVT_NEW_DATA_R_0 10 -#define DC_EVT_NEW_DATA_R_1 11 - -#define DC_WR_CH_CONF 0x0 -#define DC_WR_CH_ADDR 0x4 -#define DC_RL_CH(evt) (8 + ((evt) & ~0x1) * 2) - -#define DC_GEN 0xd4 -#define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4) -#define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4) -#define DC_STAT 0x1c8 - -#define WROD(lf) (0x18 | ((lf) << 1)) -#define WRG 0x01 -#define WCLK 0xc9 - -#define SYNC_WAVE 0 -#define NULL_WAVE (-1) - -#define DC_GEN_SYNC_1_6_SYNC (2 << 1) -#define DC_GEN_SYNC_PRIORITY_1 (1 << 7) - -#define DC_WR_CH_CONF_WORD_SIZE_8 (0 << 0) -#define DC_WR_CH_CONF_WORD_SIZE_16 (1 << 0) -#define DC_WR_CH_CONF_WORD_SIZE_24 (2 << 0) -#define DC_WR_CH_CONF_WORD_SIZE_32 (3 << 0) -#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i) (((i) & 0x1) << 3) -#define DC_WR_CH_CONF_DISP_ID_SERIAL (2 << 3) -#define DC_WR_CH_CONF_DISP_ID_ASYNC (3 << 4) -#define DC_WR_CH_CONF_FIELD_MODE (1 << 9) -#define DC_WR_CH_CONF_PROG_TYPE_NORMAL (4 << 5) -#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5) -#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2) -#define DC_WR_CH_CONF_PROG_DISP_ID(i) (((i) & 0x1) << 3) - -#define IPU_DC_NUM_CHANNELS 10 - -struct ipu_dc_priv; - -enum ipu_dc_map { - IPU_DC_MAP_RGB24, - IPU_DC_MAP_RGB565, - IPU_DC_MAP_GBR24, /* TVEv2 */ - IPU_DC_MAP_BGR666, - IPU_DC_MAP_LVDS666, - IPU_DC_MAP_BGR24, -}; - -struct ipu_dc { - /* The display interface number assigned to this dc channel */ - unsigned int di; - void __iomem *base; - struct ipu_dc_priv *priv; - int chno; - bool in_use; -}; - -struct ipu_dc_priv { - void __iomem *dc_reg; - void __iomem *dc_tmpl_reg; - struct ipu_soc *ipu; - struct device *dev; - struct ipu_dc channels[IPU_DC_NUM_CHANNELS]; - struct mutex mutex; - struct completion comp; - int dc_irq; - int dp_irq; -}; - -static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority) -{ - u32 reg; - - reg = readl(dc->base + DC_RL_CH(event)); - reg &= ~(0xffff << (16 * (event & 0x1))); - reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); - writel(reg, dc->base + DC_RL_CH(event)); -} - -static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand, - int map, int wave, int glue, int sync, int stop) -{ - struct ipu_dc_priv *priv = dc->priv; - u32 reg1, reg2; - - if (opcode == WCLK) { - reg1 = (operand << 20) & 0xfff00000; - reg2 = operand >> 12 | opcode << 1 | stop << 9; - } else if (opcode == WRG) { - reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000); - reg2 = operand >> 17 | opcode << 7 | stop << 9; - } else { - reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000); - reg2 = operand >> 12 | opcode << 4 | stop << 9; - } - writel(reg1, priv->dc_tmpl_reg + word * 8); - writel(reg2, priv->dc_tmpl_reg + word * 8 + 4); -} - -static int ipu_pixfmt_to_map(u32 fmt) -{ - switch (fmt) { - case V4L2_PIX_FMT_RGB24: - return IPU_DC_MAP_RGB24; - case V4L2_PIX_FMT_RGB565: - return IPU_DC_MAP_RGB565; - case IPU_PIX_FMT_GBR24: - return IPU_DC_MAP_GBR24; - case V4L2_PIX_FMT_BGR666: - return IPU_DC_MAP_BGR666; - case v4l2_fourcc('L', 'V', 'D', '6'): - return IPU_DC_MAP_LVDS666; - case V4L2_PIX_FMT_BGR24: - return IPU_DC_MAP_BGR24; - default: - return -EINVAL; - } -} - -int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, - u32 pixel_fmt, u32 width) -{ - struct ipu_dc_priv *priv = dc->priv; - u32 reg = 0; - int map; - - dc->di = ipu_di_get_num(di); - - map = ipu_pixfmt_to_map(pixel_fmt); - if (map < 0) { - dev_dbg(priv->dev, "IPU_DISP: No MAP\n"); - return map; - } - - if (interlaced) { - dc_link_event(dc, DC_EVT_NL, 0, 3); - dc_link_event(dc, DC_EVT_EOL, 0, 2); - dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1); - - /* Init template microcode */ - dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1); - } else { - if (dc->di) { - dc_link_event(dc, DC_EVT_NL, 2, 3); - dc_link_event(dc, DC_EVT_EOL, 3, 2); - dc_link_event(dc, DC_EVT_NEW_DATA, 1, 1); - /* Init template microcode */ - dc_write_tmpl(dc, 2, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1); - dc_write_tmpl(dc, 3, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0); - dc_write_tmpl(dc, 4, WRG, 0, map, NULL_WAVE, 0, 0, 1); - dc_write_tmpl(dc, 1, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1); - } else { - dc_link_event(dc, DC_EVT_NL, 5, 3); - dc_link_event(dc, DC_EVT_EOL, 6, 2); - dc_link_event(dc, DC_EVT_NEW_DATA, 8, 1); - /* Init template microcode */ - dc_write_tmpl(dc, 5, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1); - dc_write_tmpl(dc, 6, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0); - dc_write_tmpl(dc, 7, WRG, 0, map, NULL_WAVE, 0, 0, 1); - dc_write_tmpl(dc, 8, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1); - } - } - dc_link_event(dc, DC_EVT_NF, 0, 0); - dc_link_event(dc, DC_EVT_NFIELD, 0, 0); - dc_link_event(dc, DC_EVT_EOF, 0, 0); - dc_link_event(dc, DC_EVT_EOFIELD, 0, 0); - dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0); - dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0); - - reg = readl(dc->base + DC_WR_CH_CONF); - if (interlaced) - reg |= DC_WR_CH_CONF_FIELD_MODE; - else - reg &= ~DC_WR_CH_CONF_FIELD_MODE; - writel(reg, dc->base + DC_WR_CH_CONF); - - writel(0x0, dc->base + DC_WR_CH_ADDR); - writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di)); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_dc_init_sync); - -void ipu_dc_enable(struct ipu_soc *ipu) -{ - ipu_module_enable(ipu, IPU_CONF_DC_EN); -} -EXPORT_SYMBOL_GPL(ipu_dc_enable); - -void ipu_dc_enable_channel(struct ipu_dc *dc) -{ - int di; - u32 reg; - - di = dc->di; - - reg = readl(dc->base + DC_WR_CH_CONF); - reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL; - writel(reg, dc->base + DC_WR_CH_CONF); -} -EXPORT_SYMBOL_GPL(ipu_dc_enable_channel); - -static irqreturn_t dc_irq_handler(int irq, void *dev_id) -{ - struct ipu_dc *dc = dev_id; - u32 reg; - - reg = readl(dc->base + DC_WR_CH_CONF); - reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; - writel(reg, dc->base + DC_WR_CH_CONF); - - /* The Freescale BSP kernel clears DIx_COUNTER_RELEASE here */ - - complete(&dc->priv->comp); - return IRQ_HANDLED; -} - -void ipu_dc_disable_channel(struct ipu_dc *dc) -{ - struct ipu_dc_priv *priv = dc->priv; - int irq, ret; - u32 val; - - /* TODO: Handle MEM_FG_SYNC differently from MEM_BG_SYNC */ - if (dc->chno == 1) - irq = priv->dc_irq; - else if (dc->chno == 5) - irq = priv->dp_irq; - else - return; - - init_completion(&priv->comp); - enable_irq(irq); - ret = wait_for_completion_timeout(&priv->comp, msecs_to_jiffies(50)); - disable_irq(irq); - if (ret <= 0) { - dev_warn(priv->dev, "DC stop timeout after 50 ms\n"); - - val = readl(dc->base + DC_WR_CH_CONF); - val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; - writel(val, dc->base + DC_WR_CH_CONF); - } -} -EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); - -void ipu_dc_disable(struct ipu_soc *ipu) -{ - ipu_module_disable(ipu, IPU_CONF_DC_EN); -} -EXPORT_SYMBOL_GPL(ipu_dc_disable); - -static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map, - int byte_num, int offset, int mask) -{ - int ptr = map * 3 + byte_num; - u32 reg; - - reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr)); - reg &= ~(0xffff << (16 * (ptr & 0x1))); - reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); - writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr)); - - reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); - reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num))); - reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); - writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map)); -} - -static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map) -{ - u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); - - writel(reg & ~(0xffff << (16 * (map & 0x1))), - priv->dc_reg + DC_MAP_CONF_PTR(map)); -} - -struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel) -{ - struct ipu_dc_priv *priv = ipu->dc_priv; - struct ipu_dc *dc; - - if (channel >= IPU_DC_NUM_CHANNELS) - return ERR_PTR(-ENODEV); - - dc = &priv->channels[channel]; - - mutex_lock(&priv->mutex); - - if (dc->in_use) { - mutex_unlock(&priv->mutex); - return ERR_PTR(-EBUSY); - } - - dc->in_use = true; - - mutex_unlock(&priv->mutex); - - return dc; -} -EXPORT_SYMBOL_GPL(ipu_dc_get); - -void ipu_dc_put(struct ipu_dc *dc) -{ - struct ipu_dc_priv *priv = dc->priv; - - mutex_lock(&priv->mutex); - dc->in_use = false; - mutex_unlock(&priv->mutex); -} -EXPORT_SYMBOL_GPL(ipu_dc_put); - -int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, - unsigned long base, unsigned long template_base) -{ - struct ipu_dc_priv *priv; - static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, - 0x78, 0, 0x94, 0xb4}; - int i, ret; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - mutex_init(&priv->mutex); - - priv->dev = dev; - priv->ipu = ipu; - priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE); - priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE); - if (!priv->dc_reg || !priv->dc_tmpl_reg) - return -ENOMEM; - - for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) { - priv->channels[i].chno = i; - priv->channels[i].priv = priv; - priv->channels[i].base = priv->dc_reg + channel_offsets[i]; - } - - priv->dc_irq = ipu_map_irq(ipu, IPU_IRQ_DC_FC_1); - if (!priv->dc_irq) - return -EINVAL; - ret = devm_request_irq(dev, priv->dc_irq, dc_irq_handler, 0, NULL, - &priv->channels[1]); - if (ret < 0) - return ret; - disable_irq(priv->dc_irq); - priv->dp_irq = ipu_map_irq(ipu, IPU_IRQ_DP_SF_END); - if (!priv->dp_irq) - return -EINVAL; - ret = devm_request_irq(dev, priv->dp_irq, dc_irq_handler, 0, NULL, - &priv->channels[5]); - if (ret < 0) - return ret; - disable_irq(priv->dp_irq); - - writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) | - DC_WR_CH_CONF_PROG_DI_ID, - priv->channels[1].base + DC_WR_CH_CONF); - writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0), - priv->channels[5].base + DC_WR_CH_CONF); - - writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, - priv->dc_reg + DC_GEN); - - ipu->dc_priv = priv; - - dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n", - base, template_base); - - /* rgb24 */ - ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24); - ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */ - ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */ - ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */ - - /* rgb565 */ - ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565); - ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */ - ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */ - ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */ - - /* gbr24 */ - ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24); - ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */ - ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */ - ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */ - - /* bgr666 */ - ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666); - ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */ - 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 */ - - /* lvds666 */ - ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666); - ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */ - ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */ - ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 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; -} - -void ipu_dc_exit(struct ipu_soc *ipu) -{ -} diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-di.c b/drivers/staging/imx-drm/ipu-v3/ipu-di.c deleted file mode 100644 index 849b3e120..0000000 --- a/drivers/staging/imx-drm/ipu-v3/ipu-di.c +++ /dev/null @@ -1,730 +0,0 @@ -/* - * Copyright (c) 2010 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> - * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. - * - * 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 <linux/export.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/io.h> -#include <linux/err.h> -#include <linux/platform_device.h> - -#include "imx-ipu-v3.h" -#include "ipu-prv.h" - -struct ipu_di { - void __iomem *base; - int id; - u32 module; - struct clk *clk_di; /* display input clock */ - struct clk *clk_ipu; /* IPU bus clock */ - struct clk *clk_di_pixel; /* resulting pixel clock */ - bool inuse; - struct ipu_soc *ipu; -}; - -static DEFINE_MUTEX(di_mutex); - -struct di_sync_config { - int run_count; - int run_src; - int offset_count; - int offset_src; - int repeat_count; - int cnt_clr_src; - int cnt_polarity_gen_en; - int cnt_polarity_clr_src; - int cnt_polarity_trigger_src; - int cnt_up; - int cnt_down; -}; - -enum di_pins { - DI_PIN11 = 0, - DI_PIN12 = 1, - DI_PIN13 = 2, - DI_PIN14 = 3, - DI_PIN15 = 4, - DI_PIN16 = 5, - DI_PIN17 = 6, - DI_PIN_CS = 7, - - DI_PIN_SER_CLK = 0, - DI_PIN_SER_RS = 1, -}; - -enum di_sync_wave { - DI_SYNC_NONE = 0, - DI_SYNC_CLK = 1, - DI_SYNC_INT_HSYNC = 2, - DI_SYNC_HSYNC = 3, - DI_SYNC_VSYNC = 4, - DI_SYNC_DE = 6, -}; - -#define SYNC_WAVE 0 - -#define DI_GENERAL 0x0000 -#define DI_BS_CLKGEN0 0x0004 -#define DI_BS_CLKGEN1 0x0008 -#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1)) -#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1)) -#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2)) -#define DI_SYNC_AS_GEN 0x0054 -#define DI_DW_GEN(gen) (0x0058 + 4 * (gen)) -#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set))) -#define DI_SER_CONF 0x015c -#define DI_SSC 0x0160 -#define DI_POL 0x0164 -#define DI_AW0 0x0168 -#define DI_AW1 0x016c -#define DI_SCR_CONF 0x0170 -#define DI_STAT 0x0174 - -#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19) -#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16) -#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3) -#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0) - -#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29) -#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25) -#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12) -#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9) -#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16) -#define DI_SW_GEN1_CNT_UP(x) (x) -#define DI_SW_GEN1_AUTO_RELOAD (0x10000000) - -#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24 -#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16 - -#define DI_GEN_POLARITY_1 (1 << 0) -#define DI_GEN_POLARITY_2 (1 << 1) -#define DI_GEN_POLARITY_3 (1 << 2) -#define DI_GEN_POLARITY_4 (1 << 3) -#define DI_GEN_POLARITY_5 (1 << 4) -#define DI_GEN_POLARITY_6 (1 << 5) -#define DI_GEN_POLARITY_7 (1 << 6) -#define DI_GEN_POLARITY_8 (1 << 7) -#define DI_GEN_POLARITY_DISP_CLK (1 << 17) -#define DI_GEN_DI_CLK_EXT (1 << 20) -#define DI_GEN_DI_VSYNC_EXT (1 << 21) - -#define DI_POL_DRDY_DATA_POLARITY (1 << 7) -#define DI_POL_DRDY_POLARITY_15 (1 << 4) - -#define DI_VSYNC_SEL_OFFSET 13 - -static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset) -{ - return readl(di->base + offset); -} - -static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset) -{ - writel(value, di->base + offset); -} - -static void ipu_di_data_wave_config(struct ipu_di *di, - int wave_gen, - int access_size, int component_size) -{ - u32 reg; - reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | - (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); - ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); -} - -static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin, - int set, int up, int down) -{ - u32 reg; - - reg = ipu_di_read(di, DI_DW_GEN(wave_gen)); - reg &= ~(0x3 << (di_pin * 2)); - reg |= set << (di_pin * 2); - ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); - - ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set)); -} - -static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, - int start, int count) -{ - u32 reg; - int i; - - for (i = 0; i < count; i++) { - struct di_sync_config *c = &config[i]; - int wave_gen = start + i + 1; - - if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) || - (c->repeat_count >= 0x1000) || - (c->cnt_up >= 0x400) || - (c->cnt_down >= 0x400)) { - dev_err(di->ipu->dev, "DI%d counters out of range.\n", - di->id); - return; - } - - reg = DI_SW_GEN0_RUN_COUNT(c->run_count) | - DI_SW_GEN0_RUN_SRC(c->run_src) | - DI_SW_GEN0_OFFSET_COUNT(c->offset_count) | - DI_SW_GEN0_OFFSET_SRC(c->offset_src); - ipu_di_write(di, reg, DI_SW_GEN0(wave_gen)); - - reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) | - DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) | - DI_SW_GEN1_CNT_POL_TRIGGER_SRC( - c->cnt_polarity_trigger_src) | - DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) | - DI_SW_GEN1_CNT_DOWN(c->cnt_down) | - DI_SW_GEN1_CNT_UP(c->cnt_up); - - /* Enable auto reload */ - if (c->repeat_count == 0) - reg |= DI_SW_GEN1_AUTO_RELOAD; - - ipu_di_write(di, reg, DI_SW_GEN1(wave_gen)); - - reg = ipu_di_read(di, DI_STP_REP(wave_gen)); - reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1))); - reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1)); - ipu_di_write(di, reg, DI_STP_REP(wave_gen)); - } -} - -static void ipu_di_sync_config_interlaced(struct ipu_di *di, - struct ipu_di_signal_cfg *sig) -{ - u32 h_total = sig->width + sig->h_sync_width + - sig->h_start_width + sig->h_end_width; - u32 v_total = sig->height + sig->v_sync_width + - sig->v_start_width + sig->v_end_width; - u32 reg; - struct di_sync_config cfg[] = { - { - .run_count = h_total / 2 - 1, - .run_src = DI_SYNC_CLK, - }, { - .run_count = h_total - 11, - .run_src = DI_SYNC_CLK, - .cnt_down = 4, - }, { - .run_count = v_total * 2 - 1, - .run_src = DI_SYNC_INT_HSYNC, - .offset_count = 1, - .offset_src = DI_SYNC_INT_HSYNC, - .cnt_down = 4, - }, { - .run_count = v_total / 2 - 1, - .run_src = DI_SYNC_HSYNC, - .offset_count = sig->v_start_width, - .offset_src = DI_SYNC_HSYNC, - .repeat_count = 2, - .cnt_clr_src = DI_SYNC_VSYNC, - }, { - .run_src = DI_SYNC_HSYNC, - .repeat_count = sig->height / 2, - .cnt_clr_src = 4, - }, { - .run_count = v_total - 1, - .run_src = DI_SYNC_HSYNC, - }, { - .run_count = v_total / 2 - 1, - .run_src = DI_SYNC_HSYNC, - .offset_count = 9, - .offset_src = DI_SYNC_HSYNC, - .repeat_count = 2, - .cnt_clr_src = DI_SYNC_VSYNC, - }, { - .run_src = DI_SYNC_CLK, - .offset_count = sig->h_start_width, - .offset_src = DI_SYNC_CLK, - .repeat_count = sig->width, - .cnt_clr_src = 5, - }, { - .run_count = v_total - 1, - .run_src = DI_SYNC_INT_HSYNC, - .offset_count = v_total / 2, - .offset_src = DI_SYNC_INT_HSYNC, - .cnt_clr_src = DI_SYNC_HSYNC, - .cnt_down = 4, - } - }; - - ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); - - /* set gentime select and tag sel */ - reg = ipu_di_read(di, DI_SW_GEN1(9)); - reg &= 0x1FFFFFFF; - reg |= (3 - 1) << 29 | 0x00008000; - ipu_di_write(di, reg, DI_SW_GEN1(9)); - - ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF); -} - -static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, - struct ipu_di_signal_cfg *sig, int div) -{ - u32 h_total = sig->width + sig->h_sync_width + sig->h_start_width + - sig->h_end_width; - u32 v_total = sig->height + sig->v_sync_width + sig->v_start_width + - sig->v_end_width; - struct di_sync_config cfg[] = { - { - /* 1: INT_HSYNC */ - .run_count = h_total - 1, - .run_src = DI_SYNC_CLK, - } , { - /* PIN2: HSYNC */ - .run_count = h_total - 1, - .run_src = DI_SYNC_CLK, - .offset_count = div * sig->v_to_h_sync, - .offset_src = DI_SYNC_CLK, - .cnt_polarity_gen_en = 1, - .cnt_polarity_trigger_src = DI_SYNC_CLK, - .cnt_down = sig->h_sync_width * 2, - } , { - /* PIN3: VSYNC */ - .run_count = v_total - 1, - .run_src = DI_SYNC_INT_HSYNC, - .cnt_polarity_gen_en = 1, - .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, - .cnt_down = sig->v_sync_width * 2, - } , { - /* 4: Line Active */ - .run_src = DI_SYNC_HSYNC, - .offset_count = sig->v_sync_width + sig->v_start_width, - .offset_src = DI_SYNC_HSYNC, - .repeat_count = sig->height, - .cnt_clr_src = DI_SYNC_VSYNC, - } , { - /* 5: Pixel Active, referenced by DC */ - .run_src = DI_SYNC_CLK, - .offset_count = sig->h_sync_width + sig->h_start_width, - .offset_src = DI_SYNC_CLK, - .repeat_count = sig->width, - .cnt_clr_src = 5, /* Line Active */ - } , { - /* unused */ - } , { - /* unused */ - } , { - /* unused */ - } , { - /* unused */ - }, - }; - /* can't use #7 and #8 for line active and pixel active counters */ - struct di_sync_config cfg_vga[] = { - { - /* 1: INT_HSYNC */ - .run_count = h_total - 1, - .run_src = DI_SYNC_CLK, - } , { - /* 2: VSYNC */ - .run_count = v_total - 1, - .run_src = DI_SYNC_INT_HSYNC, - } , { - /* 3: Line Active */ - .run_src = DI_SYNC_INT_HSYNC, - .offset_count = sig->v_sync_width + sig->v_start_width, - .offset_src = DI_SYNC_INT_HSYNC, - .repeat_count = sig->height, - .cnt_clr_src = 3 /* VSYNC */, - } , { - /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */ - .run_count = h_total - 1, - .run_src = DI_SYNC_CLK, - .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ - .offset_src = DI_SYNC_CLK, - .cnt_polarity_gen_en = 1, - .cnt_polarity_trigger_src = DI_SYNC_CLK, - .cnt_down = sig->h_sync_width * 2, - } , { - /* 5: Pixel Active signal to DC */ - .run_src = DI_SYNC_CLK, - .offset_count = sig->h_sync_width + sig->h_start_width, - .offset_src = DI_SYNC_CLK, - .repeat_count = sig->width, - .cnt_clr_src = 4, /* Line Active */ - } , { - /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */ - .run_count = v_total - 1, - .run_src = DI_SYNC_INT_HSYNC, - .offset_count = 1, /* magic value from Freescale TVE driver */ - .offset_src = DI_SYNC_INT_HSYNC, - .cnt_polarity_gen_en = 1, - .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, - .cnt_down = sig->v_sync_width * 2, - } , { - /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */ - .run_count = h_total - 1, - .run_src = DI_SYNC_CLK, - .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ - .offset_src = DI_SYNC_CLK, - .cnt_polarity_gen_en = 1, - .cnt_polarity_trigger_src = DI_SYNC_CLK, - .cnt_down = sig->h_sync_width * 2, - } , { - /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */ - .run_count = v_total - 1, - .run_src = DI_SYNC_INT_HSYNC, - .offset_count = 1, /* magic value from Freescale TVE driver */ - .offset_src = DI_SYNC_INT_HSYNC, - .cnt_polarity_gen_en = 1, - .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, - .cnt_down = sig->v_sync_width * 2, - } , { - /* unused */ - }, - }; - - ipu_di_write(di, v_total - 1, DI_SCR_CONF); - if (sig->hsync_pin == 2 && sig->vsync_pin == 3) - ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); - else - ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga)); -} - -static void ipu_di_config_clock(struct ipu_di *di, - const struct ipu_di_signal_cfg *sig) -{ - struct clk *clk; - unsigned clkgen0; - uint32_t val; - - if (sig->clkflags & IPU_DI_CLKMODE_EXT) { - /* - * CLKMODE_EXT means we must use the DI clock: this is - * needed for things like LVDS which needs to feed the - * DI and LDB with the same pixel clock. - */ - clk = di->clk_di; - - if (sig->clkflags & IPU_DI_CLKMODE_SYNC) { - /* - * CLKMODE_SYNC means that we want the DI to be - * clocked at the same rate as the parent clock. - * This is needed (eg) for LDB which needs to be - * fed with the same pixel clock. We assume that - * the LDB clock has already been set correctly. - */ - clkgen0 = 1 << 4; - } else { - /* - * We can use the divider. We should really have - * a flag here indicating whether the bridge can - * cope with a fractional divider or not. For the - * time being, let's go for simplicitly and - * reliability. - */ - unsigned long in_rate; - unsigned div; - - clk_set_rate(clk, sig->pixelclock); - - in_rate = clk_get_rate(clk); - div = (in_rate + sig->pixelclock / 2) / sig->pixelclock; - if (div == 0) - div = 1; - - clkgen0 = div << 4; - } - } else { - /* - * For other interfaces, we can arbitarily select between - * the DI specific clock and the internal IPU clock. See - * DI_GENERAL bit 20. We select the IPU clock if it can - * give us a clock rate within 1% of the requested frequency, - * otherwise we use the DI clock. - */ - unsigned long rate, clkrate; - unsigned div, error; - - clkrate = clk_get_rate(di->clk_ipu); - div = (clkrate + sig->pixelclock / 2) / sig->pixelclock; - rate = clkrate / div; - - error = rate / (sig->pixelclock / 1000); - - dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %d.%u%%\n", - rate, div, (signed)(error - 1000) / 10, error % 10); - - /* Allow a 1% error */ - if (error < 1010 && error >= 990) { - clk = di->clk_ipu; - - clkgen0 = div << 4; - } else { - unsigned long in_rate; - unsigned div; - - clk = di->clk_di; - - clk_set_rate(clk, sig->pixelclock); - - in_rate = clk_get_rate(clk); - div = (in_rate + sig->pixelclock / 2) / sig->pixelclock; - if (div == 0) - div = 1; - - clkgen0 = div << 4; - } - } - - di->clk_di_pixel = clk; - - /* Set the divider */ - ipu_di_write(di, clkgen0, DI_BS_CLKGEN0); - - /* - * Set the high/low periods. Bits 24:16 give us the falling edge, - * and bits 8:0 give the rising edge. LSB is fraction, and is - * based on the divider above. We want a 50% duty cycle, so set - * the falling edge to be half the divider. - */ - ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1); - - /* Finally select the input clock */ - val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT; - if (clk == di->clk_di) - val |= DI_GEN_DI_CLK_EXT; - ipu_di_write(di, val, DI_GENERAL); - - dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n", - sig->pixelclock, - clk_get_rate(di->clk_ipu), - clk_get_rate(di->clk_di), - clk == di->clk_di ? "DI" : "IPU", - clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4)); -} - -int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) -{ - u32 reg; - u32 di_gen, vsync_cnt; - u32 div; - u32 h_total, v_total; - - dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", - di->id, sig->width, sig->height); - - if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0)) - return -EINVAL; - - h_total = sig->width + sig->h_sync_width + sig->h_start_width + - sig->h_end_width; - v_total = sig->height + sig->v_sync_width + sig->v_start_width + - sig->v_end_width; - - dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", - clk_get_rate(di->clk_ipu), - clk_get_rate(di->clk_di), - sig->pixelclock); - - mutex_lock(&di_mutex); - - ipu_di_config_clock(di, sig); - - div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; - div = div / 16; /* Now divider is integer portion */ - - /* Setup pixel clock timing */ - /* Down time is half of period */ - ipu_di_write(di, (div << 16), DI_BS_CLKGEN1); - - ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1); - ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); - - di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT; - di_gen |= DI_GEN_DI_VSYNC_EXT; - - if (sig->interlaced) { - ipu_di_sync_config_interlaced(di, sig); - - /* set y_sel = 1 */ - di_gen |= 0x10000000; - di_gen |= DI_GEN_POLARITY_5; - di_gen |= DI_GEN_POLARITY_8; - - vsync_cnt = 7; - - if (sig->Hsync_pol) - di_gen |= DI_GEN_POLARITY_3; - if (sig->Vsync_pol) - di_gen |= DI_GEN_POLARITY_2; - } else { - ipu_di_sync_config_noninterlaced(di, sig, div); - - vsync_cnt = 3; - if (di->id == 1) - /* - * TODO: change only for TVEv2, parallel display - * uses pin 2 / 3 - */ - if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3)) - vsync_cnt = 6; - - if (sig->Hsync_pol) { - if (sig->hsync_pin == 2) - di_gen |= DI_GEN_POLARITY_2; - else if (sig->hsync_pin == 4) - di_gen |= DI_GEN_POLARITY_4; - else if (sig->hsync_pin == 7) - di_gen |= DI_GEN_POLARITY_7; - } - if (sig->Vsync_pol) { - if (sig->vsync_pin == 3) - di_gen |= DI_GEN_POLARITY_3; - else if (sig->vsync_pin == 6) - di_gen |= DI_GEN_POLARITY_6; - else if (sig->vsync_pin == 8) - di_gen |= DI_GEN_POLARITY_8; - } - } - - if (sig->clk_pol) - di_gen |= DI_GEN_POLARITY_DISP_CLK; - - ipu_di_write(di, di_gen, DI_GENERAL); - - ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002, - DI_SYNC_AS_GEN); - - reg = ipu_di_read(di, DI_POL); - reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); - - if (sig->enable_pol) - reg |= DI_POL_DRDY_POLARITY_15; - if (sig->data_pol) - reg |= DI_POL_DRDY_DATA_POLARITY; - - ipu_di_write(di, reg, DI_POL); - - mutex_unlock(&di_mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel); - -int ipu_di_enable(struct ipu_di *di) -{ - int ret; - - WARN_ON(IS_ERR(di->clk_di_pixel)); - - ret = clk_prepare_enable(di->clk_di_pixel); - if (ret) - return ret; - - ipu_module_enable(di->ipu, di->module); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_di_enable); - -int ipu_di_disable(struct ipu_di *di) -{ - WARN_ON(IS_ERR(di->clk_di_pixel)); - - ipu_module_disable(di->ipu, di->module); - - clk_disable_unprepare(di->clk_di_pixel); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_di_disable); - -int ipu_di_get_num(struct ipu_di *di) -{ - return di->id; -} -EXPORT_SYMBOL_GPL(ipu_di_get_num); - -static DEFINE_MUTEX(ipu_di_lock); - -struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp) -{ - struct ipu_di *di; - - if (disp > 1) - return ERR_PTR(-EINVAL); - - di = ipu->di_priv[disp]; - - mutex_lock(&ipu_di_lock); - - if (di->inuse) { - di = ERR_PTR(-EBUSY); - goto out; - } - - di->inuse = true; -out: - mutex_unlock(&ipu_di_lock); - - return di; -} -EXPORT_SYMBOL_GPL(ipu_di_get); - -void ipu_di_put(struct ipu_di *di) -{ - mutex_lock(&ipu_di_lock); - - di->inuse = false; - - mutex_unlock(&ipu_di_lock); -} -EXPORT_SYMBOL_GPL(ipu_di_put); - -int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, - unsigned long base, - u32 module, struct clk *clk_ipu) -{ - struct ipu_di *di; - - if (id > 1) - return -ENODEV; - - di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); - if (!di) - return -ENOMEM; - - ipu->di_priv[id] = di; - - di->clk_di = devm_clk_get(dev, id ? "di1" : "di0"); - if (IS_ERR(di->clk_di)) - return PTR_ERR(di->clk_di); - - di->module = module; - di->id = id; - di->clk_ipu = clk_ipu; - di->base = devm_ioremap(dev, base, PAGE_SIZE); - if (!di->base) - return -ENOMEM; - - ipu_di_write(di, 0x10, DI_BS_CLKGEN0); - - dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n", - id, base, di->base); - di->inuse = false; - di->ipu = ipu; - - return 0; -} - -void ipu_di_exit(struct ipu_soc *ipu, int id) -{ -} diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c deleted file mode 100644 index 59f182b..0000000 --- a/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Copyright (c) 2010 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> - * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. - * - * 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 <linux/export.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/io.h> - -#include "imx-ipu-v3.h" -#include "ipu-prv.h" - -#define DMFC_RD_CHAN 0x0000 -#define DMFC_WR_CHAN 0x0004 -#define DMFC_WR_CHAN_DEF 0x0008 -#define DMFC_DP_CHAN 0x000c -#define DMFC_DP_CHAN_DEF 0x0010 -#define DMFC_GENERAL1 0x0014 -#define DMFC_GENERAL2 0x0018 -#define DMFC_IC_CTRL 0x001c -#define DMFC_WR_CHAN_ALT 0x0020 -#define DMFC_WR_CHAN_DEF_ALT 0x0024 -#define DMFC_DP_CHAN_ALT 0x0028 -#define DMFC_DP_CHAN_DEF_ALT 0x002c -#define DMFC_GENERAL1_ALT 0x0030 -#define DMFC_STAT 0x0034 - -#define DMFC_WR_CHAN_1_28 0 -#define DMFC_WR_CHAN_2_41 8 -#define DMFC_WR_CHAN_1C_42 16 -#define DMFC_WR_CHAN_2C_43 24 - -#define DMFC_DP_CHAN_5B_23 0 -#define DMFC_DP_CHAN_5F_27 8 -#define DMFC_DP_CHAN_6B_24 16 -#define DMFC_DP_CHAN_6F_29 24 - -#define DMFC_FIFO_SIZE_64 (3 << 3) -#define DMFC_FIFO_SIZE_128 (2 << 3) -#define DMFC_FIFO_SIZE_256 (1 << 3) -#define DMFC_FIFO_SIZE_512 (0 << 3) - -#define DMFC_SEGMENT(x) ((x & 0x7) << 0) -#define DMFC_BURSTSIZE_128 (0 << 6) -#define DMFC_BURSTSIZE_64 (1 << 6) -#define DMFC_BURSTSIZE_32 (2 << 6) -#define DMFC_BURSTSIZE_16 (3 << 6) - -struct dmfc_channel_data { - int ipu_channel; - unsigned long channel_reg; - unsigned long shift; - unsigned eot_shift; - unsigned max_fifo_lines; -}; - -static const struct dmfc_channel_data dmfcdata[] = { - { - .ipu_channel = IPUV3_CHANNEL_MEM_BG_SYNC, - .channel_reg = DMFC_DP_CHAN, - .shift = DMFC_DP_CHAN_5B_23, - .eot_shift = 20, - .max_fifo_lines = 3, - }, { - .ipu_channel = 24, - .channel_reg = DMFC_DP_CHAN, - .shift = DMFC_DP_CHAN_6B_24, - .eot_shift = 22, - .max_fifo_lines = 1, - }, { - .ipu_channel = IPUV3_CHANNEL_MEM_FG_SYNC, - .channel_reg = DMFC_DP_CHAN, - .shift = DMFC_DP_CHAN_5F_27, - .eot_shift = 21, - .max_fifo_lines = 2, - }, { - .ipu_channel = IPUV3_CHANNEL_MEM_DC_SYNC, - .channel_reg = DMFC_WR_CHAN, - .shift = DMFC_WR_CHAN_1_28, - .eot_shift = 16, - .max_fifo_lines = 2, - }, { - .ipu_channel = 29, - .channel_reg = DMFC_DP_CHAN, - .shift = DMFC_DP_CHAN_6F_29, - .eot_shift = 23, - .max_fifo_lines = 1, - }, -}; - -#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata) - -struct ipu_dmfc_priv; - -struct dmfc_channel { - unsigned slots; - unsigned slotmask; - unsigned segment; - int burstsize; - struct ipu_soc *ipu; - struct ipu_dmfc_priv *priv; - const struct dmfc_channel_data *data; -}; - -struct ipu_dmfc_priv { - struct ipu_soc *ipu; - struct device *dev; - struct dmfc_channel channels[DMFC_NUM_CHANNELS]; - struct mutex mutex; - unsigned long bandwidth_per_slot; - void __iomem *base; - int use_count; -}; - -int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc) -{ - struct ipu_dmfc_priv *priv = dmfc->priv; - mutex_lock(&priv->mutex); - - if (!priv->use_count) - ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN); - - priv->use_count++; - - mutex_unlock(&priv->mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); - -static void ipu_dmfc_wait_fifos(struct ipu_dmfc_priv *priv) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - - while ((readl(priv->base + DMFC_STAT) & 0x02fff000) != 0x02fff000) { - if (time_after(jiffies, timeout)) { - dev_warn(priv->dev, - "Timeout waiting for DMFC FIFOs to clear\n"); - break; - } - cpu_relax(); - } -} - -void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) -{ - struct ipu_dmfc_priv *priv = dmfc->priv; - - mutex_lock(&priv->mutex); - - priv->use_count--; - - if (!priv->use_count) { - ipu_dmfc_wait_fifos(priv); - ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN); - } - - if (priv->use_count < 0) - priv->use_count = 0; - - mutex_unlock(&priv->mutex); -} -EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel); - -static int ipu_dmfc_setup_channel(struct dmfc_channel *dmfc, int slots, - int segment, int burstsize) -{ - struct ipu_dmfc_priv *priv = dmfc->priv; - u32 val, field; - - dev_dbg(priv->dev, - "dmfc: using %d slots starting from segment %d for IPU channel %d\n", - slots, segment, dmfc->data->ipu_channel); - - switch (slots) { - case 1: - field = DMFC_FIFO_SIZE_64; - break; - case 2: - field = DMFC_FIFO_SIZE_128; - break; - case 4: - field = DMFC_FIFO_SIZE_256; - break; - case 8: - field = DMFC_FIFO_SIZE_512; - break; - default: - return -EINVAL; - } - - switch (burstsize) { - case 16: - field |= DMFC_BURSTSIZE_16; - break; - case 32: - field |= DMFC_BURSTSIZE_32; - break; - case 64: - field |= DMFC_BURSTSIZE_64; - break; - case 128: - field |= DMFC_BURSTSIZE_128; - break; - } - - field |= DMFC_SEGMENT(segment); - - val = readl(priv->base + dmfc->data->channel_reg); - - val &= ~(0xff << dmfc->data->shift); - val |= field << dmfc->data->shift; - - writel(val, priv->base + dmfc->data->channel_reg); - - dmfc->slots = slots; - dmfc->segment = segment; - dmfc->burstsize = burstsize; - dmfc->slotmask = ((1 << slots) - 1) << segment; - - return 0; -} - -static int dmfc_bandwidth_to_slots(struct ipu_dmfc_priv *priv, - unsigned long bandwidth) -{ - int slots = 1; - - while (slots * priv->bandwidth_per_slot < bandwidth) - slots *= 2; - - return slots; -} - -static int dmfc_find_slots(struct ipu_dmfc_priv *priv, int slots) -{ - unsigned slotmask_need, slotmask_used = 0; - int i, segment = 0; - - slotmask_need = (1 << slots) - 1; - - for (i = 0; i < DMFC_NUM_CHANNELS; i++) - slotmask_used |= priv->channels[i].slotmask; - - while (slotmask_need <= 0xff) { - if (!(slotmask_used & slotmask_need)) - return segment; - - slotmask_need <<= 1; - segment++; - } - - return -EBUSY; -} - -void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc) -{ - struct ipu_dmfc_priv *priv = dmfc->priv; - int i; - - dev_dbg(priv->dev, "dmfc: freeing %d slots starting from segment %d\n", - dmfc->slots, dmfc->segment); - - mutex_lock(&priv->mutex); - - if (!dmfc->slots) - goto out; - - dmfc->slotmask = 0; - dmfc->slots = 0; - dmfc->segment = 0; - - for (i = 0; i < DMFC_NUM_CHANNELS; i++) - priv->channels[i].slotmask = 0; - - for (i = 0; i < DMFC_NUM_CHANNELS; i++) { - if (priv->channels[i].slots > 0) { - priv->channels[i].segment = - dmfc_find_slots(priv, priv->channels[i].slots); - priv->channels[i].slotmask = - ((1 << priv->channels[i].slots) - 1) << - priv->channels[i].segment; - } - } - - for (i = 0; i < DMFC_NUM_CHANNELS; i++) { - if (priv->channels[i].slots > 0) - ipu_dmfc_setup_channel(&priv->channels[i], - priv->channels[i].slots, - priv->channels[i].segment, - priv->channels[i].burstsize); - } -out: - mutex_unlock(&priv->mutex); -} -EXPORT_SYMBOL_GPL(ipu_dmfc_free_bandwidth); - -int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc, - unsigned long bandwidth_pixel_per_second, int burstsize) -{ - struct ipu_dmfc_priv *priv = dmfc->priv; - int slots = dmfc_bandwidth_to_slots(priv, bandwidth_pixel_per_second); - int segment = -1, ret = 0; - - dev_dbg(priv->dev, "dmfc: trying to allocate %ldMpixel/s for IPU channel %d\n", - bandwidth_pixel_per_second / 1000000, - dmfc->data->ipu_channel); - - ipu_dmfc_free_bandwidth(dmfc); - - mutex_lock(&priv->mutex); - - if (slots > 8) { - ret = -EBUSY; - goto out; - } - - /* 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 - segment = dmfc_find_slots(priv, slots); - if (segment < 0) { - ret = -EBUSY; - goto out; - } - - ipu_dmfc_setup_channel(dmfc, slots, segment, burstsize); - -out: - mutex_unlock(&priv->mutex); - - return ret; -} -EXPORT_SYMBOL_GPL(ipu_dmfc_alloc_bandwidth); - -int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width) -{ - struct ipu_dmfc_priv *priv = dmfc->priv; - u32 dmfc_gen1; - - dmfc_gen1 = readl(priv->base + DMFC_GENERAL1); - - if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines) - dmfc_gen1 |= 1 << dmfc->data->eot_shift; - else - dmfc_gen1 &= ~(1 << dmfc->data->eot_shift); - - writel(dmfc_gen1, priv->base + DMFC_GENERAL1); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_dmfc_init_channel); - -struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel) -{ - struct ipu_dmfc_priv *priv = ipu->dmfc_priv; - int i; - - for (i = 0; i < DMFC_NUM_CHANNELS; i++) - if (dmfcdata[i].ipu_channel == ipu_channel) - return &priv->channels[i]; - return ERR_PTR(-ENODEV); -} -EXPORT_SYMBOL_GPL(ipu_dmfc_get); - -void ipu_dmfc_put(struct dmfc_channel *dmfc) -{ - ipu_dmfc_free_bandwidth(dmfc); -} -EXPORT_SYMBOL_GPL(ipu_dmfc_put); - -int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, - struct clk *ipu_clk) -{ - struct ipu_dmfc_priv *priv; - int i; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->base = devm_ioremap(dev, base, PAGE_SIZE); - if (!priv->base) - return -ENOMEM; - - priv->dev = dev; - priv->ipu = ipu; - mutex_init(&priv->mutex); - - ipu->dmfc_priv = priv; - - for (i = 0; i < DMFC_NUM_CHANNELS; i++) { - priv->channels[i].priv = priv; - priv->channels[i].ipu = ipu; - priv->channels[i].data = &dmfcdata[i]; - } - - writel(0x0, priv->base + DMFC_WR_CHAN); - writel(0x0, priv->base + DMFC_DP_CHAN); - - /* - * We have a total bandwidth of clkrate * 4pixel divided - * into 8 slots. - */ - priv->bandwidth_per_slot = clk_get_rate(ipu_clk) * 4 / 8; - - dev_dbg(dev, "dmfc: 8 slots with %ldMpixel/s bandwidth each\n", - priv->bandwidth_per_slot / 1000000); - - writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF); - writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF); - writel(0x00000003, priv->base + DMFC_GENERAL1); - - return 0; -} - -void ipu_dmfc_exit(struct ipu_soc *ipu) -{ -} diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dp.c b/drivers/staging/imx-drm/ipu-v3/ipu-dp.c deleted file mode 100644 index d90f82a..0000000 --- a/drivers/staging/imx-drm/ipu-v3/ipu-dp.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (c) 2010 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> - * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. - * - * 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 <linux/export.h> -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/io.h> -#include <linux/err.h> - -#include "imx-ipu-v3.h" -#include "ipu-prv.h" - -#define DP_SYNC 0 -#define DP_ASYNC0 0x60 -#define DP_ASYNC1 0xBC - -#define DP_COM_CONF 0x0 -#define DP_GRAPH_WIND_CTRL 0x0004 -#define DP_FG_POS 0x0008 -#define DP_CSC_A_0 0x0044 -#define DP_CSC_A_1 0x0048 -#define DP_CSC_A_2 0x004C -#define DP_CSC_A_3 0x0050 -#define DP_CSC_0 0x0054 -#define DP_CSC_1 0x0058 - -#define DP_COM_CONF_FG_EN (1 << 0) -#define DP_COM_CONF_GWSEL (1 << 1) -#define DP_COM_CONF_GWAM (1 << 2) -#define DP_COM_CONF_GWCKE (1 << 3) -#define DP_COM_CONF_CSC_DEF_MASK (3 << 8) -#define DP_COM_CONF_CSC_DEF_OFFSET 8 -#define DP_COM_CONF_CSC_DEF_FG (3 << 8) -#define DP_COM_CONF_CSC_DEF_BG (2 << 8) -#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8) - -#define IPUV3_NUM_FLOWS 3 - -struct ipu_dp_priv; - -struct ipu_dp { - u32 flow; - bool in_use; - bool foreground; - enum ipu_color_space in_cs; -}; - -struct ipu_flow { - struct ipu_dp foreground; - struct ipu_dp background; - enum ipu_color_space out_cs; - void __iomem *base; - struct ipu_dp_priv *priv; -}; - -struct ipu_dp_priv { - struct ipu_soc *ipu; - struct device *dev; - void __iomem *base; - struct ipu_flow flow[IPUV3_NUM_FLOWS]; - struct mutex mutex; - int use_count; -}; - -static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1}; - -static inline struct ipu_flow *to_flow(struct ipu_dp *dp) -{ - if (dp->foreground) - return container_of(dp, struct ipu_flow, foreground); - else - return container_of(dp, struct ipu_flow, background); -} - -int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, - u8 alpha, bool bg_chan) -{ - struct ipu_flow *flow = to_flow(dp); - struct ipu_dp_priv *priv = flow->priv; - u32 reg; - - mutex_lock(&priv->mutex); - - reg = readl(flow->base + DP_COM_CONF); - if (bg_chan) - reg &= ~DP_COM_CONF_GWSEL; - else - reg |= DP_COM_CONF_GWSEL; - writel(reg, flow->base + DP_COM_CONF); - - if (enable) { - reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL; - writel(reg | ((u32) alpha << 24), - flow->base + DP_GRAPH_WIND_CTRL); - - reg = readl(flow->base + DP_COM_CONF); - writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); - } else { - reg = readl(flow->base + DP_COM_CONF); - writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); - } - - ipu_srm_dp_sync_update(priv->ipu); - - mutex_unlock(&priv->mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha); - -int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos) -{ - struct ipu_flow *flow = to_flow(dp); - struct ipu_dp_priv *priv = flow->priv; - - writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS); - - ipu_srm_dp_sync_update(priv->ipu); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos); - -static void ipu_dp_csc_init(struct ipu_flow *flow, - enum ipu_color_space in, - enum ipu_color_space out, - u32 place) -{ - u32 reg; - - reg = readl(flow->base + DP_COM_CONF); - reg &= ~DP_COM_CONF_CSC_DEF_MASK; - - if (in == out) { - writel(reg, flow->base + DP_COM_CONF); - return; - } - - if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) { - writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0); - writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1); - writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2); - writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3); - writel(0x3d6 | (0x0000 << 16) | (2 << 30), - flow->base + DP_CSC_0); - writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30), - flow->base + DP_CSC_1); - } else { - writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0); - writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1); - writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2); - writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3); - writel(0x000 | (0x3e42 << 16) | (1 << 30), - flow->base + DP_CSC_0); - writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30), - flow->base + DP_CSC_1); - } - - reg |= place; - - writel(reg, flow->base + DP_COM_CONF); -} - -int ipu_dp_setup_channel(struct ipu_dp *dp, - enum ipu_color_space in, - enum ipu_color_space out) -{ - struct ipu_flow *flow = to_flow(dp); - struct ipu_dp_priv *priv = flow->priv; - - mutex_lock(&priv->mutex); - - dp->in_cs = in; - - if (!dp->foreground) - flow->out_cs = out; - - if (flow->foreground.in_cs == flow->background.in_cs) { - /* - * foreground and background are of same colorspace, put - * colorspace converter after combining unit. - */ - ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs, - DP_COM_CONF_CSC_DEF_BOTH); - } else { - if (flow->foreground.in_cs == flow->out_cs) - /* - * foreground identical to output, apply color - * conversion on background - */ - ipu_dp_csc_init(flow, flow->background.in_cs, - flow->out_cs, DP_COM_CONF_CSC_DEF_BG); - else - ipu_dp_csc_init(flow, flow->foreground.in_cs, - flow->out_cs, DP_COM_CONF_CSC_DEF_FG); - } - - ipu_srm_dp_sync_update(priv->ipu); - - mutex_unlock(&priv->mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_dp_setup_channel); - -int ipu_dp_enable(struct ipu_soc *ipu) -{ - struct ipu_dp_priv *priv = ipu->dp_priv; - - mutex_lock(&priv->mutex); - - if (!priv->use_count) - ipu_module_enable(priv->ipu, IPU_CONF_DP_EN); - - priv->use_count++; - - mutex_unlock(&priv->mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_dp_enable); - -int ipu_dp_enable_channel(struct ipu_dp *dp) -{ - struct ipu_flow *flow = to_flow(dp); - struct ipu_dp_priv *priv = flow->priv; - u32 reg; - - if (!dp->foreground) - return 0; - - mutex_lock(&priv->mutex); - - reg = readl(flow->base + DP_COM_CONF); - reg |= DP_COM_CONF_FG_EN; - writel(reg, flow->base + DP_COM_CONF); - - ipu_srm_dp_sync_update(priv->ipu); - - mutex_unlock(&priv->mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(ipu_dp_enable_channel); - -void ipu_dp_disable_channel(struct ipu_dp *dp) -{ - struct ipu_flow *flow = to_flow(dp); - struct ipu_dp_priv *priv = flow->priv; - u32 reg, csc; - - if (!dp->foreground) - return; - - mutex_lock(&priv->mutex); - - reg = readl(flow->base + DP_COM_CONF); - csc = reg & DP_COM_CONF_CSC_DEF_MASK; - if (csc == DP_COM_CONF_CSC_DEF_FG) - reg &= ~DP_COM_CONF_CSC_DEF_MASK; - - reg &= ~DP_COM_CONF_FG_EN; - writel(reg, flow->base + DP_COM_CONF); - - writel(0, flow->base + DP_FG_POS); - ipu_srm_dp_sync_update(priv->ipu); - - if (ipu_idmac_channel_busy(priv->ipu, IPUV3_CHANNEL_MEM_BG_SYNC)) - ipu_wait_interrupt(priv->ipu, IPU_IRQ_DP_SF_END, 50); - - mutex_unlock(&priv->mutex); -} -EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); - -void ipu_dp_disable(struct ipu_soc *ipu) -{ - struct ipu_dp_priv *priv = ipu->dp_priv; - - mutex_lock(&priv->mutex); - - priv->use_count--; - - if (!priv->use_count) - ipu_module_disable(priv->ipu, IPU_CONF_DP_EN); - - if (priv->use_count < 0) - priv->use_count = 0; - - mutex_unlock(&priv->mutex); -} -EXPORT_SYMBOL_GPL(ipu_dp_disable); - -struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow) -{ - struct ipu_dp_priv *priv = ipu->dp_priv; - struct ipu_dp *dp; - - if ((flow >> 1) >= IPUV3_NUM_FLOWS) - return ERR_PTR(-EINVAL); - - if (flow & 1) - dp = &priv->flow[flow >> 1].foreground; - else - dp = &priv->flow[flow >> 1].background; - - if (dp->in_use) - return ERR_PTR(-EBUSY); - - dp->in_use = true; - - return dp; -} -EXPORT_SYMBOL_GPL(ipu_dp_get); - -void ipu_dp_put(struct ipu_dp *dp) -{ - dp->in_use = false; -} -EXPORT_SYMBOL_GPL(ipu_dp_put); - -int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) -{ - struct ipu_dp_priv *priv; - int i; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - priv->dev = dev; - priv->ipu = ipu; - - ipu->dp_priv = priv; - - priv->base = devm_ioremap(dev, base, PAGE_SIZE); - if (!priv->base) - return -ENOMEM; - - mutex_init(&priv->mutex); - - for (i = 0; i < IPUV3_NUM_FLOWS; i++) { - priv->flow[i].foreground.foreground = true; - priv->flow[i].base = priv->base + ipu_dp_flow_base[i]; - priv->flow[i].priv = priv; - } - - return 0; -} - -void ipu_dp_exit(struct ipu_soc *ipu) -{ -} diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-prv.h b/drivers/staging/imx-drm/ipu-v3/ipu-prv.h deleted file mode 100644 index bfc1b33..0000000 --- a/drivers/staging/imx-drm/ipu-v3/ipu-prv.h +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2010 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> - * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. - * - * 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. - */ -#ifndef __IPU_PRV_H__ -#define __IPU_PRV_H__ - -struct ipu_soc; - -#include <linux/types.h> -#include <linux/device.h> -#include <linux/clk.h> -#include <linux/platform_device.h> - -#include "imx-ipu-v3.h" - -#define IPUV3_CHANNEL_CSI0 0 -#define IPUV3_CHANNEL_CSI1 1 -#define IPUV3_CHANNEL_CSI2 2 -#define IPUV3_CHANNEL_CSI3 3 -#define IPUV3_CHANNEL_MEM_BG_SYNC 23 -#define IPUV3_CHANNEL_MEM_FG_SYNC 27 -#define IPUV3_CHANNEL_MEM_DC_SYNC 28 -#define IPUV3_CHANNEL_MEM_FG_SYNC_ALPHA 31 -#define IPUV3_CHANNEL_MEM_DC_ASYNC 41 -#define IPUV3_CHANNEL_ROT_ENC_MEM 45 -#define IPUV3_CHANNEL_ROT_VF_MEM 46 -#define IPUV3_CHANNEL_ROT_PP_MEM 47 -#define IPUV3_CHANNEL_ROT_ENC_MEM_OUT 48 -#define IPUV3_CHANNEL_ROT_VF_MEM_OUT 49 -#define IPUV3_CHANNEL_ROT_PP_MEM_OUT 50 -#define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA 51 - -#define IPU_MCU_T_DEFAULT 8 -#define IPU_CM_IDMAC_REG_OFS 0x00008000 -#define IPU_CM_IC_REG_OFS 0x00020000 -#define IPU_CM_IRT_REG_OFS 0x00028000 -#define IPU_CM_CSI0_REG_OFS 0x00030000 -#define IPU_CM_CSI1_REG_OFS 0x00038000 -#define IPU_CM_SMFC_REG_OFS 0x00050000 -#define IPU_CM_DC_REG_OFS 0x00058000 -#define IPU_CM_DMFC_REG_OFS 0x00060000 - -/* Register addresses */ -/* IPU Common registers */ -#define IPU_CM_REG(offset) (offset) - -#define IPU_CONF IPU_CM_REG(0) - -#define IPU_SRM_PRI1 IPU_CM_REG(0x00a0) -#define IPU_SRM_PRI2 IPU_CM_REG(0x00a4) -#define IPU_FS_PROC_FLOW1 IPU_CM_REG(0x00a8) -#define IPU_FS_PROC_FLOW2 IPU_CM_REG(0x00ac) -#define IPU_FS_PROC_FLOW3 IPU_CM_REG(0x00b0) -#define IPU_FS_DISP_FLOW1 IPU_CM_REG(0x00b4) -#define IPU_FS_DISP_FLOW2 IPU_CM_REG(0x00b8) -#define IPU_SKIP IPU_CM_REG(0x00bc) -#define IPU_DISP_ALT_CONF IPU_CM_REG(0x00c0) -#define IPU_DISP_GEN IPU_CM_REG(0x00c4) -#define IPU_DISP_ALT1 IPU_CM_REG(0x00c8) -#define IPU_DISP_ALT2 IPU_CM_REG(0x00cc) -#define IPU_DISP_ALT3 IPU_CM_REG(0x00d0) -#define IPU_DISP_ALT4 IPU_CM_REG(0x00d4) -#define IPU_SNOOP IPU_CM_REG(0x00d8) -#define IPU_MEM_RST IPU_CM_REG(0x00dc) -#define IPU_PM IPU_CM_REG(0x00e0) -#define IPU_GPR IPU_CM_REG(0x00e4) -#define IPU_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0150 + 4 * ((ch) / 32)) -#define IPU_ALT_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0168 + 4 * ((ch) / 32)) -#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32)) -#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244) -#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248) -#define IPU_SRM_STAT IPU_CM_REG(0x024C) -#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250) -#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254) -#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32)) -#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32)) -#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32)) -#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32)) - -#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n)) -#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n)) - -#define IPU_DI0_COUNTER_RELEASE (1 << 24) -#define IPU_DI1_COUNTER_RELEASE (1 << 25) - -#define IPU_IDMAC_REG(offset) (offset) - -#define IDMAC_CONF IPU_IDMAC_REG(0x0000) -#define IDMAC_CHA_EN(ch) IPU_IDMAC_REG(0x0004 + 4 * ((ch) / 32)) -#define IDMAC_SEP_ALPHA IPU_IDMAC_REG(0x000c) -#define IDMAC_ALT_SEP_ALPHA IPU_IDMAC_REG(0x0010) -#define IDMAC_CHA_PRI(ch) IPU_IDMAC_REG(0x0014 + 4 * ((ch) / 32)) -#define IDMAC_WM_EN(ch) IPU_IDMAC_REG(0x001c + 4 * ((ch) / 32)) -#define IDMAC_CH_LOCK_EN_1 IPU_IDMAC_REG(0x0024) -#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028) -#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c) -#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030) -#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034) -#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32)) -#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32)) - -#define IPU_NUM_IRQS (32 * 15) - -enum ipu_modules { - IPU_CONF_CSI0_EN = (1 << 0), - IPU_CONF_CSI1_EN = (1 << 1), - IPU_CONF_IC_EN = (1 << 2), - IPU_CONF_ROT_EN = (1 << 3), - IPU_CONF_ISP_EN = (1 << 4), - IPU_CONF_DP_EN = (1 << 5), - IPU_CONF_DI0_EN = (1 << 6), - IPU_CONF_DI1_EN = (1 << 7), - IPU_CONF_SMFC_EN = (1 << 8), - IPU_CONF_DC_EN = (1 << 9), - IPU_CONF_DMFC_EN = (1 << 10), - - IPU_CONF_VDI_EN = (1 << 12), - - IPU_CONF_IDMAC_DIS = (1 << 22), - - IPU_CONF_IC_DMFC_SEL = (1 << 25), - IPU_CONF_IC_DMFC_SYNC = (1 << 26), - IPU_CONF_VDI_DMFC_SYNC = (1 << 27), - - IPU_CONF_CSI0_DATA_SOURCE = (1 << 28), - IPU_CONF_CSI1_DATA_SOURCE = (1 << 29), - IPU_CONF_IC_INPUT = (1 << 30), - IPU_CONF_CSI_SEL = (1 << 31), -}; - -struct ipuv3_channel { - unsigned int num; - - bool enabled; - bool busy; - - struct ipu_soc *ipu; -}; - -struct ipu_dc_priv; -struct ipu_dmfc_priv; -struct ipu_di; -struct ipu_devtype; - -struct ipu_soc { - struct device *dev; - const struct ipu_devtype *devtype; - enum ipuv3_type ipu_type; - spinlock_t lock; - struct mutex channel_lock; - - void __iomem *cm_reg; - void __iomem *idmac_reg; - struct ipu_ch_param __iomem *cpmem_base; - - int usecount; - - struct clk *clk; - - struct ipuv3_channel channel[64]; - - int irq_sync; - int irq_err; - struct irq_domain *domain; - - struct ipu_dc_priv *dc_priv; - struct ipu_dp_priv *dp_priv; - struct ipu_dmfc_priv *dmfc_priv; - struct ipu_di *di_priv[2]; -}; - -void ipu_srm_dp_sync_update(struct ipu_soc *ipu); - -int ipu_module_enable(struct ipu_soc *ipu, u32 mask); -int ipu_module_disable(struct ipu_soc *ipu, u32 mask); - -bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno); -int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms); - -int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, - unsigned long base, u32 module, struct clk *ipu_clk); -void ipu_di_exit(struct ipu_soc *ipu, int id); - -int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, - struct clk *ipu_clk); -void ipu_dmfc_exit(struct ipu_soc *ipu); - -int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); -void ipu_dp_exit(struct ipu_soc *ipu); - -int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, - unsigned long template_base); -void ipu_dc_exit(struct ipu_soc *ipu); - -int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); -void ipu_cpmem_exit(struct ipu_soc *ipu); - -#endif /* __IPU_PRV_H__ */ diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index 47bec5e..720868b 100644 --- a/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/drivers/staging/imx-drm/ipuv3-crtc.c @@ -30,7 +30,7 @@ #include <drm/drm_gem_cma_helper.h> #include <drm/drm_fb_cma_helper.h> -#include "ipu-v3/imx-ipu-v3.h" +#include <video/imx-ipu-v3.h> #include "imx-drm.h" #include "ipuv3-plane.h" diff --git a/drivers/staging/imx-drm/ipuv3-plane.c b/drivers/staging/imx-drm/ipuv3-plane.c index 5697e59..6f393a1 100644 --- a/drivers/staging/imx-drm/ipuv3-plane.c +++ b/drivers/staging/imx-drm/ipuv3-plane.c @@ -17,7 +17,7 @@ #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> -#include "ipu-v3/imx-ipu-v3.h" +#include "video/imx-ipu-v3.h" #include "ipuv3-plane.h" #define to_ipu_plane(x) container_of(x, struct ipu_plane, base) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index c7b4f0f..8bf495f 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -20,6 +20,7 @@ source "drivers/char/agp/Kconfig" source "drivers/gpu/vga/Kconfig" source "drivers/gpu/host1x/Kconfig" +source "drivers/gpu/ipu-v3/Kconfig" menu "Direct Rendering Manager" source "drivers/gpu/drm/Kconfig" diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h new file mode 100644 index 0000000..c2c6fab --- /dev/null +++ b/include/video/imx-ipu-v3.h @@ -0,0 +1,331 @@ +/* + * Copyright 2005-2009 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU Lesser General + * Public License. You may obtain a copy of the GNU Lesser General + * Public License Version 2.1 or later at the following locations: + * + * http://www.opensource.org/licenses/lgpl-license.html + * http://www.gnu.org/copyleft/lgpl.html + */ + +#ifndef __DRM_IPU_H__ +#define __DRM_IPU_H__ + +#include <linux/types.h> +#include <linux/videodev2.h> +#include <linux/bitmap.h> +#include <linux/fb.h> + +struct ipu_soc; + +enum ipuv3_type { + IPUV3EX, + IPUV3M, + IPUV3H, +}; + +#define IPU_PIX_FMT_GBR24 v4l2_fourcc('G', 'B', 'R', '3') + +/* + * Bitfield of Display Interface signal polarities. + */ +struct ipu_di_signal_cfg { + unsigned datamask_en:1; + unsigned interlaced:1; + unsigned odd_field_first:1; + unsigned clksel_en:1; + unsigned clkidle_en:1; + unsigned data_pol:1; /* true = inverted */ + unsigned clk_pol:1; /* true = rising edge */ + unsigned enable_pol:1; + unsigned Hsync_pol:1; /* true = active high */ + unsigned Vsync_pol:1; + + u16 width; + u16 height; + u32 pixel_fmt; + u16 h_start_width; + u16 h_sync_width; + u16 h_end_width; + u16 v_start_width; + u16 v_sync_width; + u16 v_end_width; + u32 v_to_h_sync; + unsigned long pixelclock; +#define IPU_DI_CLKMODE_SYNC (1 << 0) +#define IPU_DI_CLKMODE_EXT (1 << 1) + unsigned long clkflags; + + u8 hsync_pin; + u8 vsync_pin; +}; + +enum ipu_color_space { + IPUV3_COLORSPACE_RGB, + IPUV3_COLORSPACE_YUV, + IPUV3_COLORSPACE_UNKNOWN, +}; + +struct ipuv3_channel; + +enum ipu_channel_irq { + IPU_IRQ_EOF = 0, + IPU_IRQ_NFACK = 64, + IPU_IRQ_NFB4EOF = 128, + IPU_IRQ_EOS = 192, +}; + +int ipu_map_irq(struct ipu_soc *ipu, int irq); +int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, + enum ipu_channel_irq irq); + +#define IPU_IRQ_DP_SF_START (448 + 2) +#define IPU_IRQ_DP_SF_END (448 + 3) +#define IPU_IRQ_BG_SF_END IPU_IRQ_DP_SF_END, +#define IPU_IRQ_DC_FC_0 (448 + 8) +#define IPU_IRQ_DC_FC_1 (448 + 9) +#define IPU_IRQ_DC_FC_2 (448 + 10) +#define IPU_IRQ_DC_FC_3 (448 + 11) +#define IPU_IRQ_DC_FC_4 (448 + 12) +#define IPU_IRQ_DC_FC_6 (448 + 13) +#define IPU_IRQ_VSYNC_PRE_0 (448 + 14) +#define IPU_IRQ_VSYNC_PRE_1 (448 + 15) + +/* + * IPU Image DMA Controller (idmac) functions + */ +struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned channel); +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); +void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num); + +/* + * IPU Display Controller (dc) functions + */ +struct ipu_dc; +struct ipu_di; +struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel); +void ipu_dc_put(struct ipu_dc *dc); +int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, + u32 pixel_fmt, u32 width); +void ipu_dc_enable(struct ipu_soc *ipu); +void ipu_dc_enable_channel(struct ipu_dc *dc); +void ipu_dc_disable_channel(struct ipu_dc *dc); +void ipu_dc_disable(struct ipu_soc *ipu); + +/* + * IPU Display Interface (di) functions + */ +struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp); +void ipu_di_put(struct ipu_di *); +int ipu_di_disable(struct ipu_di *); +int ipu_di_enable(struct ipu_di *); +int ipu_di_get_num(struct ipu_di *); +int ipu_di_init_sync_panel(struct ipu_di *, struct ipu_di_signal_cfg *sig); + +/* + * IPU Display Multi FIFO Controller (dmfc) functions + */ +struct dmfc_channel; +int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc); +void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc); +int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc, + unsigned long bandwidth_mbs, int burstsize); +void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc); +int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width); +struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipuv3_channel); +void ipu_dmfc_put(struct dmfc_channel *dmfc); + +/* + * IPU Display Processor (dp) functions + */ +#define IPU_DP_FLOW_SYNC_BG 0 +#define IPU_DP_FLOW_SYNC_FG 1 +#define IPU_DP_FLOW_ASYNC0_BG 2 +#define IPU_DP_FLOW_ASYNC0_FG 3 +#define IPU_DP_FLOW_ASYNC1_BG 4 +#define IPU_DP_FLOW_ASYNC1_FG 5 + +struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow); +void ipu_dp_put(struct ipu_dp *); +int ipu_dp_enable(struct ipu_soc *ipu); +int ipu_dp_enable_channel(struct ipu_dp *dp); +void ipu_dp_disable_channel(struct ipu_dp *dp); +void ipu_dp_disable(struct ipu_soc *ipu); +int ipu_dp_setup_channel(struct ipu_dp *dp, + enum ipu_color_space in, enum ipu_color_space out); +int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos); +int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha, + bool bg_chan); + +#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size)) + +#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22) +#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22) +#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4) +#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1) +#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1) +#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14) +#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14) + +#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10) +#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9) +#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13) +#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12) +#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1) +#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1) +#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12) +#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11) +#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10) +#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7) +#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10) +#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1) +#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1) +#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7) +#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1) +#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1) +#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3) +#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2) +#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1) +#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3) +#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2) +#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1) +#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1) +#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1) +#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1) +#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1) +#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1) +#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13) +#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12) +#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29) +#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29) +#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20) +#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7) +#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4) +#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1) +#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3) +#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2) +#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7) +#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14) +#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3) +#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3) +#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3) +#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3) +#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5) +#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5) +#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5) +#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5) +#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1) +#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1) +#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1) + +struct ipu_cpmem_word { + u32 data[5]; + u32 res[3]; +}; + +struct ipu_ch_param { + struct ipu_cpmem_word word[2]; +}; + +void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v); +u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs); +struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel); +void ipu_ch_param_dump(struct ipu_ch_param __iomem *p); + +static inline void ipu_ch_param_zero(struct ipu_ch_param __iomem *p) +{ + int i; + void __iomem *base = p; + + for (i = 0; i < sizeof(*p) / sizeof(u32); i++) + writel(0, base + i * sizeof(u32)); +} + +static inline void ipu_cpmem_set_buffer(struct ipu_ch_param __iomem *p, + int bufnum, dma_addr_t buf) +{ + if (bufnum) + ipu_ch_param_write_field(p, IPU_FIELD_EBA1, buf >> 3); + else + ipu_ch_param_write_field(p, IPU_FIELD_EBA0, buf >> 3); +} + +static inline void ipu_cpmem_set_resolution(struct ipu_ch_param __iomem *p, + int xres, int yres) +{ + ipu_ch_param_write_field(p, IPU_FIELD_FW, xres - 1); + ipu_ch_param_write_field(p, IPU_FIELD_FH, yres - 1); +} + +static inline void ipu_cpmem_set_stride(struct ipu_ch_param __iomem *p, + int stride) +{ + ipu_ch_param_write_field(p, IPU_FIELD_SLY, stride - 1); +} + +void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel); + +struct ipu_rgb { + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; + int bits_per_pixel; +}; + +struct ipu_image { + struct v4l2_pix_format pix; + struct v4l2_rect rect; + dma_addr_t phys; +}; + +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 *, + const struct ipu_rgb *rgb); + +static inline void ipu_cpmem_interlaced_scan(struct ipu_ch_param *p, + int stride) +{ + ipu_ch_param_write_field(p, IPU_FIELD_SO, 1); + ipu_ch_param_write_field(p, IPU_FIELD_ILO, stride / 8); + ipu_ch_param_write_field(p, IPU_FIELD_SLY, (stride * 2) - 1); +}; + +void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format, + int stride, int height); +void ipu_cpmem_set_yuv_interleaved(struct ipu_ch_param __iomem *p, + u32 pixel_format); +void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p, + u32 pixel_format, int stride, int u_offset, int v_offset); +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, + int burstsize) +{ + ipu_ch_param_write_field(p, IPU_FIELD_NPB, burstsize - 1); +}; + +struct ipu_client_platformdata { + int di; + int dc; + int dp; + int dmfc; + int dma[2]; +}; + +#endif /* __DRM_IPU_H__ */ -- 2.0.0.rc2 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel