diff options
Diffstat (limited to 'drivers/gpu/ipu-v3/ipu-vdi.c')
-rw-r--r-- | drivers/gpu/ipu-v3/ipu-vdi.c | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/drivers/gpu/ipu-v3/ipu-vdi.c b/drivers/gpu/ipu-v3/ipu-vdi.c new file mode 100644 index 000000000..a593b232b --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-vdi.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2016 Mentor Graphics Inc. + * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. + */ +#include <linux/io.h> +#include "ipu-prv.h" + +struct ipu_vdi { + void __iomem *base; + u32 module; + spinlock_t lock; + int use_count; + struct ipu_soc *ipu; +}; + + +/* VDI Register Offsets */ +#define VDI_FSIZE 0x0000 +#define VDI_C 0x0004 + +/* VDI Register Fields */ +#define VDI_C_CH_420 (0 << 1) +#define VDI_C_CH_422 (1 << 1) +#define VDI_C_MOT_SEL_MASK (0x3 << 2) +#define VDI_C_MOT_SEL_FULL (2 << 2) +#define VDI_C_MOT_SEL_LOW (1 << 2) +#define VDI_C_MOT_SEL_MED (0 << 2) +#define VDI_C_BURST_SIZE1_4 (3 << 4) +#define VDI_C_BURST_SIZE2_4 (3 << 8) +#define VDI_C_BURST_SIZE3_4 (3 << 12) +#define VDI_C_BURST_SIZE_MASK 0xF +#define VDI_C_BURST_SIZE1_OFFSET 4 +#define VDI_C_BURST_SIZE2_OFFSET 8 +#define VDI_C_BURST_SIZE3_OFFSET 12 +#define VDI_C_VWM1_SET_1 (0 << 16) +#define VDI_C_VWM1_SET_2 (1 << 16) +#define VDI_C_VWM1_CLR_2 (1 << 19) +#define VDI_C_VWM3_SET_1 (0 << 22) +#define VDI_C_VWM3_SET_2 (1 << 22) +#define VDI_C_VWM3_CLR_2 (1 << 25) +#define VDI_C_TOP_FIELD_MAN_1 (1 << 30) +#define VDI_C_TOP_FIELD_AUTO_1 (1 << 31) + +static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset) +{ + return readl(vdi->base + offset); +} + +static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value, + unsigned int offset) +{ + writel(value, vdi->base + offset); +} + +void ipu_vdi_set_field_order(struct ipu_vdi *vdi, v4l2_std_id std, u32 field) +{ + bool top_field_0 = false; + unsigned long flags; + u32 reg; + + switch (field) { + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_TOP: + top_field_0 = true; + break; + case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_SEQ_BT: + case V4L2_FIELD_BOTTOM: + top_field_0 = false; + break; + default: + top_field_0 = (std & V4L2_STD_525_60) ? true : false; + break; + } + + spin_lock_irqsave(&vdi->lock, flags); + + reg = ipu_vdi_read(vdi, VDI_C); + if (top_field_0) + reg &= ~(VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1); + else + reg |= VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1; + ipu_vdi_write(vdi, reg, VDI_C); + + spin_unlock_irqrestore(&vdi->lock, flags); +} +EXPORT_SYMBOL_GPL(ipu_vdi_set_field_order); + +void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel) +{ + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&vdi->lock, flags); + + reg = ipu_vdi_read(vdi, VDI_C); + + reg &= ~VDI_C_MOT_SEL_MASK; + + switch (motion_sel) { + case MED_MOTION: + reg |= VDI_C_MOT_SEL_MED; + break; + case HIGH_MOTION: + reg |= VDI_C_MOT_SEL_FULL; + break; + default: + reg |= VDI_C_MOT_SEL_LOW; + break; + } + + ipu_vdi_write(vdi, reg, VDI_C); + + spin_unlock_irqrestore(&vdi->lock, flags); +} +EXPORT_SYMBOL_GPL(ipu_vdi_set_motion); + +void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres) +{ + unsigned long flags; + u32 pixel_fmt, reg; + + spin_lock_irqsave(&vdi->lock, flags); + + reg = ((yres - 1) << 16) | (xres - 1); + ipu_vdi_write(vdi, reg, VDI_FSIZE); + + /* + * Full motion, only vertical filter is used. + * Burst size is 4 accesses + */ + if (code == MEDIA_BUS_FMT_UYVY8_2X8 || + code == MEDIA_BUS_FMT_UYVY8_1X16 || + code == MEDIA_BUS_FMT_YUYV8_2X8 || + code == MEDIA_BUS_FMT_YUYV8_1X16) + pixel_fmt = VDI_C_CH_422; + else + pixel_fmt = VDI_C_CH_420; + + reg = ipu_vdi_read(vdi, VDI_C); + reg |= pixel_fmt; + reg |= VDI_C_BURST_SIZE2_4; + reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2; + reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2; + ipu_vdi_write(vdi, reg, VDI_C); + + spin_unlock_irqrestore(&vdi->lock, flags); +} +EXPORT_SYMBOL_GPL(ipu_vdi_setup); + +void ipu_vdi_unsetup(struct ipu_vdi *vdi) +{ + unsigned long flags; + + spin_lock_irqsave(&vdi->lock, flags); + ipu_vdi_write(vdi, 0, VDI_FSIZE); + ipu_vdi_write(vdi, 0, VDI_C); + spin_unlock_irqrestore(&vdi->lock, flags); +} +EXPORT_SYMBOL_GPL(ipu_vdi_unsetup); + +int ipu_vdi_enable(struct ipu_vdi *vdi) +{ + unsigned long flags; + + spin_lock_irqsave(&vdi->lock, flags); + + if (!vdi->use_count) + ipu_module_enable(vdi->ipu, vdi->module); + + vdi->use_count++; + + spin_unlock_irqrestore(&vdi->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_vdi_enable); + +int ipu_vdi_disable(struct ipu_vdi *vdi) +{ + unsigned long flags; + + spin_lock_irqsave(&vdi->lock, flags); + + if (vdi->use_count) { + if (!--vdi->use_count) + ipu_module_disable(vdi->ipu, vdi->module); + } + + spin_unlock_irqrestore(&vdi->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_vdi_disable); + +struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu) +{ + return ipu->vdi_priv; +} +EXPORT_SYMBOL_GPL(ipu_vdi_get); + +void ipu_vdi_put(struct ipu_vdi *vdi) +{ +} +EXPORT_SYMBOL_GPL(ipu_vdi_put); + +int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, + unsigned long base, u32 module) +{ + struct ipu_vdi *vdi; + + vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL); + if (!vdi) + return -ENOMEM; + + ipu->vdi_priv = vdi; + + spin_lock_init(&vdi->lock); + vdi->module = module; + vdi->base = devm_ioremap(dev, base, PAGE_SIZE); + if (!vdi->base) + return -ENOMEM; + + dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base); + vdi->ipu = ipu; + + return 0; +} + +void ipu_vdi_exit(struct ipu_soc *ipu) +{ +} |