Commit fdcaa1db authored by Dave Airlie's avatar Dave Airlie

Merge tag 'ipu-3.18' of git://git.pengutronix.de/git/pza/linux into drm-next

IPUv3 preparations for capture support

* tag 'ipu-3.18' of git://git.pengutronix.de/git/pza/linux: (26 commits)
  gpu: ipu-v3: Add ipu_dump()
  gpu: ipu-cpmem: Add ipu_cpmem_dump()
  gpu: ipu-v3: Add more planar formats support
  gpu: ipu-cpmem: Add second buffer support to ipu_cpmem_set_image()
  gpu: ipu-cpmem: Add ipu_cpmem_set_rotation()
  gpu: ipu-cpmem: Add ipu_cpmem_set_axi_id()
  gpu: ipu-cpmem: Add ipu_cpmem_set_block_mode()
  gpu: ipu-v3: Add ipu_idmac_lock_enable()
  gpu: ipu-v3: Add ipu_idmac_enable_watermark()
  gpu: ipu-v3: Add ipu_stride_to_bytes()
  gpu: ipu-v3: Add __ipu_idmac_reset_current_buffer()
  gpu: ipu-v3: Add ipu_idmac_clear_buffer()
  gpu: ipu-v3: Add ipu_idmac_buffer_is_ready()
  gpu: ipu-v3: Move IDMAC channel names to imx-ipu-v3.h
  gpu: ipu-v3: Add helper function checking if pixfmt is planar
  gpu: ipu-v3: Add rotation mode conversion utilities
  gpu: ipu-v3: Add ipu_mbus_code_to_colorspace()
  gpu: ipu-v3: smfc: Add ipu_smfc_set_watermark()
  gpu: ipu-v3: smfc: Convert to per-channel
  gpu: ipu-v3: smfc: Move enable/disable to ipu-smfc.c
  ...
parents bb6d822e 3feb049f
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 ipu-smfc.o
imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \
ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o
......@@ -44,17 +44,6 @@ 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;
......@@ -65,457 +54,184 @@ void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
}
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)
enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
{
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;
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:
case DRM_FORMAT_YUV422:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV61:
return IPUV3_COLORSPACE_YUV;
default:
return -EINVAL;
return IPUV3_COLORSPACE_UNKNOWN;
}
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);
EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace);
void ipu_cpmem_set_yuv_interleaved(struct ipu_ch_param __iomem *p,
u32 pixel_format)
enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
{
switch (pixel_format) {
switch (pixelformat) {
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_YUV422P:
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;
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
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_cpmem_set_yuv_interleaved);
EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p,
u32 pixel_format, int stride, int u_offset, int v_offset)
bool ipu_pixelformat_is_planar(u32 pixelformat)
{
switch (pixel_format) {
switch (pixelformat) {
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;
case V4L2_PIX_FMT_YUV422P:
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
return true;
}
}
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;
}
return false;
}
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,
};
EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar);
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)
enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code)
{
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;
switch (mbus_code & 0xf000) {
case 0x1000:
return IPUV3_COLORSPACE_RGB;
case 0x2000:
return IPUV3_COLORSPACE_YUV;
default:
return -EINVAL;
return IPUV3_COLORSPACE_UNKNOWN;
}
return 0;
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt);
EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace);
/*
* 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)
int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat)
{
switch (pixelformat) {
case V4L2_PIX_FMT_RGB565:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_YUV422P:
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
/*
* 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
* for the planar YUV formats, the stride passed to
* cpmem must be the stride in bytes of the Y plane.
* And all the planar YUV formats have an 8-bit
* Y component.
*/
return DRM_FORMAT_RGB565;
return (8 * pixel_stride) >> 3;
case V4L2_PIX_FMT_RGB565:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_UYVY:
return (16 * pixel_stride) >> 3;
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;
return (24 * pixel_stride) >> 3;
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 (32 * pixel_stride) >> 3;
default:
break;
}
return -EINVAL;
}
EXPORT_SYMBOL_GPL(ipu_stride_to_bytes);
enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
bool hflip, bool vflip)
{
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;
u32 r90, vf, hf;
switch (degrees) {
case 0:
vf = hf = r90 = 0;
break;
case 90:
vf = hf = 0;
r90 = 1;
break;
case 180:
vf = hf = 1;
r90 = 0;
break;
case 270:
vf = hf = r90 = 1;
break;
default:
return IPUV3_COLORSPACE_UNKNOWN;
return -EINVAL;
}
}
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;
hf ^= (u32)hflip;
vf ^= (u32)vflip;
pr_debug("%s: resolution: %dx%d stride: %d\n",
__func__, pix->width, pix->height,
pix->bytesperline);
*mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode);
ipu_cpmem_set_resolution(cpmem, image->rect.width,
image->rect.height);
ipu_cpmem_set_stride(cpmem, pix->bytesperline);
int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
bool hflip, bool vflip)
{
u32 r90, vf, hf;
ipu_cpmem_set_fmt(cpmem, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat));
r90 = ((u32)mode >> 2) & 0x1;
hf = ((u32)mode >> 1) & 0x1;
vf = ((u32)mode >> 0) & 0x1;
hf ^= (u32)hflip;
vf ^= (u32)vflip;
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);
switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) {
case IPU_ROTATE_NONE:
*degrees = 0;
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);
case IPU_ROTATE_90_RIGHT:
*degrees = 90;
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);
case IPU_ROTATE_180:
*degrees = 180;
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);
case IPU_ROTATE_90_LEFT:
*degrees = 270;
break;
default:
return -EINVAL;
......@@ -523,27 +239,7 @@ int ipu_cpmem_set_image(struct ipu_ch_param __iomem *cpmem,
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);
EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees);
struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
{
......@@ -587,7 +283,26 @@ void ipu_idmac_put(struct ipuv3_channel *channel)
}
EXPORT_SYMBOL_GPL(ipu_idmac_put);
#define idma_mask(ch) (1 << (ch & 0x1f))
#define idma_mask(ch) (1 << ((ch) & 0x1f))
/*
* This is an undocumented feature, a write one to a channel bit in
* IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's
* internal current buffer pointer so that transfers start from buffer
* 0 on the next channel enable (that's the theory anyway, the imx6 TRM
* only says these are read-only registers). This operation is required
* for channel linking to work correctly, for instance video capture
* pipelines that carry out image rotations will fail after the first
* streaming unless this function is called for each channel before
* re-enabling the channels.
*/
static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel)
{
struct ipu_soc *ipu = channel->ipu;
unsigned int chno = channel->num;
ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno));
}
void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
bool doublebuffer)
......@@ -605,10 +320,81 @@ void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
reg &= ~idma_mask(channel->num);
ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
__ipu_idmac_reset_current_buffer(channel);
spin_unlock_irqrestore(&ipu->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
static const struct {
int chnum;
u32 reg;
int shift;
} idmac_lock_en_info[] = {
{ .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, },
{ .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, },
{ .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, },
{ .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, },
{ .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, },
{ .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, },
{ .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, },
{ .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, },
{ .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, },
{ .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, },
{ .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, },
{ .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, },
{ .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, },
{ .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, },
{ .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, },
{ .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, },
{ .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, },
};
int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts)
{
struct ipu_soc *ipu = channel->ipu;
unsigned long flags;
u32 bursts, regval;
int i;
switch (num_bursts) {
case 0:
case 1:
bursts = 0x00; /* locking disabled */
break;
case 2:
bursts = 0x01;
break;
case 4:
bursts = 0x02;
break;
case 8:
bursts = 0x03;
break;
default:
return -EINVAL;
}
for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) {
if (channel->num == idmac_lock_en_info[i].chnum)
break;
}
if (i >= ARRAY_SIZE(idmac_lock_en_info))
return -EINVAL;
spin_lock_irqsave(&ipu->lock, flags);
regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg);
regval &= ~(0x03 << idmac_lock_en_info[i].shift);
regval |= (bursts << idmac_lock_en_info[i].shift);
ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg);
spin_unlock_irqrestore(&ipu->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable);
int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
{
unsigned long lock_flags;
......@@ -661,30 +447,6 @@ int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
}
EXPORT_SYMBOL_GPL(ipu_module_disable);
int ipu_csi_enable(struct ipu_soc *ipu, int csi)
{
return ipu_module_enable(ipu, csi ? IPU_CONF_CSI1_EN : IPU_CONF_CSI0_EN);
}
EXPORT_SYMBOL_GPL(ipu_csi_enable);
int ipu_csi_disable(struct ipu_soc *ipu, int csi)
{
return ipu_module_disable(ipu, csi ? IPU_CONF_CSI1_EN : IPU_CONF_CSI0_EN);
}
EXPORT_SYMBOL_GPL(ipu_csi_disable);
int ipu_smfc_enable(struct ipu_soc *ipu)
{
return ipu_module_enable(ipu, IPU_CONF_SMFC_EN);
}
EXPORT_SYMBOL_GPL(ipu_smfc_enable);
int ipu_smfc_disable(struct ipu_soc *ipu)
{
return ipu_module_disable(ipu, IPU_CONF_SMFC_EN);
}
EXPORT_SYMBOL_GPL(ipu_smfc_disable);
int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
{
struct ipu_soc *ipu = channel->ipu;
......@@ -694,6 +456,30 @@ int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
}
EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer);
bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num)
{
struct ipu_soc *ipu = channel->ipu;
unsigned long flags;
u32 reg = 0;
spin_lock_irqsave(&ipu->lock, flags);
switch (buf_num) {
case 0:
reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num));
break;
case 1:
reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num));
break;
case 2:
reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num));
break;
}
spin_unlock_irqrestore(&ipu->lock, flags);
return ((reg & idma_mask(channel->num)) != 0);
}
EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready);
void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
{
struct ipu_soc *ipu = channel->ipu;
......@@ -712,6 +498,34 @@ void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
}
EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
void ipu_idmac_clear_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);
ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */
switch (buf_num) {
case 0:
ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
break;
case 1:
ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
break;
case 2:
ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno));
break;
default:
break;
}
ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
spin_unlock_irqrestore(&ipu->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer);
int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
{
struct ipu_soc *ipu = channel->ipu;
......@@ -782,6 +596,8 @@ int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
val &= ~idma_mask(channel->num);
ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
__ipu_idmac_reset_current_buffer(channel);
/* Set channel buffers NOT to be ready */
ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
......@@ -810,6 +626,31 @@ int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
}
EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
/*
* The imx6 rev. D TRM says that enabling the WM feature will increase
* a channel's priority. Refer to Table 36-8 Calculated priority value.
* The sub-module that is the sink or source for the channel must enable
* watermark signal for this to take effect (SMFC_WM for instance).
*/
void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable)
{
struct ipu_soc *ipu = channel->ipu;
unsigned long flags;
u32 val;
spin_lock_irqsave(&ipu->lock, flags);
val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num));
if (enable)
val |= 1 << (channel->num % 32);
else
val &= ~(1 << (channel->num % 32));
ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num));
spin_unlock_irqrestore(&ipu->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark);
static int ipu_memory_reset(struct ipu_soc *ipu)
{
unsigned long timeout;
......@@ -826,12 +667,66 @@ static int ipu_memory_reset(struct ipu_soc *ipu)
return 0;
}
/*
* Set the source mux for the given CSI. Selects either parallel or
* MIPI CSI2 sources.
*/
void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2)
{
unsigned long flags;
u32 val, mask;
mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE :
IPU_CONF_CSI0_DATA_SOURCE;
spin_lock_irqsave(&ipu->lock, flags);
val = ipu_cm_read(ipu, IPU_CONF);
if (mipi_csi2)
val |= mask;
else
val &= ~mask;
ipu_cm_write(ipu, val, IPU_CONF);
spin_unlock_irqrestore(&ipu->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux);
/*
* Set the source mux for the IC. Selects either CSI[01] or the VDI.
*/
void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
{
unsigned long flags;
u32 val;
spin_lock_irqsave(&ipu->lock, flags);
val = ipu_cm_read(ipu, IPU_CONF);
if (vdi) {
val |= IPU_CONF_IC_INPUT;
} else {
val &= ~IPU_CONF_IC_INPUT;
if (csi_id == 1)
val |= IPU_CONF_CSI_SEL;
else
val &= ~IPU_CONF_CSI_SEL;
}
ipu_cm_write(ipu, val, IPU_CONF);
spin_unlock_irqrestore(&ipu->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
struct ipu_devtype {
const char *name;
unsigned long cm_ofs;
unsigned long cpmem_ofs;
unsigned long srm_ofs;
unsigned long tpm_ofs;
unsigned long csi0_ofs;
unsigned long csi1_ofs;
unsigned long ic_ofs;
unsigned long disp0_ofs;
unsigned long disp1_ofs;
unsigned long dc_tmpl_ofs;
......@@ -845,6 +740,9 @@ static struct ipu_devtype ipu_type_imx51 = {
.cpmem_ofs = 0x1f000000,
.srm_ofs = 0x1f040000,
.tpm_ofs = 0x1f060000,
.csi0_ofs = 0x1f030000,
.csi1_ofs = 0x1f038000,
.ic_ofs = 0x1f020000,
.disp0_ofs = 0x1e040000,
.disp1_ofs = 0x1e048000,
.dc_tmpl_ofs = 0x1f080000,
......@@ -858,6 +756,9 @@ static struct ipu_devtype ipu_type_imx53 = {
.cpmem_ofs = 0x07000000,
.srm_ofs = 0x07040000,
.tpm_ofs = 0x07060000,
.csi0_ofs = 0x07030000,
.csi1_ofs = 0x07038000,
.ic_ofs = 0x07020000,
.disp0_ofs = 0x06040000,
.disp1_ofs = 0x06048000,
.dc_tmpl_ofs = 0x07080000,
......@@ -871,6 +772,9 @@ static struct ipu_devtype ipu_type_imx6q = {
.cpmem_ofs = 0x00300000,
.srm_ofs = 0x00340000,
.tpm_ofs = 0x00360000,
.csi0_ofs = 0x00230000,
.csi1_ofs = 0x00238000,
.ic_ofs = 0x00220000,
.disp0_ofs = 0x00240000,
.disp1_ofs = 0x00248000,
.dc_tmpl_ofs = 0x00380000,
......@@ -895,8 +799,36 @@ static int ipu_submodules_init(struct ipu_soc *ipu,
struct device *dev = &pdev->dev;
const struct ipu_devtype *devtype = ipu->devtype;
ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs);
if (ret) {
unit = "cpmem";
goto err_cpmem;
}
ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs,
IPU_CONF_CSI0_EN, ipu_clk);
if (ret) {
unit = "csi0";
goto err_csi_0;
}
ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs,
IPU_CONF_CSI1_EN, ipu_clk);
if (ret) {
unit = "csi1";
goto err_csi_1;
}
ret = ipu_ic_init(ipu, dev,
ipu_base + devtype->ic_ofs,
ipu_base + devtype->tpm_ofs);
if (ret) {
unit = "ic";
goto err_ic;
}
ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
IPU_CONF_DI0_EN, ipu_clk);
IPU_CONF_DI0_EN, ipu_clk);
if (ret) {
unit = "di0";
goto err_di_0;
......@@ -949,6 +881,14 @@ static int ipu_submodules_init(struct ipu_soc *ipu,
err_di_1:
ipu_di_exit(ipu, 0);
err_di_0:
ipu_ic_exit(ipu);
err_ic:
ipu_csi_exit(ipu, 1);
err_csi_1:
ipu_csi_exit(ipu, 0);
err_csi_0:
ipu_cpmem_exit(ipu);
err_cpmem:
dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
return ret;
}
......@@ -1025,6 +965,10 @@ static void ipu_submodules_exit(struct ipu_soc *ipu)
ipu_dc_exit(ipu);
ipu_di_exit(ipu, 1);
ipu_di_exit(ipu, 0);
ipu_ic_exit(ipu);
ipu_csi_exit(ipu, 1);
ipu_csi_exit(ipu, 0);
ipu_cpmem_exit(ipu);
}
static int platform_remove_devices_fn(struct device *dev, void *unused)
......@@ -1201,6 +1145,44 @@ static void ipu_irq_exit(struct ipu_soc *ipu)
irq_domain_remove(ipu->domain);
}
void ipu_dump(struct ipu_soc *ipu)
{
int i;
dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n",
ipu_cm_read(ipu, IPU_CONF));
dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n",
ipu_idmac_read(ipu, IDMAC_CONF));
dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n",
ipu_idmac_read(ipu, IDMAC_CHA_EN(0)));
dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n",
ipu_idmac_read(ipu, IDMAC_CHA_EN(32)));
dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n",
ipu_idmac_read(ipu, IDMAC_CHA_PRI(0)));
dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n",
ipu_idmac_read(ipu, IDMAC_CHA_PRI(32)));
dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n",
ipu_idmac_read(ipu, IDMAC_BAND_EN(0)));
dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n",
ipu_idmac_read(ipu, IDMAC_BAND_EN(32)));
dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0)));
dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32)));
dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
ipu_cm_read(ipu, IPU_FS_PROC_FLOW1));
dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
ipu_cm_read(ipu, IPU_FS_PROC_FLOW2));
dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
ipu_cm_read(ipu, IPU_FS_PROC_FLOW3));
dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
ipu_cm_read(ipu, IPU_FS_DISP_FLOW1));
for (i = 0; i < 15; i++)
dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i,
ipu_cm_read(ipu, IPU_INT_CTRL(i)));
}
EXPORT_SYMBOL_GPL(ipu_dump);
static int ipu_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
......@@ -1243,6 +1225,12 @@ static int ipu_probe(struct platform_device *pdev)
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, "csi0: 0x%08lx\n",
ipu_base + devtype->csi0_ofs);
dev_dbg(&pdev->dev, "csi1: 0x%08lx\n",
ipu_base + devtype->csi1_ofs);
dev_dbg(&pdev->dev, "ic: 0x%08lx\n",
ipu_base + devtype->ic_ofs);
dev_dbg(&pdev->dev, "disp0: 0x%08lx\n",
ipu_base + devtype->disp0_ofs);
dev_dbg(&pdev->dev, "disp1: 0x%08lx\n",
......@@ -1265,10 +1253,8 @@ static int ipu_probe(struct platform_device *pdev)
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)
if (!ipu->cm_reg || !ipu->idmac_reg)
return -ENOMEM;
ipu->clk = devm_clk_get(&pdev->dev, "bus");
......
/*
* Copyright (C) 2012 Mentor Graphics Inc.
* Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/types.h>
#include <linux/bitrev.h>
#include <linux/io.h>
#include <drm/drm_fourcc.h>
#include "ipu-prv.h"
struct ipu_cpmem_word {
u32 data[5];
u32 res[3];
};
struct ipu_ch_param {
struct ipu_cpmem_word word[2];
};
struct ipu_cpmem {
struct ipu_ch_param __iomem *base;
u32 module;
spinlock_t lock;
int use_count;
struct ipu_soc *ipu;
};
#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_ROT_HF_VF IPU_CPMEM_WORD(0, 119, 3)
#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)
static inline struct ipu_ch_param __iomem *
ipu_get_cpmem(struct ipuv3_channel *ch)
{
struct ipu_cpmem *cpmem = ch->ipu->cpmem_priv;
return cpmem->base + ch->num;
}
static void ipu_ch_param_write_field(struct ipuv3_channel *ch, u32 wbs, u32 v)
{
struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch);
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]);
}
}
static u32 ipu_ch_param_read_field(struct ipuv3_channel *ch, u32 wbs)
{
struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch);
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;
}
/*
* 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_YUV422P:
return DRM_FORMAT_YUV422;
case V4L2_PIX_FMT_YVU420:
return DRM_FORMAT_YVU420;
case V4L2_PIX_FMT_NV12:
return DRM_FORMAT_NV12;
case V4L2_PIX_FMT_NV16:
return DRM_FORMAT_NV16;
}
return -EINVAL;
}
void ipu_cpmem_zero(struct ipuv3_channel *ch)
{
struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch);
void __iomem *base = p;
int i;
for (i = 0; i < sizeof(*p) / sizeof(u32); i++)
writel(0, base + i * sizeof(u32));
}
EXPORT_SYMBOL_GPL(ipu_cpmem_zero);
void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres)
{
ipu_ch_param_write_field(ch, IPU_FIELD_FW, xres - 1);
ipu_ch_param_write_field(ch, IPU_FIELD_FH, yres - 1);
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_resolution);
void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride)
{
ipu_ch_param_write_field(ch, IPU_FIELD_SLY, stride - 1);
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_stride);
void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch)
{
struct ipu_soc *ipu = ch->ipu;
u32 val;
if (ipu->ipu_type == IPUV3EX)
ipu_ch_param_write_field(ch, IPU_FIELD_ID, 1);
val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(ch->num));
val |= 1 << (ch->num % 32);
ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(ch->num));
};
EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority);
void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf)
{
if (bufnum)
ipu_ch_param_write_field(ch, IPU_FIELD_EBA1, buf >> 3);
else
ipu_ch_param_write_field(ch, IPU_FIELD_EBA0, buf >> 3);
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer);
void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride)
{
ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1);
ipu_ch_param_write_field(ch, IPU_FIELD_ILO, stride / 8);
ipu_ch_param_write_field(ch, IPU_FIELD_SLY, (stride * 2) - 1);
};
EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan);
void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id)
{
id &= 0x3;
ipu_ch_param_write_field(ch, IPU_FIELD_ID, id);
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id);
void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize)
{
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1);
};
EXPORT_SYMBOL_GPL(ipu_cpmem_set_burstsize);
void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch)
{
ipu_ch_param_write_field(ch, IPU_FIELD_BM, 1);
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_block_mode);
void ipu_cpmem_set_rotation(struct ipuv3_channel *ch,
enum ipu_rotate_mode rot)
{
u32 temp_rot = bitrev8(rot) >> 5;
ipu_ch_param_write_field(ch, IPU_FIELD_ROT_HF_VF, temp_rot);
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_rotation);
int ipu_cpmem_set_format_rgb(struct ipuv3_channel *ch,
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(ch, IPU_FIELD_WID0, rgb->red.length - 1);
ipu_ch_param_write_field(ch, IPU_FIELD_OFS0, ro);
ipu_ch_param_write_field(ch, IPU_FIELD_WID1, rgb->green.length - 1);
ipu_ch_param_write_field(ch, IPU_FIELD_OFS1, go);
ipu_ch_param_write_field(ch, IPU_FIELD_WID2, rgb->blue.length - 1);
ipu_ch_param_write_field(ch, IPU_FIELD_OFS2, bo);
if (rgb->transp.length) {
ipu_ch_param_write_field(ch, IPU_FIELD_WID3,
rgb->transp.length - 1);
ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, to);
} else {
ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7);
ipu_ch_param_write_field(ch, 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(ch, IPU_FIELD_BPP, bpp);
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb);
ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 7); /* rgb mode */
return 0;
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb);
int ipu_cpmem_set_format_passthrough(struct ipuv3_channel *ch, 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(ch, IPU_FIELD_BPP, bpp);
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb);
ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 6); /* raw mode */
return 0;
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough);
void ipu_cpmem_set_yuv_interleaved(struct ipuv3_channel *ch, u32 pixel_format)
{
switch (pixel_format) {
case V4L2_PIX_FMT_UYVY:
ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */
ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA);/* pix fmt */
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */
break;
case V4L2_PIX_FMT_YUYV:
ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */
ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8);/* pix fmt */
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */
break;
}
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved);
void ipu_cpmem_set_yuv_planar_full(struct ipuv3_channel *ch,
u32 pixel_format, int stride,
int u_offset, int v_offset)
{
switch (pixel_format) {
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YUV422P:
ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, (stride / 2) - 1);
ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_offset / 8);
ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_offset / 8);
break;
case V4L2_PIX_FMT_YVU420:
ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, (stride / 2) - 1);
ipu_ch_param_write_field(ch, IPU_FIELD_UBO, v_offset / 8);
ipu_ch_param_write_field(ch, IPU_FIELD_VBO, u_offset / 8);
break;
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV16:
ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, stride - 1);
ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_offset / 8);
ipu_ch_param_write_field(ch, IPU_FIELD_VBO, u_offset / 8);
break;
}
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full);
void ipu_cpmem_set_yuv_planar(struct ipuv3_channel *ch,
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(ch, pixel_format, stride,
u_offset, v_offset);
break;
case V4L2_PIX_FMT_YUV422P:
uv_stride = stride / 2;
u_offset = stride * height;
v_offset = u_offset + (uv_stride * height);
ipu_cpmem_set_yuv_planar_full(ch, pixel_format, stride,
u_offset, v_offset);
break;
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV16:
u_offset = stride * height;
ipu_cpmem_set_yuv_planar_full(ch, pixel_format, stride,
u_offset, 0);
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)
#define U2_OFFSET(pix, x, y) ((pix->width * pix->height) + \
(pix->width * (y) / 2) + (x) / 2)
#define V2_OFFSET(pix, x, y) ((pix->width * pix->height) + \
(pix->width * pix->height / 2) + \
(pix->width * (y) / 2) + (x) / 2)
#define UV_OFFSET(pix, x, y) ((pix->width * pix->height) + \
(pix->width * (y) / 2) + (x))
#define UV2_OFFSET(pix, x, y) ((pix->width * pix->height) + \
(pix->width * y) + (x))
int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc)
{
switch (drm_fourcc) {
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YVU420:
/* pix format */
ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 2);
/* burst size */
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);
break;
case DRM_FORMAT_YUV422:
case DRM_FORMAT_YVU422:
/* pix format */
ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 1);
/* burst size */
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);
break;
case DRM_FORMAT_NV12:
/* pix format */
ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 4);
/* burst size */
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);
break;
case DRM_FORMAT_NV16:
/* pix format */
ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 3);
/* burst size */
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);
break;
case DRM_FORMAT_UYVY:
/* bits/pixel */
ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3);
/* pix format */
ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA);
/* burst size */
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);
break;
case DRM_FORMAT_YUYV:
/* bits/pixel */
ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3);
/* pix format */
ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8);
/* burst size */
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);
break;
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_XBGR8888:
ipu_cpmem_set_format_rgb(ch, &def_bgr_32);
break;
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_XRGB8888:
ipu_cpmem_set_format_rgb(ch, &def_rgb_32);
break;
case DRM_FORMAT_BGR888:
ipu_cpmem_set_format_rgb(ch, &def_bgr_24);
break;
case DRM_FORMAT_RGB888:
ipu_cpmem_set_format_rgb(ch, &def_rgb_24);
break;
case DRM_FORMAT_RGB565:
ipu_cpmem_set_format_rgb(ch, &def_rgb_16);
break;
case DRM_FORMAT_BGR565:
ipu_cpmem_set_format_rgb(ch, &def_bgr_16);
break;
default:
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt);
int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image)
{
struct v4l2_pix_format *pix = &image->pix;
int 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(ch, image->rect.width, image->rect.height);
ipu_cpmem_set_stride(ch, pix->bytesperline);
ipu_cpmem_set_fmt(ch, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat));
switch (pix->pixelformat) {
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
u_offset = U_OFFSET(pix, image->rect.left,
image->rect.top) - offset;
v_offset = V_OFFSET(pix, image->rect.left,
image->rect.top) - offset;
ipu_cpmem_set_yuv_planar_full(ch, pix->pixelformat,
pix->bytesperline,
u_offset, v_offset);
break;
case V4L2_PIX_FMT_YUV422P:
offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
u_offset = U2_OFFSET(pix, image->rect.left,
image->rect.top) - offset;
v_offset = V2_OFFSET(pix, image->rect.left,
image->rect.top) - offset;
ipu_cpmem_set_yuv_planar_full(ch, pix->pixelformat,
pix->bytesperline,
u_offset, v_offset);
break;
case V4L2_PIX_FMT_NV12:
offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
u_offset = UV_OFFSET(pix, image->rect.left,
image->rect.top) - offset;
v_offset = 0;
ipu_cpmem_set_yuv_planar_full(ch, pix->pixelformat,
pix->bytesperline,
u_offset, v_offset);
break;
case V4L2_PIX_FMT_NV16:
offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
u_offset = UV2_OFFSET(pix, image->rect.left,
image->rect.top) - offset;
v_offset = 0;
ipu_cpmem_set_yuv_planar_full(ch, pix->pixelformat,
pix->bytesperline,
u_offset, v_offset);
break;
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_RGB565:
offset = image->rect.left * 2 +
image->rect.top * pix->bytesperline;
break;
case V4L2_PIX_FMT_RGB32:
case V4L2_PIX_FMT_BGR32:
offset = image->rect.left * 4 +
image->rect.top * pix->bytesperline;
break;
case V4L2_PIX_FMT_RGB24:
case V4L2_PIX_FMT_BGR24:
offset = image->rect.left * 3 +
image->rect.top * pix->bytesperline;
break;
default:
return -EINVAL;
}
ipu_cpmem_set_buffer(ch, 0, image->phys0 + offset);
ipu_cpmem_set_buffer(ch, 1, image->phys1 + offset);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_image);
void ipu_cpmem_dump(struct ipuv3_channel *ch)
{
struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch);
struct ipu_soc *ipu = ch->ipu;
int chno = ch->num;
dev_dbg(ipu->dev, "ch %d word 0 - %08X %08X %08X %08X %08X\n", chno,
readl(&p->word[0].data[0]),
readl(&p->word[0].data[1]),
readl(&p->word[0].data[2]),
readl(&p->word[0].data[3]),
readl(&p->word[0].data[4]));
dev_dbg(ipu->dev, "ch %d word 1 - %08X %08X %08X %08X %08X\n", chno,
readl(&p->word[1].data[0]),
readl(&p->word[1].data[1]),
readl(&p->word[1].data[2]),
readl(&p->word[1].data[3]),
readl(&p->word[1].data[4]));
dev_dbg(ipu->dev, "PFS 0x%x, ",
ipu_ch_param_read_field(ch, IPU_FIELD_PFS));
dev_dbg(ipu->dev, "BPP 0x%x, ",
ipu_ch_param_read_field(ch, IPU_FIELD_BPP));
dev_dbg(ipu->dev, "NPB 0x%x\n",
ipu_ch_param_read_field(ch, IPU_FIELD_NPB));
dev_dbg(ipu->dev, "FW %d, ",
ipu_ch_param_read_field(ch, IPU_FIELD_FW));
dev_dbg(ipu->dev, "FH %d, ",
ipu_ch_param_read_field(ch, IPU_FIELD_FH));
dev_dbg(ipu->dev, "EBA0 0x%x\n",
ipu_ch_param_read_field(ch, IPU_FIELD_EBA0) << 3);
dev_dbg(ipu->dev, "EBA1 0x%x\n",
ipu_ch_param_read_field(ch, IPU_FIELD_EBA1) << 3);
dev_dbg(ipu->dev, "Stride %d\n",
ipu_ch_param_read_field(ch, IPU_FIELD_SL));
dev_dbg(ipu->dev, "scan_order %d\n",
ipu_ch_param_read_field(ch, IPU_FIELD_SO));
dev_dbg(ipu->dev, "uv_stride %d\n",
ipu_ch_param_read_field(ch, IPU_FIELD_SLUV));
dev_dbg(ipu->dev, "u_offset 0x%x\n",
ipu_ch_param_read_field(ch, IPU_FIELD_UBO) << 3);
dev_dbg(ipu->dev, "v_offset 0x%x\n",
ipu_ch_param_read_field(ch, IPU_FIELD_VBO) << 3);
dev_dbg(ipu->dev, "Width0 %d+1, ",
ipu_ch_param_read_field(ch, IPU_FIELD_WID0));
dev_dbg(ipu->dev, "Width1 %d+1, ",
ipu_ch_param_read_field(ch, IPU_FIELD_WID1));
dev_dbg(ipu->dev, "Width2 %d+1, ",
ipu_ch_param_read_field(ch, IPU_FIELD_WID2));
dev_dbg(ipu->dev, "Width3 %d+1, ",
ipu_ch_param_read_field(ch, IPU_FIELD_WID3));
dev_dbg(ipu->dev, "Offset0 %d, ",
ipu_ch_param_read_field(ch, IPU_FIELD_OFS0));
dev_dbg(ipu->dev, "Offset1 %d, ",
ipu_ch_param_read_field(ch, IPU_FIELD_OFS1));
dev_dbg(ipu->dev, "Offset2 %d, ",
ipu_ch_param_read_field(ch, IPU_FIELD_OFS2));
dev_dbg(ipu->dev, "Offset3 %d\n",
ipu_ch_param_read_field(ch, IPU_FIELD_OFS3));
}
EXPORT_SYMBOL_GPL(ipu_cpmem_dump);
int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base)
{
struct ipu_cpmem *cpmem;
cpmem = devm_kzalloc(dev, sizeof(*cpmem), GFP_KERNEL);
if (!cpmem)
return -ENOMEM;
ipu->cpmem_priv = cpmem;
spin_lock_init(&cpmem->lock);
cpmem->base = devm_ioremap(dev, base, SZ_128K);
if (!cpmem->base)
return -ENOMEM;
dev_dbg(dev, "CPMEM base: 0x%08lx remapped to %p\n",
base, cpmem->base);
cpmem->ipu = ipu;
return 0;
}
void ipu_cpmem_exit(struct ipu_soc *ipu)
{
}
/*
* Copyright (C) 2012-2014 Mentor Graphics Inc.
* 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/io.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/videodev2.h>
#include <uapi/linux/v4l2-mediabus.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include "ipu-prv.h"
struct ipu_csi {
void __iomem *base;
int id;
u32 module;
struct clk *clk_ipu; /* IPU bus clock */
spinlock_t lock;
bool inuse;
struct ipu_soc *ipu;
};
/* CSI Register Offsets */
#define CSI_SENS_CONF 0x0000
#define CSI_SENS_FRM_SIZE 0x0004
#define CSI_ACT_FRM_SIZE 0x0008
#define CSI_OUT_FRM_CTRL 0x000c
#define CSI_TST_CTRL 0x0010
#define CSI_CCIR_CODE_1 0x0014
#define CSI_CCIR_CODE_2 0x0018
#define CSI_CCIR_CODE_3 0x001c
#define CSI_MIPI_DI 0x0020
#define CSI_SKIP 0x0024
#define CSI_CPD_CTRL 0x0028
#define CSI_CPD_RC(n) (0x002c + ((n)*4))
#define CSI_CPD_RS(n) (0x004c + ((n)*4))
#define CSI_CPD_GRC(n) (0x005c + ((n)*4))
#define CSI_CPD_GRS(n) (0x007c + ((n)*4))
#define CSI_CPD_GBC(n) (0x008c + ((n)*4))
#define CSI_CPD_GBS(n) (0x00Ac + ((n)*4))
#define CSI_CPD_BC(n) (0x00Bc + ((n)*4))
#define CSI_CPD_BS(n) (0x00Dc + ((n)*4))
#define CSI_CPD_OFFSET1 0x00ec
#define CSI_CPD_OFFSET2 0x00f0
/* CSI Register Fields */
#define CSI_SENS_CONF_DATA_FMT_SHIFT 8
#define CSI_SENS_CONF_DATA_FMT_MASK 0x00000700
#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 0L
#define CSI_SENS_CONF_DATA_FMT_YUV422_YUYV 1L
#define CSI_SENS_CONF_DATA_FMT_YUV422_UYVY 2L
#define CSI_SENS_CONF_DATA_FMT_BAYER 3L
#define CSI_SENS_CONF_DATA_FMT_RGB565 4L
#define CSI_SENS_CONF_DATA_FMT_RGB555 5L
#define CSI_SENS_CONF_DATA_FMT_RGB444 6L
#define CSI_SENS_CONF_DATA_FMT_JPEG 7L
#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0
#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1
#define CSI_SENS_CONF_DATA_POL_SHIFT 2
#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3
#define CSI_SENS_CONF_SENS_PRTCL_MASK 0x00000070
#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4
#define CSI_SENS_CONF_PACK_TIGHT_SHIFT 7
#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 11
#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15
#define CSI_SENS_CONF_DIVRATIO_SHIFT 16
#define CSI_SENS_CONF_DIVRATIO_MASK 0x00ff0000
#define CSI_SENS_CONF_DATA_DEST_SHIFT 24
#define CSI_SENS_CONF_DATA_DEST_MASK 0x07000000
#define CSI_SENS_CONF_JPEG8_EN_SHIFT 27
#define CSI_SENS_CONF_JPEG_EN_SHIFT 28
#define CSI_SENS_CONF_FORCE_EOF_SHIFT 29
#define CSI_SENS_CONF_DATA_EN_POL_SHIFT 31
#define CSI_DATA_DEST_IC 2
#define CSI_DATA_DEST_IDMAC 4
#define CSI_CCIR_ERR_DET_EN 0x01000000
#define CSI_HORI_DOWNSIZE_EN 0x80000000
#define CSI_VERT_DOWNSIZE_EN 0x40000000
#define CSI_TEST_GEN_MODE_EN 0x01000000
#define CSI_HSC_MASK 0x1fff0000
#define CSI_HSC_SHIFT 16
#define CSI_VSC_MASK 0x00000fff
#define CSI_VSC_SHIFT 0
#define CSI_TEST_GEN_R_MASK 0x000000ff
#define CSI_TEST_GEN_R_SHIFT 0
#define CSI_TEST_GEN_G_MASK 0x0000ff00
#define CSI_TEST_GEN_G_SHIFT 8
#define CSI_TEST_GEN_B_MASK 0x00ff0000
#define CSI_TEST_GEN_B_SHIFT 16
#define CSI_MAX_RATIO_SKIP_SMFC_MASK 0x00000007
#define CSI_MAX_RATIO_SKIP_SMFC_SHIFT 0
#define CSI_SKIP_SMFC_MASK 0x000000f8
#define CSI_SKIP_SMFC_SHIFT 3
#define CSI_ID_2_SKIP_MASK 0x00000300
#define CSI_ID_2_SKIP_SHIFT 8
#define CSI_COLOR_FIRST_ROW_MASK 0x00000002
#define CSI_COLOR_FIRST_COMP_MASK 0x00000001
/* MIPI CSI-2 data types */
#define MIPI_DT_YUV420 0x18 /* YYY.../UYVY.... */
#define MIPI_DT_YUV420_LEGACY 0x1a /* UYY.../VYY... */
#define MIPI_DT_YUV422 0x1e /* UYVY... */
#define MIPI_DT_RGB444 0x20
#define MIPI_DT_RGB555 0x21
#define MIPI_DT_RGB565 0x22
#define MIPI_DT_RGB666 0x23
#define MIPI_DT_RGB888 0x24
#define MIPI_DT_RAW6 0x28
#define MIPI_DT_RAW7 0x29
#define MIPI_DT_RAW8 0x2a
#define MIPI_DT_RAW10 0x2b
#define MIPI_DT_RAW12 0x2c
#define MIPI_DT_RAW14 0x2d
/*
* Bitfield of CSI bus signal polarities and modes.
*/
struct ipu_csi_bus_config {
unsigned data_width:4;
unsigned clk_mode:3;
unsigned ext_vsync:1;
unsigned vsync_pol:1;
unsigned hsync_pol:1;
unsigned pixclk_pol:1;
unsigned data_pol:1;
unsigned sens_clksrc:1;
unsigned pack_tight:1;
unsigned force_eof:1;
unsigned data_en_pol:1;
unsigned data_fmt;
unsigned mipi_dt;
};
/*
* Enumeration of CSI data bus widths.
*/
enum ipu_csi_data_width {
IPU_CSI_DATA_WIDTH_4 = 0,
IPU_CSI_DATA_WIDTH_8 = 1,
IPU_CSI_DATA_WIDTH_10 = 3,
IPU_CSI_DATA_WIDTH_12 = 5,
IPU_CSI_DATA_WIDTH_16 = 9,
};
/*
* Enumeration of CSI clock modes.
*/
enum ipu_csi_clk_mode {
IPU_CSI_CLK_MODE_GATED_CLK,
IPU_CSI_CLK_MODE_NONGATED_CLK,
IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE,
IPU_CSI_CLK_MODE_CCIR656_INTERLACED,
IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR,
IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR,
IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR,
IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR,
};
static inline u32 ipu_csi_read(struct ipu_csi *csi, unsigned offset)
{
return readl(csi->base + offset);
}
static inline void ipu_csi_write(struct ipu_csi *csi, u32 value,
unsigned offset)
{
writel(value, csi->base + offset);
}
/*
* Set mclk division ratio for generating test mode mclk. Only used
* for test generator.
*/
static int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk,
u32 ipu_clk)
{
u32 temp;
u32 div_ratio;
div_ratio = (ipu_clk / pixel_clk) - 1;
if (div_ratio > 0xFF || div_ratio < 0) {
dev_err(csi->ipu->dev,
"value of pixel_clk extends normal range\n");
return -EINVAL;
}
temp = ipu_csi_read(csi, CSI_SENS_CONF);
temp &= ~CSI_SENS_CONF_DIVRATIO_MASK;
ipu_csi_write(csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT),
CSI_SENS_CONF);
return 0;
}
/*
* Find the CSI data format and data width for the given V4L2 media
* bus pixel format code.
*/
static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code)
{
switch (mbus_code) {
case V4L2_MBUS_FMT_BGR565_2X8_BE:
case V4L2_MBUS_FMT_BGR565_2X8_LE:
case V4L2_MBUS_FMT_RGB565_2X8_BE:
case V4L2_MBUS_FMT_RGB565_2X8_LE:
cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565;
cfg->mipi_dt = MIPI_DT_RGB565;
cfg->data_width = IPU_CSI_DATA_WIDTH_8;
break;
case V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE:
case V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE:
cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB444;
cfg->mipi_dt = MIPI_DT_RGB444;
cfg->data_width = IPU_CSI_DATA_WIDTH_8;
break;
case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555;
cfg->mipi_dt = MIPI_DT_RGB555;
cfg->data_width = IPU_CSI_DATA_WIDTH_8;
break;
case V4L2_MBUS_FMT_UYVY8_2X8:
cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
cfg->mipi_dt = MIPI_DT_YUV422;
cfg->data_width = IPU_CSI_DATA_WIDTH_8;
break;
case V4L2_MBUS_FMT_YUYV8_2X8:
cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
cfg->mipi_dt = MIPI_DT_YUV422;
cfg->data_width = IPU_CSI_DATA_WIDTH_8;
break;
case V4L2_MBUS_FMT_UYVY8_1X16:
cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
cfg->mipi_dt = MIPI_DT_YUV422;
cfg->data_width = IPU_CSI_DATA_WIDTH_16;
break;
case V4L2_MBUS_FMT_YUYV8_1X16:
cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
cfg->mipi_dt = MIPI_DT_YUV422;
cfg->data_width = IPU_CSI_DATA_WIDTH_16;
break;
case V4L2_MBUS_FMT_SBGGR8_1X8:
case V4L2_MBUS_FMT_SGBRG8_1X8:
case V4L2_MBUS_FMT_SGRBG8_1X8:
case V4L2_MBUS_FMT_SRGGB8_1X8:
cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
cfg->mipi_dt = MIPI_DT_RAW8;
cfg->data_width = IPU_CSI_DATA_WIDTH_8;
break;
case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8:
case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8:
case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8:
case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE:
case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE:
case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE:
cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
cfg->mipi_dt = MIPI_DT_RAW10;
cfg->data_width = IPU_CSI_DATA_WIDTH_8;
break;
case V4L2_MBUS_FMT_SBGGR10_1X10:
case V4L2_MBUS_FMT_SGBRG10_1X10:
case V4L2_MBUS_FMT_SGRBG10_1X10:
case V4L2_MBUS_FMT_SRGGB10_1X10:
cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
cfg->mipi_dt = MIPI_DT_RAW10;
cfg->data_width = IPU_CSI_DATA_WIDTH_10;
break;
case V4L2_MBUS_FMT_SBGGR12_1X12:
case V4L2_MBUS_FMT_SGBRG12_1X12:
case V4L2_MBUS_FMT_SGRBG12_1X12:
case V4L2_MBUS_FMT_SRGGB12_1X12:
cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
cfg->mipi_dt = MIPI_DT_RAW12;
cfg->data_width = IPU_CSI_DATA_WIDTH_12;
break;
case V4L2_MBUS_FMT_JPEG_1X8:
/* TODO */
cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_JPEG;
cfg->mipi_dt = MIPI_DT_RAW8;
cfg->data_width = IPU_CSI_DATA_WIDTH_8;
break;
default:
return -EINVAL;
}
return 0;
}
/*
* Fill a CSI bus config struct from mbus_config and mbus_framefmt.
*/
static void fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg,
struct v4l2_mbus_config *mbus_cfg,
struct v4l2_mbus_framefmt *mbus_fmt)
{
memset(csicfg, 0, sizeof(*csicfg));
mbus_code_to_bus_cfg(csicfg, mbus_fmt->code);
switch (mbus_cfg->type) {
case V4L2_MBUS_PARALLEL:
csicfg->ext_vsync = 1;
csicfg->vsync_pol = (mbus_cfg->flags &
V4L2_MBUS_VSYNC_ACTIVE_LOW) ? 1 : 0;
csicfg->hsync_pol = (mbus_cfg->flags &
V4L2_MBUS_HSYNC_ACTIVE_LOW) ? 1 : 0;
csicfg->pixclk_pol = (mbus_cfg->flags &
V4L2_MBUS_PCLK_SAMPLE_FALLING) ? 1 : 0;
csicfg->clk_mode = IPU_CSI_CLK_MODE_GATED_CLK;
break;
case V4L2_MBUS_BT656:
csicfg->ext_vsync = 0;
if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field))
csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED;
else
csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE;
break;
case V4L2_MBUS_CSI2:
/*
* MIPI CSI-2 requires non gated clock mode, all other
* parameters are not applicable for MIPI CSI-2 bus.
*/
csicfg->clk_mode = IPU_CSI_CLK_MODE_NONGATED_CLK;
break;
default:
/* will never get here, keep compiler quiet */
break;
}
}
int ipu_csi_init_interface(struct ipu_csi *csi,
struct v4l2_mbus_config *mbus_cfg,
struct v4l2_mbus_framefmt *mbus_fmt)
{
struct ipu_csi_bus_config cfg;
unsigned long flags;
u32 data = 0;
fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt);
/* Set the CSI_SENS_CONF register remaining fields */
data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
cfg.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
cfg.vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
cfg.hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
cfg.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
cfg.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
cfg.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
cfg.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT |
cfg.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT |
cfg.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT;
spin_lock_irqsave(&csi->lock, flags);
ipu_csi_write(csi, data, CSI_SENS_CONF);
/* Setup sensor frame size */
ipu_csi_write(csi,
(mbus_fmt->width - 1) | ((mbus_fmt->height - 1) << 16),
CSI_SENS_FRM_SIZE);
/* Set CCIR registers */
switch (cfg.clk_mode) {
case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
ipu_csi_write(csi, 0x40030, CSI_CCIR_CODE_1);
ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
break;
case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
if (mbus_fmt->width == 720 && mbus_fmt->height == 576) {
/*
* PAL case
*
* Field0BlankEnd = 0x6, Field0BlankStart = 0x2,
* Field0ActiveEnd = 0x4, Field0ActiveStart = 0
* Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
* Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
*/
ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
CSI_CCIR_CODE_1);
ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
} else if (mbus_fmt->width == 720 && mbus_fmt->height == 480) {
/*
* NTSC case
*
* Field0BlankEnd = 0x7, Field0BlankStart = 0x3,
* Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1
* Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
* Field1ActiveEnd = 0x4, Field1ActiveStart = 0
*/
ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
CSI_CCIR_CODE_1);
ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
} else {
dev_err(csi->ipu->dev,
"Unsupported CCIR656 interlaced video mode\n");
spin_unlock_irqrestore(&csi->lock, flags);
return -EINVAL;
}
break;
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
ipu_csi_write(csi, 0x40030 | CSI_CCIR_ERR_DET_EN,
CSI_CCIR_CODE_1);
ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
break;
case IPU_CSI_CLK_MODE_GATED_CLK:
case IPU_CSI_CLK_MODE_NONGATED_CLK:
ipu_csi_write(csi, 0, CSI_CCIR_CODE_1);
break;
}
dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
ipu_csi_read(csi, CSI_SENS_CONF));
dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
ipu_csi_read(csi, CSI_ACT_FRM_SIZE));
spin_unlock_irqrestore(&csi->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_csi_init_interface);
bool ipu_csi_is_interlaced(struct ipu_csi *csi)
{
unsigned long flags;
u32 sensor_protocol;
spin_lock_irqsave(&csi->lock, flags);
sensor_protocol =
(ipu_csi_read(csi, CSI_SENS_CONF) &
CSI_SENS_CONF_SENS_PRTCL_MASK) >>
CSI_SENS_CONF_SENS_PRTCL_SHIFT;
spin_unlock_irqrestore(&csi->lock, flags);
switch (sensor_protocol) {
case IPU_CSI_CLK_MODE_GATED_CLK:
case IPU_CSI_CLK_MODE_NONGATED_CLK:
case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
return false;
case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
return true;
default:
dev_err(csi->ipu->dev,
"CSI %d sensor protocol unsupported\n", csi->id);
return false;
}
}
EXPORT_SYMBOL_GPL(ipu_csi_is_interlaced);
void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w)
{
unsigned long flags;
u32 reg;
spin_lock_irqsave(&csi->lock, flags);
reg = ipu_csi_read(csi, CSI_ACT_FRM_SIZE);
w->width = (reg & 0xFFFF) + 1;
w->height = (reg >> 16 & 0xFFFF) + 1;
reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL);
w->left = (reg & CSI_HSC_MASK) >> CSI_HSC_SHIFT;
w->top = (reg & CSI_VSC_MASK) >> CSI_VSC_SHIFT;
spin_unlock_irqrestore(&csi->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_csi_get_window);
void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w)
{
unsigned long flags;
u32 reg;
spin_lock_irqsave(&csi->lock, flags);
ipu_csi_write(csi, (w->width - 1) | ((w->height - 1) << 16),
CSI_ACT_FRM_SIZE);
reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL);
reg &= ~(CSI_HSC_MASK | CSI_VSC_MASK);
reg |= ((w->top << CSI_VSC_SHIFT) | (w->left << CSI_HSC_SHIFT));
ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL);
spin_unlock_irqrestore(&csi->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_csi_set_window);
void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active,
u32 r_value, u32 g_value, u32 b_value,
u32 pix_clk)
{
unsigned long flags;
u32 ipu_clk = clk_get_rate(csi->clk_ipu);
u32 temp;
spin_lock_irqsave(&csi->lock, flags);
temp = ipu_csi_read(csi, CSI_TST_CTRL);
if (active == false) {
temp &= ~CSI_TEST_GEN_MODE_EN;
ipu_csi_write(csi, temp, CSI_TST_CTRL);
} else {
/* Set sensb_mclk div_ratio */
ipu_csi_set_testgen_mclk(csi, pix_clk, ipu_clk);
temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK |
CSI_TEST_GEN_B_MASK);
temp |= CSI_TEST_GEN_MODE_EN;
temp |= (r_value << CSI_TEST_GEN_R_SHIFT) |
(g_value << CSI_TEST_GEN_G_SHIFT) |
(b_value << CSI_TEST_GEN_B_SHIFT);
ipu_csi_write(csi, temp, CSI_TST_CTRL);
}
spin_unlock_irqrestore(&csi->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_csi_set_test_generator);
int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc,
struct v4l2_mbus_framefmt *mbus_fmt)
{
struct ipu_csi_bus_config cfg;
unsigned long flags;
u32 temp;
if (vc > 3)
return -EINVAL;
mbus_code_to_bus_cfg(&cfg, mbus_fmt->code);
spin_lock_irqsave(&csi->lock, flags);
temp = ipu_csi_read(csi, CSI_MIPI_DI);
temp &= ~(0xff << (vc * 8));
temp |= (cfg.mipi_dt << (vc * 8));
ipu_csi_write(csi, temp, CSI_MIPI_DI);
spin_unlock_irqrestore(&csi->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype);
int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip,
u32 max_ratio, u32 id)
{
unsigned long flags;
u32 temp;
if (max_ratio > 5 || id > 3)
return -EINVAL;
spin_lock_irqsave(&csi->lock, flags);
temp = ipu_csi_read(csi, CSI_SKIP);
temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK |
CSI_SKIP_SMFC_MASK);
temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) |
(id << CSI_ID_2_SKIP_SHIFT) |
(skip << CSI_SKIP_SMFC_SHIFT);
ipu_csi_write(csi, temp, CSI_SKIP);
spin_unlock_irqrestore(&csi->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc);
int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest)
{
unsigned long flags;
u32 csi_sens_conf, dest;
if (csi_dest == IPU_CSI_DEST_IDMAC)
dest = CSI_DATA_DEST_IDMAC;
else
dest = CSI_DATA_DEST_IC; /* IC or VDIC */
spin_lock_irqsave(&csi->lock, flags);
csi_sens_conf = ipu_csi_read(csi, CSI_SENS_CONF);
csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK;
csi_sens_conf |= (dest << CSI_SENS_CONF_DATA_DEST_SHIFT);
ipu_csi_write(csi, csi_sens_conf, CSI_SENS_CONF);
spin_unlock_irqrestore(&csi->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_csi_set_dest);
int ipu_csi_enable(struct ipu_csi *csi)
{
ipu_module_enable(csi->ipu, csi->module);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_csi_enable);
int ipu_csi_disable(struct ipu_csi *csi)
{
ipu_module_disable(csi->ipu, csi->module);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_csi_disable);
struct ipu_csi *ipu_csi_get(struct ipu_soc *ipu, int id)
{
unsigned long flags;
struct ipu_csi *csi, *ret;
if (id > 1)
return ERR_PTR(-EINVAL);
csi = ipu->csi_priv[id];
ret = csi;
spin_lock_irqsave(&csi->lock, flags);
if (csi->inuse) {
ret = ERR_PTR(-EBUSY);
goto unlock;
}
csi->inuse = true;
unlock:
spin_unlock_irqrestore(&csi->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(ipu_csi_get);
void ipu_csi_put(struct ipu_csi *csi)
{
unsigned long flags;
spin_lock_irqsave(&csi->lock, flags);
csi->inuse = false;
spin_unlock_irqrestore(&csi->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_csi_put);
int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id,
unsigned long base, u32 module, struct clk *clk_ipu)
{
struct ipu_csi *csi;
if (id > 1)
return -ENODEV;
csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL);
if (!csi)
return -ENOMEM;
ipu->csi_priv[id] = csi;
spin_lock_init(&csi->lock);
csi->module = module;
csi->id = id;
csi->clk_ipu = clk_ipu;
csi->base = devm_ioremap(dev, base, PAGE_SIZE);
if (!csi->base)
return -ENOMEM;
dev_dbg(dev, "CSI%d base: 0x%08lx remapped to %p\n",
id, base, csi->base);
csi->ipu = ipu;
return 0;
}
void ipu_csi_exit(struct ipu_soc *ipu, int id)
{
}
void ipu_csi_dump(struct ipu_csi *csi)
{
dev_dbg(csi->ipu->dev, "CSI_SENS_CONF: %08x\n",
ipu_csi_read(csi, CSI_SENS_CONF));
dev_dbg(csi->ipu->dev, "CSI_SENS_FRM_SIZE: %08x\n",
ipu_csi_read(csi, CSI_SENS_FRM_SIZE));
dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE: %08x\n",
ipu_csi_read(csi, CSI_ACT_FRM_SIZE));
dev_dbg(csi->ipu->dev, "CSI_OUT_FRM_CTRL: %08x\n",
ipu_csi_read(csi, CSI_OUT_FRM_CTRL));
dev_dbg(csi->ipu->dev, "CSI_TST_CTRL: %08x\n",
ipu_csi_read(csi, CSI_TST_CTRL));
dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_1: %08x\n",
ipu_csi_read(csi, CSI_CCIR_CODE_1));
dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_2: %08x\n",
ipu_csi_read(csi, CSI_CCIR_CODE_2));
dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_3: %08x\n",
ipu_csi_read(csi, CSI_CCIR_CODE_3));
dev_dbg(csi->ipu->dev, "CSI_MIPI_DI: %08x\n",
ipu_csi_read(csi, CSI_MIPI_DI));
dev_dbg(csi->ipu->dev, "CSI_SKIP: %08x\n",
ipu_csi_read(csi, CSI_SKIP));
}
EXPORT_SYMBOL_GPL(ipu_csi_dump);
/*
* Copyright (C) 2012-2014 Mentor Graphics Inc.
* Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/types.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <linux/bitrev.h>
#include <linux/io.h>
#include <linux/err.h>
#include "ipu-prv.h"
/* IC Register Offsets */
#define IC_CONF 0x0000
#define IC_PRP_ENC_RSC 0x0004
#define IC_PRP_VF_RSC 0x0008
#define IC_PP_RSC 0x000C
#define IC_CMBP_1 0x0010
#define IC_CMBP_2 0x0014
#define IC_IDMAC_1 0x0018
#define IC_IDMAC_2 0x001C
#define IC_IDMAC_3 0x0020
#define IC_IDMAC_4 0x0024
/* IC Register Fields */
#define IC_CONF_PRPENC_EN (1 << 0)
#define IC_CONF_PRPENC_CSC1 (1 << 1)
#define IC_CONF_PRPENC_ROT_EN (1 << 2)
#define IC_CONF_PRPVF_EN (1 << 8)
#define IC_CONF_PRPVF_CSC1 (1 << 9)
#define IC_CONF_PRPVF_CSC2 (1 << 10)
#define IC_CONF_PRPVF_CMB (1 << 11)
#define IC_CONF_PRPVF_ROT_EN (1 << 12)
#define IC_CONF_PP_EN (1 << 16)
#define IC_CONF_PP_CSC1 (1 << 17)
#define IC_CONF_PP_CSC2 (1 << 18)
#define IC_CONF_PP_CMB (1 << 19)
#define IC_CONF_PP_ROT_EN (1 << 20)
#define IC_CONF_IC_GLB_LOC_A (1 << 28)
#define IC_CONF_KEY_COLOR_EN (1 << 29)
#define IC_CONF_RWS_EN (1 << 30)
#define IC_CONF_CSI_MEM_WR_EN (1 << 31)
#define IC_IDMAC_1_CB0_BURST_16 (1 << 0)
#define IC_IDMAC_1_CB1_BURST_16 (1 << 1)
#define IC_IDMAC_1_CB2_BURST_16 (1 << 2)
#define IC_IDMAC_1_CB3_BURST_16 (1 << 3)
#define IC_IDMAC_1_CB4_BURST_16 (1 << 4)
#define IC_IDMAC_1_CB5_BURST_16 (1 << 5)
#define IC_IDMAC_1_CB6_BURST_16 (1 << 6)
#define IC_IDMAC_1_CB7_BURST_16 (1 << 7)
#define IC_IDMAC_1_PRPENC_ROT_MASK (0x7 << 11)
#define IC_IDMAC_1_PRPENC_ROT_OFFSET 11
#define IC_IDMAC_1_PRPVF_ROT_MASK (0x7 << 14)
#define IC_IDMAC_1_PRPVF_ROT_OFFSET 14
#define IC_IDMAC_1_PP_ROT_MASK (0x7 << 17)
#define IC_IDMAC_1_PP_ROT_OFFSET 17
#define IC_IDMAC_1_PP_FLIP_RS (1 << 22)
#define IC_IDMAC_1_PRPVF_FLIP_RS (1 << 21)
#define IC_IDMAC_1_PRPENC_FLIP_RS (1 << 20)
#define IC_IDMAC_2_PRPENC_HEIGHT_MASK (0x3ff << 0)
#define IC_IDMAC_2_PRPENC_HEIGHT_OFFSET 0
#define IC_IDMAC_2_PRPVF_HEIGHT_MASK (0x3ff << 10)
#define IC_IDMAC_2_PRPVF_HEIGHT_OFFSET 10
#define IC_IDMAC_2_PP_HEIGHT_MASK (0x3ff << 20)
#define IC_IDMAC_2_PP_HEIGHT_OFFSET 20
#define IC_IDMAC_3_PRPENC_WIDTH_MASK (0x3ff << 0)
#define IC_IDMAC_3_PRPENC_WIDTH_OFFSET 0
#define IC_IDMAC_3_PRPVF_WIDTH_MASK (0x3ff << 10)
#define IC_IDMAC_3_PRPVF_WIDTH_OFFSET 10
#define IC_IDMAC_3_PP_WIDTH_MASK (0x3ff << 20)
#define IC_IDMAC_3_PP_WIDTH_OFFSET 20
struct ic_task_regoffs {
u32 rsc;
u32 tpmem_csc[2];
};
struct ic_task_bitfields {
u32 ic_conf_en;
u32 ic_conf_rot_en;
u32 ic_conf_cmb_en;
u32 ic_conf_csc1_en;
u32 ic_conf_csc2_en;
u32 ic_cmb_galpha_bit;
};
static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = {
[IC_TASK_ENCODER] = {
.rsc = IC_PRP_ENC_RSC,
.tpmem_csc = {0x2008, 0},
},
[IC_TASK_VIEWFINDER] = {
.rsc = IC_PRP_VF_RSC,
.tpmem_csc = {0x4028, 0x4040},
},
[IC_TASK_POST_PROCESSOR] = {
.rsc = IC_PP_RSC,
.tpmem_csc = {0x6060, 0x6078},
},
};
static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = {
[IC_TASK_ENCODER] = {
.ic_conf_en = IC_CONF_PRPENC_EN,
.ic_conf_rot_en = IC_CONF_PRPENC_ROT_EN,
.ic_conf_cmb_en = 0, /* NA */
.ic_conf_csc1_en = IC_CONF_PRPENC_CSC1,
.ic_conf_csc2_en = 0, /* NA */
.ic_cmb_galpha_bit = 0, /* NA */
},
[IC_TASK_VIEWFINDER] = {
.ic_conf_en = IC_CONF_PRPVF_EN,
.ic_conf_rot_en = IC_CONF_PRPVF_ROT_EN,
.ic_conf_cmb_en = IC_CONF_PRPVF_CMB,
.ic_conf_csc1_en = IC_CONF_PRPVF_CSC1,
.ic_conf_csc2_en = IC_CONF_PRPVF_CSC2,
.ic_cmb_galpha_bit = 0,
},
[IC_TASK_POST_PROCESSOR] = {
.ic_conf_en = IC_CONF_PP_EN,
.ic_conf_rot_en = IC_CONF_PP_ROT_EN,
.ic_conf_cmb_en = IC_CONF_PP_CMB,
.ic_conf_csc1_en = IC_CONF_PP_CSC1,
.ic_conf_csc2_en = IC_CONF_PP_CSC2,
.ic_cmb_galpha_bit = 8,
},
};
struct ipu_ic_priv;
struct ipu_ic {
enum ipu_ic_task task;
const struct ic_task_regoffs *reg;
const struct ic_task_bitfields *bit;
enum ipu_color_space in_cs, g_in_cs;
enum ipu_color_space out_cs;
bool graphics;
bool rotation;
bool in_use;
struct ipu_ic_priv *priv;
};
struct ipu_ic_priv {
void __iomem *base;
void __iomem *tpmem_base;
spinlock_t lock;
struct ipu_soc *ipu;
int use_count;
struct ipu_ic task[IC_NUM_TASKS];
};
static inline u32 ipu_ic_read(struct ipu_ic *ic, unsigned offset)
{
return readl(ic->priv->base + offset);
}
static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset)
{
writel(value, ic->priv->base + offset);
}
struct ic_csc_params {
s16 coeff[3][3]; /* signed 9-bit integer coefficients */
s16 offset[3]; /* signed 11+2-bit fixed point offset */
u8 scale:2; /* scale coefficients * 2^(scale-1) */
bool sat:1; /* saturate to (16, 235(Y) / 240(U, V)) */
};
/*
* Y = R * .299 + G * .587 + B * .114;
* U = R * -.169 + G * -.332 + B * .500 + 128.;
* V = R * .500 + G * -.419 + B * -.0813 + 128.;
*/
static const struct ic_csc_params ic_csc_rgb2ycbcr = {
.coeff = {
{ 77, 150, 29 },
{ 469, 427, 128 },
{ 128, 405, 491 },
},
.offset = { 0, 512, 512 },
.scale = 1,
};
/* transparent RGB->RGB matrix for graphics combining */
static const struct ic_csc_params ic_csc_rgb2rgb = {
.coeff = {
{ 128, 0, 0 },
{ 0, 128, 0 },
{ 0, 0, 128 },
},
.scale = 2,
};
/*
* R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
* G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
* B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128);
*/
static const struct ic_csc_params ic_csc_ycbcr2rgb = {
.coeff = {
{ 149, 0, 204 },
{ 149, 462, 408 },
{ 149, 255, 0 },
},
.offset = { -446, 266, -554 },
.scale = 2,
};
static int init_csc(struct ipu_ic *ic,
enum ipu_color_space inf,
enum ipu_color_space outf,
int csc_index)
{
struct ipu_ic_priv *priv = ic->priv;
const struct ic_csc_params *params;
u32 __iomem *base;
const u16 (*c)[3];
const u16 *a;
u32 param;
base = (u32 __iomem *)
(priv->tpmem_base + ic->reg->tpmem_csc[csc_index]);
if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB)
params = &ic_csc_ycbcr2rgb;
else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV)
params = &ic_csc_rgb2ycbcr;
else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB)
params = &ic_csc_rgb2rgb;
else {
dev_err(priv->ipu->dev, "Unsupported color space conversion\n");
return -EINVAL;
}
/* Cast to unsigned */
c = (const u16 (*)[3])params->coeff;
a = (const u16 *)params->offset;
param = ((a[0] & 0x1f) << 27) | ((c[0][0] & 0x1ff) << 18) |
((c[1][1] & 0x1ff) << 9) | (c[2][2] & 0x1ff);
writel(param, base++);
param = ((a[0] & 0x1fe0) >> 5) | (params->scale << 8) |
(params->sat << 9);
writel(param, base++);
param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) |
((c[1][0] & 0x1ff) << 9) | (c[2][0] & 0x1ff);
writel(param, base++);
param = ((a[1] & 0x1fe0) >> 5);
writel(param, base++);
param = ((a[2] & 0x1f) << 27) | ((c[0][2] & 0x1ff) << 18) |
((c[1][2] & 0x1ff) << 9) | (c[2][1] & 0x1ff);
writel(param, base++);
param = ((a[2] & 0x1fe0) >> 5);
writel(param, base++);
return 0;
}
static int calc_resize_coeffs(struct ipu_ic *ic,
u32 in_size, u32 out_size,
u32 *resize_coeff,
u32 *downsize_coeff)
{
struct ipu_ic_priv *priv = ic->priv;
struct ipu_soc *ipu = priv->ipu;
u32 temp_size, temp_downsize;
/*
* Input size cannot be more than 4096, and output size cannot
* be more than 1024
*/
if (in_size > 4096) {
dev_err(ipu->dev, "Unsupported resize (in_size > 4096)\n");
return -EINVAL;
}
if (out_size > 1024) {
dev_err(ipu->dev, "Unsupported resize (out_size > 1024)\n");
return -EINVAL;
}
/* Cannot downsize more than 8:1 */
if ((out_size << 3) < in_size) {
dev_err(ipu->dev, "Unsupported downsize\n");
return -EINVAL;
}
/* Compute downsizing coefficient */
temp_downsize = 0;
temp_size = in_size;
while (((temp_size > 1024) || (temp_size >= out_size * 2)) &&
(temp_downsize < 2)) {
temp_size >>= 1;
temp_downsize++;
}
*downsize_coeff = temp_downsize;
/*
* compute resizing coefficient using the following equation:
* resize_coeff = M * (SI - 1) / (SO - 1)
* where M = 2^13, SI = input size, SO = output size
*/
*resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1);
if (*resize_coeff >= 16384L) {
dev_err(ipu->dev, "Warning! Overflow on resize coeff.\n");
*resize_coeff = 0x3FFF;
}
return 0;
}
void ipu_ic_task_enable(struct ipu_ic *ic)
{
struct ipu_ic_priv *priv = ic->priv;
unsigned long flags;
u32 ic_conf;
spin_lock_irqsave(&priv->lock, flags);
ic_conf = ipu_ic_read(ic, IC_CONF);
ic_conf |= ic->bit->ic_conf_en;
if (ic->rotation)
ic_conf |= ic->bit->ic_conf_rot_en;
if (ic->in_cs != ic->out_cs)
ic_conf |= ic->bit->ic_conf_csc1_en;
if (ic->graphics) {
ic_conf |= ic->bit->ic_conf_cmb_en;
ic_conf |= ic->bit->ic_conf_csc1_en;
if (ic->g_in_cs != ic->out_cs)
ic_conf |= ic->bit->ic_conf_csc2_en;
}
ipu_ic_write(ic, ic_conf, IC_CONF);
spin_unlock_irqrestore(&priv->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_ic_task_enable);
void ipu_ic_task_disable(struct ipu_ic *ic)
{
struct ipu_ic_priv *priv = ic->priv;
unsigned long flags;
u32 ic_conf;
spin_lock_irqsave(&priv->lock, flags);
ic_conf = ipu_ic_read(ic, IC_CONF);
ic_conf &= ~(ic->bit->ic_conf_en |
ic->bit->ic_conf_csc1_en |
ic->bit->ic_conf_rot_en);
if (ic->bit->ic_conf_csc2_en)
ic_conf &= ~ic->bit->ic_conf_csc2_en;
if (ic->bit->ic_conf_cmb_en)
ic_conf &= ~ic->bit->ic_conf_cmb_en;
ipu_ic_write(ic, ic_conf, IC_CONF);
ic->rotation = ic->graphics = false;
spin_unlock_irqrestore(&priv->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_ic_task_disable);
int ipu_ic_task_graphics_init(struct ipu_ic *ic,
enum ipu_color_space in_g_cs,
bool galpha_en, u32 galpha,
bool colorkey_en, u32 colorkey)
{
struct ipu_ic_priv *priv = ic->priv;
unsigned long flags;
u32 reg, ic_conf;
int ret = 0;
if (ic->task == IC_TASK_ENCODER)
return -EINVAL;
spin_lock_irqsave(&priv->lock, flags);
ic_conf = ipu_ic_read(ic, IC_CONF);
if (!(ic_conf & ic->bit->ic_conf_csc1_en)) {
/* need transparent CSC1 conversion */
ret = init_csc(ic, IPUV3_COLORSPACE_RGB,
IPUV3_COLORSPACE_RGB, 0);
if (ret)
goto unlock;
}
ic->g_in_cs = in_g_cs;
if (ic->g_in_cs != ic->out_cs) {
ret = init_csc(ic, ic->g_in_cs, ic->out_cs, 1);
if (ret)
goto unlock;
}
if (galpha_en) {
ic_conf |= IC_CONF_IC_GLB_LOC_A;
reg = ipu_ic_read(ic, IC_CMBP_1);
reg &= ~(0xff << ic->bit->ic_cmb_galpha_bit);
reg |= (galpha << ic->bit->ic_cmb_galpha_bit);
ipu_ic_write(ic, reg, IC_CMBP_1);
} else
ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
if (colorkey_en) {
ic_conf |= IC_CONF_KEY_COLOR_EN;
ipu_ic_write(ic, colorkey, IC_CMBP_2);
} else
ic_conf &= ~IC_CONF_KEY_COLOR_EN;
ipu_ic_write(ic, ic_conf, IC_CONF);
ic->graphics = true;
unlock:
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(ipu_ic_task_graphics_init);
int ipu_ic_task_init(struct ipu_ic *ic,
int in_width, int in_height,
int out_width, int out_height,
enum ipu_color_space in_cs,
enum ipu_color_space out_cs)
{
struct ipu_ic_priv *priv = ic->priv;
u32 reg, downsize_coeff, resize_coeff;
unsigned long flags;
int ret = 0;
/* Setup vertical resizing */
ret = calc_resize_coeffs(ic, in_height, out_height,
&resize_coeff, &downsize_coeff);
if (ret)
return ret;
reg = (downsize_coeff << 30) | (resize_coeff << 16);
/* Setup horizontal resizing */
ret = calc_resize_coeffs(ic, in_width, out_width,
&resize_coeff, &downsize_coeff);
if (ret)
return ret;
reg |= (downsize_coeff << 14) | resize_coeff;
spin_lock_irqsave(&priv->lock, flags);
ipu_ic_write(ic, reg, ic->reg->rsc);
/* Setup color space conversion */
ic->in_cs = in_cs;
ic->out_cs = out_cs;
if (ic->in_cs != ic->out_cs) {
ret = init_csc(ic, ic->in_cs, ic->out_cs, 0);
if (ret)
goto unlock;
}
unlock:
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(ipu_ic_task_init);
int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
u32 width, u32 height, int burst_size,
enum ipu_rotate_mode rot)
{
struct ipu_ic_priv *priv = ic->priv;
struct ipu_soc *ipu = priv->ipu;
u32 ic_idmac_1, ic_idmac_2, ic_idmac_3;
u32 temp_rot = bitrev8(rot) >> 5;
bool need_hor_flip = false;
unsigned long flags;
int ret = 0;
if ((burst_size != 8) && (burst_size != 16)) {
dev_err(ipu->dev, "Illegal burst length for IC\n");
return -EINVAL;
}
width--;
height--;
if (temp_rot & 0x2) /* Need horizontal flip */
need_hor_flip = true;
spin_lock_irqsave(&priv->lock, flags);
ic_idmac_1 = ipu_ic_read(ic, IC_IDMAC_1);
ic_idmac_2 = ipu_ic_read(ic, IC_IDMAC_2);
ic_idmac_3 = ipu_ic_read(ic, IC_IDMAC_3);
switch (channel->num) {
case IPUV3_CHANNEL_IC_PP_MEM:
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16;
if (need_hor_flip)
ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS;
else
ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS;
ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK;
ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET;
ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK;
ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET;
break;
case IPUV3_CHANNEL_MEM_IC_PP:
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16;
break;
case IPUV3_CHANNEL_MEM_ROT_PP:
ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK;
ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET;
break;
case IPUV3_CHANNEL_MEM_IC_PRP_VF:
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16;
break;
case IPUV3_CHANNEL_IC_PRP_ENC_MEM:
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16;
if (need_hor_flip)
ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS;
else
ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS;
ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK;
ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET;
ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK;
ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET;
break;
case IPUV3_CHANNEL_MEM_ROT_ENC:
ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK;
ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET;
break;
case IPUV3_CHANNEL_IC_PRP_VF_MEM:
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16;
if (need_hor_flip)
ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS;
else
ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS;
ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK;
ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET;
ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK;
ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET;
break;
case IPUV3_CHANNEL_MEM_ROT_VF:
ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK;
ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET;
break;
case IPUV3_CHANNEL_G_MEM_IC_PRP_VF:
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16;
break;
case IPUV3_CHANNEL_G_MEM_IC_PP:
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16;
break;
case IPUV3_CHANNEL_VDI_MEM_IC_VF:
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB7_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB7_BURST_16;
break;
default:
goto unlock;
}
ipu_ic_write(ic, ic_idmac_1, IC_IDMAC_1);
ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2);
ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3);
if (rot >= IPU_ROTATE_90_RIGHT)
ic->rotation = true;
unlock:
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init);
int ipu_ic_enable(struct ipu_ic *ic)
{
struct ipu_ic_priv *priv = ic->priv;
unsigned long flags;
u32 module = IPU_CONF_IC_EN;
spin_lock_irqsave(&priv->lock, flags);
if (ic->rotation)
module |= IPU_CONF_ROT_EN;
if (!priv->use_count)
ipu_module_enable(priv->ipu, module);
priv->use_count++;
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_ic_enable);
int ipu_ic_disable(struct ipu_ic *ic)
{
struct ipu_ic_priv *priv = ic->priv;
unsigned long flags;
u32 module = IPU_CONF_IC_EN | IPU_CONF_ROT_EN;
spin_lock_irqsave(&priv->lock, flags);
priv->use_count--;
if (!priv->use_count)
ipu_module_disable(priv->ipu, module);
if (priv->use_count < 0)
priv->use_count = 0;
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_ic_disable);
struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task)
{
struct ipu_ic_priv *priv = ipu->ic_priv;
unsigned long flags;
struct ipu_ic *ic, *ret;
if (task >= IC_NUM_TASKS)
return ERR_PTR(-EINVAL);
ic = &priv->task[task];
spin_lock_irqsave(&priv->lock, flags);
if (ic->in_use) {
ret = ERR_PTR(-EBUSY);
goto unlock;
}
ic->in_use = true;
ret = ic;
unlock:
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(ipu_ic_get);
void ipu_ic_put(struct ipu_ic *ic)
{
struct ipu_ic_priv *priv = ic->priv;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
ic->in_use = false;
spin_unlock_irqrestore(&priv->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_ic_put);
int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
unsigned long base, unsigned long tpmem_base)
{
struct ipu_ic_priv *priv;
int i;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
ipu->ic_priv = priv;
spin_lock_init(&priv->lock);
priv->base = devm_ioremap(dev, base, PAGE_SIZE);
if (!priv->base)
return -ENOMEM;
priv->tpmem_base = devm_ioremap(dev, tpmem_base, SZ_64K);
if (!priv->tpmem_base)
return -ENOMEM;
dev_dbg(dev, "IC base: 0x%08lx remapped to %p\n", base, priv->base);
priv->ipu = ipu;
for (i = 0; i < IC_NUM_TASKS; i++) {
priv->task[i].task = i;
priv->task[i].priv = priv;
priv->task[i].reg = &ic_task_reg[i];
priv->task[i].bit = &ic_task_bit[i];
}
return 0;
}
void ipu_ic_exit(struct ipu_soc *ipu)
{
}
void ipu_ic_dump(struct ipu_ic *ic)
{
struct ipu_ic_priv *priv = ic->priv;
struct ipu_soc *ipu = priv->ipu;
dev_dbg(ipu->dev, "IC_CONF = \t0x%08X\n",
ipu_ic_read(ic, IC_CONF));
dev_dbg(ipu->dev, "IC_PRP_ENC_RSC = \t0x%08X\n",
ipu_ic_read(ic, IC_PRP_ENC_RSC));
dev_dbg(ipu->dev, "IC_PRP_VF_RSC = \t0x%08X\n",
ipu_ic_read(ic, IC_PRP_VF_RSC));
dev_dbg(ipu->dev, "IC_PP_RSC = \t0x%08X\n",
ipu_ic_read(ic, IC_PP_RSC));
dev_dbg(ipu->dev, "IC_CMBP_1 = \t0x%08X\n",
ipu_ic_read(ic, IC_CMBP_1));
dev_dbg(ipu->dev, "IC_CMBP_2 = \t0x%08X\n",
ipu_ic_read(ic, IC_CMBP_2));
dev_dbg(ipu->dev, "IC_IDMAC_1 = \t0x%08X\n",
ipu_ic_read(ic, IC_IDMAC_1));
dev_dbg(ipu->dev, "IC_IDMAC_2 = \t0x%08X\n",
ipu_ic_read(ic, IC_IDMAC_2));
dev_dbg(ipu->dev, "IC_IDMAC_3 = \t0x%08X\n",
ipu_ic_read(ic, IC_IDMAC_3));
dev_dbg(ipu->dev, "IC_IDMAC_4 = \t0x%08X\n",
ipu_ic_read(ic, IC_IDMAC_4));
}
EXPORT_SYMBOL_GPL(ipu_ic_dump);
......@@ -24,23 +24,6 @@ struct ipu_soc;
#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
......@@ -85,6 +68,7 @@ struct ipu_soc;
#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_CHA_BUF2_RDY(ch) IPU_CM_REG(0x0288 + 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))
......@@ -148,9 +132,12 @@ struct ipuv3_channel {
struct ipu_soc *ipu;
};
struct ipu_cpmem;
struct ipu_csi;
struct ipu_dc_priv;
struct ipu_dmfc_priv;
struct ipu_di;
struct ipu_ic_priv;
struct ipu_smfc_priv;
struct ipu_devtype;
......@@ -164,7 +151,6 @@ struct ipu_soc {
void __iomem *cm_reg;
void __iomem *idmac_reg;
struct ipu_ch_param __iomem *cpmem_base;
int usecount;
......@@ -176,13 +162,27 @@ struct ipu_soc {
int irq_err;
struct irq_domain *domain;
struct ipu_cpmem *cpmem_priv;
struct ipu_dc_priv *dc_priv;
struct ipu_dp_priv *dp_priv;
struct ipu_dmfc_priv *dmfc_priv;
struct ipu_di *di_priv[2];
struct ipu_csi *csi_priv[2];
struct ipu_ic_priv *ic_priv;
struct ipu_smfc_priv *smfc_priv;
};
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);
int ipu_module_enable(struct ipu_soc *ipu, u32 mask);
......@@ -191,6 +191,14 @@ 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_csi_init(struct ipu_soc *ipu, struct device *dev, int id,
unsigned long base, u32 module, struct clk *clk_ipu);
void ipu_csi_exit(struct ipu_soc *ipu, int id);
int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
unsigned long base, unsigned long tpmem_base);
void ipu_ic_exit(struct ipu_soc *ipu);
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);
......
......@@ -21,9 +21,18 @@
#include "ipu-prv.h"
struct ipu_smfc {
struct ipu_smfc_priv *priv;
int chno;
bool inuse;
};
struct ipu_smfc_priv {
void __iomem *base;
spinlock_t lock;
struct ipu_soc *ipu;
struct ipu_smfc channel[4];
int use_count;
};
/*SMFC Registers */
......@@ -31,63 +40,166 @@ struct ipu_smfc_priv {
#define SMFC_WMC 0x0004
#define SMFC_BS 0x0008
int ipu_smfc_set_burstsize(struct ipu_soc *ipu, int channel, int burstsize)
int ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize)
{
struct ipu_smfc_priv *smfc = ipu->smfc_priv;
struct ipu_smfc_priv *priv = smfc->priv;
unsigned long flags;
u32 val, shift;
spin_lock_irqsave(&smfc->lock, flags);
spin_lock_irqsave(&priv->lock, flags);
shift = channel * 4;
val = readl(smfc->base + SMFC_BS);
shift = smfc->chno * 4;
val = readl(priv->base + SMFC_BS);
val &= ~(0xf << shift);
val |= burstsize << shift;
writel(val, smfc->base + SMFC_BS);
writel(val, priv->base + SMFC_BS);
spin_unlock_irqrestore(&smfc->lock, flags);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize);
int ipu_smfc_map_channel(struct ipu_soc *ipu, int channel, int csi_id, int mipi_id)
int ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id)
{
struct ipu_smfc_priv *smfc = ipu->smfc_priv;
struct ipu_smfc_priv *priv = smfc->priv;
unsigned long flags;
u32 val, shift;
spin_lock_irqsave(&smfc->lock, flags);
spin_lock_irqsave(&priv->lock, flags);
shift = channel * 3;
val = readl(smfc->base + SMFC_MAP);
shift = smfc->chno * 3;
val = readl(priv->base + SMFC_MAP);
val &= ~(0x7 << shift);
val |= ((csi_id << 2) | mipi_id) << shift;
writel(val, smfc->base + SMFC_MAP);
writel(val, priv->base + SMFC_MAP);
spin_unlock_irqrestore(&smfc->lock, flags);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_smfc_map_channel);
int ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level)
{
struct ipu_smfc_priv *priv = smfc->priv;
unsigned long flags;
u32 val, shift;
spin_lock_irqsave(&priv->lock, flags);
shift = smfc->chno * 6 + (smfc->chno > 1 ? 4 : 0);
val = readl(priv->base + SMFC_WMC);
val &= ~(0x3f << shift);
val |= ((clr_level << 3) | set_level) << shift;
writel(val, priv->base + SMFC_WMC);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_smfc_set_watermark);
int ipu_smfc_enable(struct ipu_smfc *smfc)
{
struct ipu_smfc_priv *priv = smfc->priv;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
if (!priv->use_count)
ipu_module_enable(priv->ipu, IPU_CONF_SMFC_EN);
priv->use_count++;
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_smfc_enable);
int ipu_smfc_disable(struct ipu_smfc *smfc)
{
struct ipu_smfc_priv *priv = smfc->priv;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
priv->use_count--;
if (!priv->use_count)
ipu_module_disable(priv->ipu, IPU_CONF_SMFC_EN);
if (priv->use_count < 0)
priv->use_count = 0;
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_smfc_disable);
struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno)
{
struct ipu_smfc_priv *priv = ipu->smfc_priv;
struct ipu_smfc *smfc, *ret;
unsigned long flags;
if (chno >= 4)
return ERR_PTR(-EINVAL);
smfc = &priv->channel[chno];
ret = smfc;
spin_lock_irqsave(&priv->lock, flags);
if (smfc->inuse) {
ret = ERR_PTR(-EBUSY);
goto unlock;
}
smfc->inuse = true;
unlock:
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(ipu_smfc_get);
void ipu_smfc_put(struct ipu_smfc *smfc)
{
struct ipu_smfc_priv *priv = smfc->priv;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
smfc->inuse = false;
spin_unlock_irqrestore(&priv->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_smfc_put);
int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev,
unsigned long base)
{
struct ipu_smfc_priv *smfc;
struct ipu_smfc_priv *priv;
int i;
smfc = devm_kzalloc(dev, sizeof(*smfc), GFP_KERNEL);
if (!smfc)
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
ipu->smfc_priv = smfc;
spin_lock_init(&smfc->lock);
ipu->smfc_priv = priv;
spin_lock_init(&priv->lock);
priv->ipu = ipu;
smfc->base = devm_ioremap(dev, base, PAGE_SIZE);
if (!smfc->base)
priv->base = devm_ioremap(dev, base, PAGE_SIZE);
if (!priv->base)
return -ENOMEM;
pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, smfc->base);
for (i = 0; i < 4; i++) {
priv->channel[i].priv = priv;
priv->channel[i].chno = i;
}
pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, priv->base);
return 0;
}
......
......@@ -62,7 +62,6 @@ static inline int calc_bandwidth(int width, int height, unsigned int vref)
int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
int x, int y)
{
struct ipu_ch_param __iomem *cpmem;
struct drm_gem_cma_object *cma_obj;
unsigned long eba;
......@@ -75,13 +74,12 @@ int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d",
&cma_obj->paddr, x, y);
cpmem = ipu_get_cpmem(ipu_plane->ipu_ch);
ipu_cpmem_set_stride(cpmem, fb->pitches[0]);
ipu_cpmem_set_stride(ipu_plane->ipu_ch, fb->pitches[0]);
eba = cma_obj->paddr + fb->offsets[0] +
fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x;
ipu_cpmem_set_buffer(cpmem, 0, eba);
ipu_cpmem_set_buffer(cpmem, 1, eba);
ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba);
ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba);
/* cache offsets for subsequent pageflips */
ipu_plane->x = x;
......@@ -97,7 +95,6 @@ int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
{
struct ipu_ch_param __iomem *cpmem;
struct device *dev = ipu_plane->base.dev->dev;
int ret;
......@@ -175,10 +172,9 @@ int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
return ret;
}
cpmem = ipu_get_cpmem(ipu_plane->ipu_ch);
ipu_ch_param_zero(cpmem);
ipu_cpmem_set_resolution(cpmem, src_w, src_h);
ret = ipu_cpmem_set_fmt(cpmem, fb->pixel_format);
ipu_cpmem_zero(ipu_plane->ipu_ch);
ipu_cpmem_set_resolution(ipu_plane->ipu_ch, src_w, src_h);
ret = ipu_cpmem_set_fmt(ipu_plane->ipu_ch, fb->pixel_format);
if (ret < 0) {
dev_err(dev, "unsupported pixel format 0x%08x\n",
fb->pixel_format);
......
......@@ -16,6 +16,7 @@
#include <linux/videodev2.h>
#include <linux/bitmap.h>
#include <linux/fb.h>
#include <media/v4l2-mediabus.h>
struct ipu_soc;
......@@ -61,6 +62,29 @@ struct ipu_di_signal_cfg {
u8 vsync_pin;
};
/*
* Enumeration of CSI destinations
*/
enum ipu_csi_dest {
IPU_CSI_DEST_IDMAC, /* to memory via SMFC */
IPU_CSI_DEST_IC, /* to Image Converter */
IPU_CSI_DEST_VDIC, /* to VDIC */
};
/*
* Enumeration of IPU rotation modes
*/
enum ipu_rotate_mode {
IPU_ROTATE_NONE = 0,
IPU_ROTATE_VERT_FLIP,
IPU_ROTATE_HORIZ_FLIP,
IPU_ROTATE_180,
IPU_ROTATE_90_RIGHT,
IPU_ROTATE_90_RIGHT_VFLIP,
IPU_ROTATE_90_RIGHT_HFLIP,
IPU_ROTATE_90_LEFT,
};
enum ipu_color_space {
IPUV3_COLORSPACE_RGB,
IPUV3_COLORSPACE_YUV,
......@@ -76,6 +100,36 @@ enum ipu_channel_irq {
IPU_IRQ_EOS = 192,
};
/*
* Enumeration of IDMAC channels
*/
#define IPUV3_CHANNEL_CSI0 0
#define IPUV3_CHANNEL_CSI1 1
#define IPUV3_CHANNEL_CSI2 2
#define IPUV3_CHANNEL_CSI3 3
#define IPUV3_CHANNEL_VDI_MEM_IC_VF 5
#define IPUV3_CHANNEL_MEM_IC_PP 11
#define IPUV3_CHANNEL_MEM_IC_PRP_VF 12
#define IPUV3_CHANNEL_G_MEM_IC_PRP_VF 14
#define IPUV3_CHANNEL_G_MEM_IC_PP 15
#define IPUV3_CHANNEL_IC_PRP_ENC_MEM 20
#define IPUV3_CHANNEL_IC_PRP_VF_MEM 21
#define IPUV3_CHANNEL_IC_PP_MEM 22
#define IPUV3_CHANNEL_MEM_BG_SYNC 23
#define IPUV3_CHANNEL_MEM_BG_ASYNC 24
#define IPUV3_CHANNEL_MEM_FG_SYNC 27
#define IPUV3_CHANNEL_MEM_DC_SYNC 28
#define IPUV3_CHANNEL_MEM_FG_ASYNC 29
#define IPUV3_CHANNEL_MEM_FG_SYNC_ALPHA 31
#define IPUV3_CHANNEL_MEM_DC_ASYNC 41
#define IPUV3_CHANNEL_MEM_ROT_ENC 45
#define IPUV3_CHANNEL_MEM_ROT_VF 46
#define IPUV3_CHANNEL_MEM_ROT_PP 47
#define IPUV3_CHANNEL_ROT_ENC_MEM 48
#define IPUV3_CHANNEL_ROT_VF_MEM 49
#define IPUV3_CHANNEL_ROT_PP_MEM 50
#define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA 51
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);
......@@ -92,6 +146,13 @@ int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
#define IPU_IRQ_VSYNC_PRE_0 (448 + 14)
#define IPU_IRQ_VSYNC_PRE_1 (448 + 15)
/*
* IPU Common functions
*/
void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2);
void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi);
void ipu_dump(struct ipu_soc *ipu);
/*
* IPU Image DMA Controller (idmac) functions
*/
......@@ -100,12 +161,58 @@ 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);
void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable);
int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts);
int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms);
void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
bool doublebuffer);
int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel);
bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num);
void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num);
void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num);
/*
* IPU Channel Parameter Memory (cpmem) functions
*/
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 phys0;
dma_addr_t phys1;
};
void ipu_cpmem_zero(struct ipuv3_channel *ch);
void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres);
void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride);
void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch);
void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf);
void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride);
void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id);
void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize);
void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch);
void ipu_cpmem_set_rotation(struct ipuv3_channel *ch,
enum ipu_rotate_mode rot);
int ipu_cpmem_set_format_rgb(struct ipuv3_channel *ch,
const struct ipu_rgb *rgb);
int ipu_cpmem_set_format_passthrough(struct ipuv3_channel *ch, int width);
void ipu_cpmem_set_yuv_interleaved(struct ipuv3_channel *ch, u32 pixel_format);
void ipu_cpmem_set_yuv_planar_full(struct ipuv3_channel *ch,
u32 pixel_format, int stride,
int u_offset, int v_offset);
void ipu_cpmem_set_yuv_planar(struct ipuv3_channel *ch,
u32 pixel_format, int stride, int height);
int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc);
int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image);
void ipu_cpmem_dump(struct ipuv3_channel *ch);
/*
* IPU Display Controller (dc) functions
......@@ -169,171 +276,78 @@ int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha,
/*
* IPU CMOS Sensor Interface (csi) functions
*/
int ipu_csi_enable(struct ipu_soc *ipu, int csi);
int ipu_csi_disable(struct ipu_soc *ipu, int csi);
struct ipu_csi;
int ipu_csi_init_interface(struct ipu_csi *csi,
struct v4l2_mbus_config *mbus_cfg,
struct v4l2_mbus_framefmt *mbus_fmt);
bool ipu_csi_is_interlaced(struct ipu_csi *csi);
void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w);
void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w);
void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active,
u32 r_value, u32 g_value, u32 b_value,
u32 pix_clk);
int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc,
struct v4l2_mbus_framefmt *mbus_fmt);
int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip,
u32 max_ratio, u32 id);
int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest);
int ipu_csi_enable(struct ipu_csi *csi);
int ipu_csi_disable(struct ipu_csi *csi);
struct ipu_csi *ipu_csi_get(struct ipu_soc *ipu, int id);
void ipu_csi_put(struct ipu_csi *csi);
void ipu_csi_dump(struct ipu_csi *csi);
/*
* IPU Sensor Multiple FIFO Controller (SMFC) functions
* IPU Image Converter (ic) functions
*/
int ipu_smfc_enable(struct ipu_soc *ipu);
int ipu_smfc_disable(struct ipu_soc *ipu);
int ipu_smfc_map_channel(struct ipu_soc *ipu, int channel, int csi_id, int mipi_id);
int ipu_smfc_set_burstsize(struct ipu_soc *ipu, int channel, int burstsize);
#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;
enum ipu_ic_task {
IC_TASK_ENCODER,
IC_TASK_VIEWFINDER,
IC_TASK_POST_PROCESSOR,
IC_NUM_TASKS,
};
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);
};
struct ipu_ic;
int ipu_ic_task_init(struct ipu_ic *ic,
int in_width, int in_height,
int out_width, int out_height,
enum ipu_color_space in_cs,
enum ipu_color_space out_cs);
int ipu_ic_task_graphics_init(struct ipu_ic *ic,
enum ipu_color_space in_g_cs,
bool galpha_en, u32 galpha,
bool colorkey_en, u32 colorkey);
void ipu_ic_task_enable(struct ipu_ic *ic);
void ipu_ic_task_disable(struct ipu_ic *ic);
int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
u32 width, u32 height, int burst_size,
enum ipu_rotate_mode rot);
int ipu_ic_enable(struct ipu_ic *ic);
int ipu_ic_disable(struct ipu_ic *ic);
struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task);
void ipu_ic_put(struct ipu_ic *ic);
void ipu_ic_dump(struct ipu_ic *ic);
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);
/*
* IPU Sensor Multiple FIFO Controller (SMFC) functions
*/
struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno);
void ipu_smfc_put(struct ipu_smfc *smfc);
int ipu_smfc_enable(struct ipu_smfc *smfc);
int ipu_smfc_disable(struct ipu_smfc *smfc);
int ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id);
int ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize);
int ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level);
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);
};
enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code);
int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat);
bool ipu_pixelformat_is_planar(u32 pixelformat);
int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
bool hflip, bool vflip);
int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
bool hflip, bool vflip);
struct ipu_client_platformdata {
int csi;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment