diff options
Diffstat (limited to 'drivers/gpu/drm/meson')
33 files changed, 12066 insertions, 0 deletions
diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig new file mode 100644 index 0000000000..615fdd0ce4 --- /dev/null +++ b/drivers/gpu/drm/meson/Kconfig @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DRM_MESON + tristate "DRM Support for Amlogic Meson Display Controller" + depends on DRM && OF && (ARM || ARM64) + depends on ARCH_MESON || COMPILE_TEST + select DRM_KMS_HELPER + select DRM_GEM_DMA_HELPER + select DRM_DISPLAY_CONNECTOR + select VIDEOMODE_HELPERS + select REGMAP_MMIO + select MESON_CANVAS + select CEC_CORE if CEC_NOTIFIER + +config DRM_MESON_DW_HDMI + tristate "HDMI Synopsys Controller support for Amlogic Meson Display" + depends on DRM_MESON + default y if DRM_MESON + select DRM_DW_HDMI + imply DRM_DW_HDMI_I2S_AUDIO + +config DRM_MESON_DW_MIPI_DSI + tristate "MIPI DSI Synopsys Controller support for Amlogic Meson Display" + depends on DRM_MESON + default y if DRM_MESON + select DRM_DW_MIPI_DSI + select GENERIC_PHY_MIPI_DPHY diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile new file mode 100644 index 0000000000..43071bdbd4 --- /dev/null +++ b/drivers/gpu/drm/meson/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +meson-drm-y := meson_drv.o meson_plane.o meson_crtc.o meson_encoder_cvbs.o +meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_overlay.o +meson-drm-y += meson_rdma.o meson_osd_afbcd.o +meson-drm-y += meson_encoder_hdmi.o meson_encoder_dsi.o + +obj-$(CONFIG_DRM_MESON) += meson-drm.o +obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o +obj-$(CONFIG_DRM_MESON_DW_MIPI_DSI) += meson_dw_mipi_dsi.o diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c new file mode 100644 index 0000000000..d70616da8c --- /dev/null +++ b/drivers/gpu/drm/meson/meson_crtc.c @@ -0,0 +1,719 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#include <linux/bitfield.h> +#include <linux/soc/amlogic/meson-canvas.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_device.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> + +#include "meson_crtc.h" +#include "meson_plane.h" +#include "meson_registers.h" +#include "meson_venc.h" +#include "meson_viu.h" +#include "meson_rdma.h" +#include "meson_vpp.h" +#include "meson_osd_afbcd.h" + +#define MESON_G12A_VIU_OFFSET 0x5ec0 + +/* CRTC definition */ + +struct meson_crtc { + struct drm_crtc base; + struct drm_pending_vblank_event *event; + struct meson_drm *priv; + void (*enable_osd1)(struct meson_drm *priv); + void (*enable_vd1)(struct meson_drm *priv); + void (*enable_osd1_afbc)(struct meson_drm *priv); + void (*disable_osd1_afbc)(struct meson_drm *priv); + unsigned int viu_offset; + bool vsync_forced; + bool vsync_disabled; +}; +#define to_meson_crtc(x) container_of(x, struct meson_crtc, base) + +/* CRTC */ + +static int meson_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + struct meson_drm *priv = meson_crtc->priv; + + meson_crtc->vsync_disabled = false; + meson_venc_enable_vsync(priv); + + return 0; +} + +static void meson_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + struct meson_drm *priv = meson_crtc->priv; + + if (!meson_crtc->vsync_forced) { + meson_crtc->vsync_disabled = true; + meson_venc_disable_vsync(priv); + } +} + +static const struct drm_crtc_funcs meson_crtc_funcs = { + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .destroy = drm_crtc_cleanup, + .page_flip = drm_atomic_helper_page_flip, + .reset = drm_atomic_helper_crtc_reset, + .set_config = drm_atomic_helper_set_config, + .enable_vblank = meson_crtc_enable_vblank, + .disable_vblank = meson_crtc_disable_vblank, + +}; + +static void meson_g12a_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + struct drm_crtc_state *crtc_state = crtc->state; + struct meson_drm *priv = meson_crtc->priv; + + DRM_DEBUG_DRIVER("\n"); + + if (!crtc_state) { + DRM_ERROR("Invalid crtc_state\n"); + return; + } + + /* VD1 Preblend vertical start/end */ + writel(FIELD_PREP(GENMASK(11, 0), 2303), + priv->io_base + _REG(VPP_PREBLEND_VD1_V_START_END)); + + /* Setup Blender */ + writel(crtc_state->mode.hdisplay | + crtc_state->mode.vdisplay << 16, + priv->io_base + _REG(VPP_POSTBLEND_H_SIZE)); + + writel_relaxed(0 << 16 | + (crtc_state->mode.hdisplay - 1), + priv->io_base + _REG(VPP_OSD1_BLD_H_SCOPE)); + writel_relaxed(0 << 16 | + (crtc_state->mode.vdisplay - 1), + priv->io_base + _REG(VPP_OSD1_BLD_V_SCOPE)); + writel_relaxed(crtc_state->mode.hdisplay << 16 | + crtc_state->mode.vdisplay, + priv->io_base + _REG(VPP_OUT_H_V_SIZE)); + + drm_crtc_vblank_on(crtc); +} + +static void meson_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + struct drm_crtc_state *crtc_state = crtc->state; + struct meson_drm *priv = meson_crtc->priv; + + DRM_DEBUG_DRIVER("\n"); + + if (!crtc_state) { + DRM_ERROR("Invalid crtc_state\n"); + return; + } + + /* Enable VPP Postblend */ + writel(crtc_state->mode.hdisplay, + priv->io_base + _REG(VPP_POSTBLEND_H_SIZE)); + + /* VD1 Preblend vertical start/end */ + writel(FIELD_PREP(GENMASK(11, 0), 2303), + priv->io_base + _REG(VPP_PREBLEND_VD1_V_START_END)); + + writel_bits_relaxed(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE, + priv->io_base + _REG(VPP_MISC)); + + drm_crtc_vblank_on(crtc); +} + +static void meson_g12a_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + struct meson_drm *priv = meson_crtc->priv; + + DRM_DEBUG_DRIVER("\n"); + + drm_crtc_vblank_off(crtc); + + priv->viu.osd1_enabled = false; + priv->viu.osd1_commit = false; + + priv->viu.vd1_enabled = false; + priv->viu.vd1_commit = false; + + if (crtc->state->event && !crtc->state->active) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + + crtc->state->event = NULL; + } +} + +static void meson_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + struct meson_drm *priv = meson_crtc->priv; + + DRM_DEBUG_DRIVER("\n"); + + drm_crtc_vblank_off(crtc); + + priv->viu.osd1_enabled = false; + priv->viu.osd1_commit = false; + + priv->viu.vd1_enabled = false; + priv->viu.vd1_commit = false; + + /* Disable VPP Postblend */ + writel_bits_relaxed(VPP_OSD1_POSTBLEND | VPP_VD1_POSTBLEND | + VPP_VD1_PREBLEND | VPP_POSTBLEND_ENABLE, 0, + priv->io_base + _REG(VPP_MISC)); + + if (crtc->state->event && !crtc->state->active) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + + crtc->state->event = NULL; + } +} + +static void meson_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + unsigned long flags; + + if (crtc->state->event) { + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + meson_crtc->event = crtc->state->event; + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + crtc->state->event = NULL; + } +} + +static void meson_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + struct meson_drm *priv = meson_crtc->priv; + + priv->viu.osd1_commit = true; + priv->viu.vd1_commit = true; +} + +static const struct drm_crtc_helper_funcs meson_crtc_helper_funcs = { + .atomic_begin = meson_crtc_atomic_begin, + .atomic_flush = meson_crtc_atomic_flush, + .atomic_enable = meson_crtc_atomic_enable, + .atomic_disable = meson_crtc_atomic_disable, +}; + +static const struct drm_crtc_helper_funcs meson_g12a_crtc_helper_funcs = { + .atomic_begin = meson_crtc_atomic_begin, + .atomic_flush = meson_crtc_atomic_flush, + .atomic_enable = meson_g12a_crtc_atomic_enable, + .atomic_disable = meson_g12a_crtc_atomic_disable, +}; + +static void meson_crtc_enable_osd1(struct meson_drm *priv) +{ + writel_bits_relaxed(VPP_OSD1_POSTBLEND, VPP_OSD1_POSTBLEND, + priv->io_base + _REG(VPP_MISC)); +} + +static void meson_crtc_g12a_enable_osd1_afbc(struct meson_drm *priv) +{ + writel_relaxed(priv->viu.osd1_blk2_cfg4, + priv->io_base + _REG(VIU_OSD1_BLK2_CFG_W4)); + + writel_bits_relaxed(OSD_MEM_LINEAR_ADDR, OSD_MEM_LINEAR_ADDR, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT)); + + writel_relaxed(priv->viu.osd1_blk1_cfg4, + priv->io_base + _REG(VIU_OSD1_BLK1_CFG_W4)); + + meson_viu_g12a_enable_osd1_afbc(priv); + + writel_bits_relaxed(OSD_MEM_LINEAR_ADDR, OSD_MEM_LINEAR_ADDR, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT)); + + writel_bits_relaxed(OSD_MALI_SRC_EN, OSD_MALI_SRC_EN, + priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W0)); +} + +static void meson_g12a_crtc_enable_osd1(struct meson_drm *priv) +{ + writel_relaxed(priv->viu.osd_blend_din0_scope_h, + priv->io_base + + _REG(VIU_OSD_BLEND_DIN0_SCOPE_H)); + writel_relaxed(priv->viu.osd_blend_din0_scope_v, + priv->io_base + + _REG(VIU_OSD_BLEND_DIN0_SCOPE_V)); + writel_relaxed(priv->viu.osb_blend0_size, + priv->io_base + + _REG(VIU_OSD_BLEND_BLEND0_SIZE)); + writel_relaxed(priv->viu.osb_blend1_size, + priv->io_base + + _REG(VIU_OSD_BLEND_BLEND1_SIZE)); + writel_bits_relaxed(3 << 8, 3 << 8, + priv->io_base + _REG(OSD1_BLEND_SRC_CTRL)); +} + +static void meson_crtc_enable_vd1(struct meson_drm *priv) +{ + writel_bits_relaxed(VPP_VD1_PREBLEND | VPP_VD1_POSTBLEND | + VPP_COLOR_MNG_ENABLE, + VPP_VD1_PREBLEND | VPP_VD1_POSTBLEND | + VPP_COLOR_MNG_ENABLE, + priv->io_base + _REG(VPP_MISC)); + + writel_bits_relaxed(VIU_CTRL0_AFBC_TO_VD1, + priv->viu.vd1_afbc ? VIU_CTRL0_AFBC_TO_VD1 : 0, + priv->io_base + _REG(VIU_MISC_CTRL0)); +} + +static void meson_g12a_crtc_enable_vd1(struct meson_drm *priv) +{ + writel_relaxed(VD_BLEND_PREBLD_SRC_VD1 | + VD_BLEND_PREBLD_PREMULT_EN | + VD_BLEND_POSTBLD_SRC_VD1 | + VD_BLEND_POSTBLD_PREMULT_EN, + priv->io_base + _REG(VD1_BLEND_SRC_CTRL)); + + writel_relaxed(priv->viu.vd1_afbc ? + (VD1_AXI_SEL_AFBC | AFBC_VD1_SEL) : 0, + priv->io_base + _REG(VD1_AFBCD0_MISC_CTRL)); +} + +void meson_crtc_irq(struct meson_drm *priv) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(priv->crtc); + unsigned long flags; + + /* Update the OSD registers */ + if (priv->viu.osd1_enabled && priv->viu.osd1_commit) { + writel_relaxed(priv->viu.osd1_ctrl_stat, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT)); + writel_relaxed(priv->viu.osd1_ctrl_stat2, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); + writel_relaxed(priv->viu.osd1_blk0_cfg[0], + priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W0)); + writel_relaxed(priv->viu.osd1_blk0_cfg[1], + priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W1)); + writel_relaxed(priv->viu.osd1_blk0_cfg[2], + priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W2)); + writel_relaxed(priv->viu.osd1_blk0_cfg[3], + priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W3)); + writel_relaxed(priv->viu.osd1_blk0_cfg[4], + priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W4)); + + if (priv->viu.osd1_afbcd) { + if (meson_crtc->enable_osd1_afbc) + meson_crtc->enable_osd1_afbc(priv); + } else { + if (meson_crtc->disable_osd1_afbc) + meson_crtc->disable_osd1_afbc(priv); + if (priv->afbcd.ops) { + priv->afbcd.ops->reset(priv); + priv->afbcd.ops->disable(priv); + } + meson_crtc->vsync_forced = false; + } + + writel_relaxed(priv->viu.osd_sc_ctrl0, + priv->io_base + _REG(VPP_OSD_SC_CTRL0)); + writel_relaxed(priv->viu.osd_sc_i_wh_m1, + priv->io_base + _REG(VPP_OSD_SCI_WH_M1)); + writel_relaxed(priv->viu.osd_sc_o_h_start_end, + priv->io_base + _REG(VPP_OSD_SCO_H_START_END)); + writel_relaxed(priv->viu.osd_sc_o_v_start_end, + priv->io_base + _REG(VPP_OSD_SCO_V_START_END)); + writel_relaxed(priv->viu.osd_sc_v_ini_phase, + priv->io_base + _REG(VPP_OSD_VSC_INI_PHASE)); + writel_relaxed(priv->viu.osd_sc_v_phase_step, + priv->io_base + _REG(VPP_OSD_VSC_PHASE_STEP)); + writel_relaxed(priv->viu.osd_sc_h_ini_phase, + priv->io_base + _REG(VPP_OSD_HSC_INI_PHASE)); + writel_relaxed(priv->viu.osd_sc_h_phase_step, + priv->io_base + _REG(VPP_OSD_HSC_PHASE_STEP)); + writel_relaxed(priv->viu.osd_sc_h_ctrl0, + priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); + writel_relaxed(priv->viu.osd_sc_v_ctrl0, + priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); + + if (!priv->viu.osd1_afbcd) + meson_canvas_config(priv->canvas, priv->canvas_id_osd1, + priv->viu.osd1_addr, + priv->viu.osd1_stride, + priv->viu.osd1_height, + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, 0); + + /* Enable OSD1 */ + if (meson_crtc->enable_osd1) + meson_crtc->enable_osd1(priv); + + if (priv->viu.osd1_afbcd) { + priv->afbcd.ops->reset(priv); + priv->afbcd.ops->setup(priv); + priv->afbcd.ops->enable(priv); + meson_crtc->vsync_forced = true; + } + + priv->viu.osd1_commit = false; + } + + /* Update the VD1 registers */ + if (priv->viu.vd1_enabled && priv->viu.vd1_commit) { + + if (priv->viu.vd1_afbc) { + writel_relaxed(priv->viu.vd1_afbc_head_addr, + priv->io_base + + _REG(AFBC_HEAD_BADDR)); + writel_relaxed(priv->viu.vd1_afbc_body_addr, + priv->io_base + + _REG(AFBC_BODY_BADDR)); + writel_relaxed(priv->viu.vd1_afbc_en, + priv->io_base + + _REG(AFBC_ENABLE)); + writel_relaxed(priv->viu.vd1_afbc_mode, + priv->io_base + + _REG(AFBC_MODE)); + writel_relaxed(priv->viu.vd1_afbc_size_in, + priv->io_base + + _REG(AFBC_SIZE_IN)); + writel_relaxed(priv->viu.vd1_afbc_dec_def_color, + priv->io_base + + _REG(AFBC_DEC_DEF_COLOR)); + writel_relaxed(priv->viu.vd1_afbc_conv_ctrl, + priv->io_base + + _REG(AFBC_CONV_CTRL)); + writel_relaxed(priv->viu.vd1_afbc_size_out, + priv->io_base + + _REG(AFBC_SIZE_OUT)); + writel_relaxed(priv->viu.vd1_afbc_vd_cfmt_ctrl, + priv->io_base + + _REG(AFBC_VD_CFMT_CTRL)); + writel_relaxed(priv->viu.vd1_afbc_vd_cfmt_w, + priv->io_base + + _REG(AFBC_VD_CFMT_W)); + writel_relaxed(priv->viu.vd1_afbc_mif_hor_scope, + priv->io_base + + _REG(AFBC_MIF_HOR_SCOPE)); + writel_relaxed(priv->viu.vd1_afbc_mif_ver_scope, + priv->io_base + + _REG(AFBC_MIF_VER_SCOPE)); + writel_relaxed(priv->viu.vd1_afbc_pixel_hor_scope, + priv->io_base+ + _REG(AFBC_PIXEL_HOR_SCOPE)); + writel_relaxed(priv->viu.vd1_afbc_pixel_ver_scope, + priv->io_base + + _REG(AFBC_PIXEL_VER_SCOPE)); + writel_relaxed(priv->viu.vd1_afbc_vd_cfmt_h, + priv->io_base + + _REG(AFBC_VD_CFMT_H)); + } else { + switch (priv->viu.vd1_planes) { + case 3: + meson_canvas_config(priv->canvas, + priv->canvas_id_vd1_2, + priv->viu.vd1_addr2, + priv->viu.vd1_stride2, + priv->viu.vd1_height2, + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + fallthrough; + case 2: + meson_canvas_config(priv->canvas, + priv->canvas_id_vd1_1, + priv->viu.vd1_addr1, + priv->viu.vd1_stride1, + priv->viu.vd1_height1, + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + fallthrough; + case 1: + meson_canvas_config(priv->canvas, + priv->canvas_id_vd1_0, + priv->viu.vd1_addr0, + priv->viu.vd1_stride0, + priv->viu.vd1_height0, + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + } + + writel_relaxed(0, priv->io_base + _REG(AFBC_ENABLE)); + } + + writel_relaxed(priv->viu.vd1_if0_gen_reg, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_GEN_REG)); + writel_relaxed(priv->viu.vd1_if0_gen_reg, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_GEN_REG)); + writel_relaxed(priv->viu.vd1_if0_gen_reg2, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_GEN_REG2)); + writel_relaxed(priv->viu.viu_vd1_fmt_ctrl, + priv->io_base + meson_crtc->viu_offset + + _REG(VIU_VD1_FMT_CTRL)); + writel_relaxed(priv->viu.viu_vd1_fmt_ctrl, + priv->io_base + meson_crtc->viu_offset + + _REG(VIU_VD2_FMT_CTRL)); + writel_relaxed(priv->viu.viu_vd1_fmt_w, + priv->io_base + meson_crtc->viu_offset + + _REG(VIU_VD1_FMT_W)); + writel_relaxed(priv->viu.viu_vd1_fmt_w, + priv->io_base + meson_crtc->viu_offset + + _REG(VIU_VD2_FMT_W)); + writel_relaxed(priv->viu.vd1_if0_canvas0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_CANVAS0)); + writel_relaxed(priv->viu.vd1_if0_canvas0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_CANVAS1)); + writel_relaxed(priv->viu.vd1_if0_canvas0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_CANVAS0)); + writel_relaxed(priv->viu.vd1_if0_canvas0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_CANVAS1)); + writel_relaxed(priv->viu.vd1_if0_luma_x0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_LUMA_X0)); + writel_relaxed(priv->viu.vd1_if0_luma_x0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_LUMA_X1)); + writel_relaxed(priv->viu.vd1_if0_luma_x0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_LUMA_X0)); + writel_relaxed(priv->viu.vd1_if0_luma_x0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_LUMA_X1)); + writel_relaxed(priv->viu.vd1_if0_luma_y0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_LUMA_Y0)); + writel_relaxed(priv->viu.vd1_if0_luma_y0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_LUMA_Y1)); + writel_relaxed(priv->viu.vd1_if0_luma_y0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_LUMA_Y0)); + writel_relaxed(priv->viu.vd1_if0_luma_y0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_LUMA_Y1)); + writel_relaxed(priv->viu.vd1_if0_chroma_x0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_CHROMA_X0)); + writel_relaxed(priv->viu.vd1_if0_chroma_x0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_CHROMA_X1)); + writel_relaxed(priv->viu.vd1_if0_chroma_x0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_CHROMA_X0)); + writel_relaxed(priv->viu.vd1_if0_chroma_x0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_CHROMA_X1)); + writel_relaxed(priv->viu.vd1_if0_chroma_y0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_CHROMA_Y0)); + writel_relaxed(priv->viu.vd1_if0_chroma_y0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_CHROMA_Y1)); + writel_relaxed(priv->viu.vd1_if0_chroma_y0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_CHROMA_Y0)); + writel_relaxed(priv->viu.vd1_if0_chroma_y0, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_CHROMA_Y1)); + writel_relaxed(priv->viu.vd1_if0_repeat_loop, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_RPT_LOOP)); + writel_relaxed(priv->viu.vd1_if0_repeat_loop, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_RPT_LOOP)); + writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_LUMA0_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_LUMA0_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_LUMA1_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_LUMA1_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_CHROMA0_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_CHROMA0_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_CHROMA1_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat, + priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_CHROMA1_RPT_PAT)); + writel_relaxed(0, priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_LUMA_PSEL)); + writel_relaxed(0, priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_CHROMA_PSEL)); + writel_relaxed(0, priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_LUMA_PSEL)); + writel_relaxed(0, priv->io_base + meson_crtc->viu_offset + + _REG(VD2_IF0_CHROMA_PSEL)); + writel_relaxed(priv->viu.vd1_range_map_y, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_RANGE_MAP_Y)); + writel_relaxed(priv->viu.vd1_range_map_cb, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_RANGE_MAP_CB)); + writel_relaxed(priv->viu.vd1_range_map_cr, + priv->io_base + meson_crtc->viu_offset + + _REG(VD1_IF0_RANGE_MAP_CR)); + writel_relaxed(VPP_VSC_BANK_LENGTH(4) | + VPP_HSC_BANK_LENGTH(4) | + VPP_SC_VD_EN_ENABLE | + VPP_SC_TOP_EN_ENABLE | + VPP_SC_HSC_EN_ENABLE | + VPP_SC_VSC_EN_ENABLE, + priv->io_base + _REG(VPP_SC_MISC)); + writel_relaxed(priv->viu.vpp_pic_in_height, + priv->io_base + _REG(VPP_PIC_IN_HEIGHT)); + writel_relaxed(priv->viu.vpp_postblend_vd1_h_start_end, + priv->io_base + _REG(VPP_POSTBLEND_VD1_H_START_END)); + writel_relaxed(priv->viu.vpp_blend_vd2_h_start_end, + priv->io_base + _REG(VPP_BLEND_VD2_H_START_END)); + writel_relaxed(priv->viu.vpp_postblend_vd1_v_start_end, + priv->io_base + _REG(VPP_POSTBLEND_VD1_V_START_END)); + writel_relaxed(priv->viu.vpp_blend_vd2_v_start_end, + priv->io_base + _REG(VPP_BLEND_VD2_V_START_END)); + writel_relaxed(priv->viu.vpp_hsc_region12_startp, + priv->io_base + _REG(VPP_HSC_REGION12_STARTP)); + writel_relaxed(priv->viu.vpp_hsc_region34_startp, + priv->io_base + _REG(VPP_HSC_REGION34_STARTP)); + writel_relaxed(priv->viu.vpp_hsc_region4_endp, + priv->io_base + _REG(VPP_HSC_REGION4_ENDP)); + writel_relaxed(priv->viu.vpp_hsc_start_phase_step, + priv->io_base + _REG(VPP_HSC_START_PHASE_STEP)); + writel_relaxed(priv->viu.vpp_hsc_region1_phase_slope, + priv->io_base + _REG(VPP_HSC_REGION1_PHASE_SLOPE)); + writel_relaxed(priv->viu.vpp_hsc_region3_phase_slope, + priv->io_base + _REG(VPP_HSC_REGION3_PHASE_SLOPE)); + writel_relaxed(priv->viu.vpp_line_in_length, + priv->io_base + _REG(VPP_LINE_IN_LENGTH)); + writel_relaxed(priv->viu.vpp_preblend_h_size, + priv->io_base + _REG(VPP_PREBLEND_H_SIZE)); + writel_relaxed(priv->viu.vpp_vsc_region12_startp, + priv->io_base + _REG(VPP_VSC_REGION12_STARTP)); + writel_relaxed(priv->viu.vpp_vsc_region34_startp, + priv->io_base + _REG(VPP_VSC_REGION34_STARTP)); + writel_relaxed(priv->viu.vpp_vsc_region4_endp, + priv->io_base + _REG(VPP_VSC_REGION4_ENDP)); + writel_relaxed(priv->viu.vpp_vsc_start_phase_step, + priv->io_base + _REG(VPP_VSC_START_PHASE_STEP)); + writel_relaxed(priv->viu.vpp_vsc_ini_phase, + priv->io_base + _REG(VPP_VSC_INI_PHASE)); + writel_relaxed(priv->viu.vpp_vsc_phase_ctrl, + priv->io_base + _REG(VPP_VSC_PHASE_CTRL)); + writel_relaxed(priv->viu.vpp_hsc_phase_ctrl, + priv->io_base + _REG(VPP_HSC_PHASE_CTRL)); + writel_relaxed(0x42, priv->io_base + _REG(VPP_SCALE_COEF_IDX)); + + /* Enable VD1 */ + if (meson_crtc->enable_vd1) + meson_crtc->enable_vd1(priv); + + priv->viu.vd1_commit = false; + } + + if (meson_crtc->vsync_disabled) + return; + + drm_crtc_handle_vblank(priv->crtc); + + spin_lock_irqsave(&priv->drm->event_lock, flags); + if (meson_crtc->event) { + drm_crtc_send_vblank_event(priv->crtc, meson_crtc->event); + drm_crtc_vblank_put(priv->crtc); + meson_crtc->event = NULL; + } + spin_unlock_irqrestore(&priv->drm->event_lock, flags); +} + +int meson_crtc_create(struct meson_drm *priv) +{ + struct meson_crtc *meson_crtc; + struct drm_crtc *crtc; + int ret; + + meson_crtc = devm_kzalloc(priv->drm->dev, sizeof(*meson_crtc), + GFP_KERNEL); + if (!meson_crtc) + return -ENOMEM; + + meson_crtc->priv = priv; + crtc = &meson_crtc->base; + ret = drm_crtc_init_with_planes(priv->drm, crtc, + priv->primary_plane, NULL, + &meson_crtc_funcs, "meson_crtc"); + if (ret) { + dev_err(priv->drm->dev, "Failed to init CRTC\n"); + return ret; + } + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + meson_crtc->enable_osd1 = meson_g12a_crtc_enable_osd1; + meson_crtc->enable_vd1 = meson_g12a_crtc_enable_vd1; + meson_crtc->viu_offset = MESON_G12A_VIU_OFFSET; + meson_crtc->enable_osd1_afbc = + meson_crtc_g12a_enable_osd1_afbc; + meson_crtc->disable_osd1_afbc = + meson_viu_g12a_disable_osd1_afbc; + drm_crtc_helper_add(crtc, &meson_g12a_crtc_helper_funcs); + } else { + meson_crtc->enable_osd1 = meson_crtc_enable_osd1; + meson_crtc->enable_vd1 = meson_crtc_enable_vd1; + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM)) { + meson_crtc->enable_osd1_afbc = + meson_viu_gxm_enable_osd1_afbc; + meson_crtc->disable_osd1_afbc = + meson_viu_gxm_disable_osd1_afbc; + } + drm_crtc_helper_add(crtc, &meson_crtc_helper_funcs); + } + + priv->crtc = crtc; + + return 0; +} diff --git a/drivers/gpu/drm/meson/meson_crtc.h b/drivers/gpu/drm/meson/meson_crtc.h new file mode 100644 index 0000000000..8e3998cabf --- /dev/null +++ b/drivers/gpu/drm/meson/meson_crtc.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#ifndef __MESON_CRTC_H +#define __MESON_CRTC_H + +#include "meson_drv.h" + +int meson_crtc_create(struct meson_drm *priv); + +void meson_crtc_irq(struct meson_drm *priv); + +#endif /* __MESON_CRTC_H */ diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c new file mode 100644 index 0000000000..cb674966e9 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -0,0 +1,575 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#include <linux/component.h> +#include <linux/module.h> +#include <linux/of_graph.h> +#include <linux/sys_soc.h> +#include <linux/platform_device.h> +#include <linux/soc/amlogic/meson-canvas.h> + +#include <drm/drm_aperture.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fbdev_dma.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_module.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> + +#include "meson_crtc.h" +#include "meson_drv.h" +#include "meson_overlay.h" +#include "meson_plane.h" +#include "meson_osd_afbcd.h" +#include "meson_registers.h" +#include "meson_encoder_cvbs.h" +#include "meson_encoder_hdmi.h" +#include "meson_encoder_dsi.h" +#include "meson_viu.h" +#include "meson_vpp.h" +#include "meson_rdma.h" + +#define DRIVER_NAME "meson" +#define DRIVER_DESC "Amlogic Meson DRM driver" + +/** + * DOC: Video Processing Unit + * + * VPU Handles the Global Video Processing, it includes management of the + * clocks gates, blocks reset lines and power domains. + * + * What is missing : + * + * - Full reset of entire video processing HW blocks + * - Scaling and setup of the VPU clock + * - Bus clock gates + * - Powering up video processing HW blocks + * - Powering Up HDMI controller and PHY + */ + +static const struct drm_mode_config_funcs meson_mode_config_funcs = { + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, + .fb_create = drm_gem_fb_create, +}; + +static const struct drm_mode_config_helper_funcs meson_mode_config_helpers = { + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, +}; + +static irqreturn_t meson_irq(int irq, void *arg) +{ + struct drm_device *dev = arg; + struct meson_drm *priv = dev->dev_private; + + (void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG)); + + meson_crtc_irq(priv); + + return IRQ_HANDLED; +} + +static int meson_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + /* + * We need 64bytes aligned stride, and PAGE aligned size + */ + args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), SZ_64); + args->size = PAGE_ALIGN(args->pitch * args->height); + + return drm_gem_dma_dumb_create_internal(file, dev, args); +} + +DEFINE_DRM_GEM_DMA_FOPS(fops); + +static const struct drm_driver meson_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + + /* DMA Ops */ + DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(meson_dumb_create), + + /* Misc */ + .fops = &fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = "20161109", + .major = 1, + .minor = 0, +}; + +static bool meson_vpu_has_available_connectors(struct device *dev) +{ + struct device_node *ep, *remote; + + /* Parses each endpoint and check if remote exists */ + for_each_endpoint_of_node(dev->of_node, ep) { + /* If the endpoint node exists, consider it enabled */ + remote = of_graph_get_remote_port(ep); + if (remote) { + of_node_put(remote); + of_node_put(ep); + return true; + } + } + + return false; +} + +static struct regmap_config meson_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x1000, +}; + +static void meson_vpu_init(struct meson_drm *priv) +{ + u32 value; + + /* + * Slave dc0 and dc5 connected to master port 1. + * By default other slaves are connected to master port 0. + */ + value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) | + VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1); + writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C1)); + + /* Slave dc0 connected to master port 1 */ + value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1); + writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C2)); + + /* Slave dc4 and dc7 connected to master port 1 */ + value = VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) | + VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1); + writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L2C1)); + + /* Slave dc1 connected to master port 1 */ + value = VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1); + writel_relaxed(value, priv->io_base + _REG(VPU_WRARB_MODE_L2C1)); +} + +struct meson_drm_soc_attr { + struct meson_drm_soc_limits limits; + const struct soc_device_attribute *attrs; +}; + +static const struct meson_drm_soc_attr meson_drm_soc_attrs[] = { + /* S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz */ + { + .limits = { + .max_hdmi_phy_freq = 1650000, + }, + .attrs = (const struct soc_device_attribute []) { + { .soc_id = "GXL (S805*)", }, + { /* sentinel */ } + } + }, +}; + +static int meson_drv_bind_master(struct device *dev, bool has_components) +{ + struct platform_device *pdev = to_platform_device(dev); + const struct meson_drm_match_data *match; + struct meson_drm *priv; + struct drm_device *drm; + struct resource *res; + void __iomem *regs; + int ret, i; + + /* Checks if an output connector is available */ + if (!meson_vpu_has_available_connectors(dev)) { + dev_err(dev, "No output connector available\n"); + return -ENODEV; + } + + match = of_device_get_match_data(dev); + if (!match) + return -ENODEV; + + drm = drm_dev_alloc(&meson_driver, dev); + if (IS_ERR(drm)) + return PTR_ERR(drm); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto free_drm; + } + drm->dev_private = priv; + priv->drm = drm; + priv->dev = dev; + priv->compat = match->compat; + priv->afbcd.ops = match->afbcd_ops; + + regs = devm_platform_ioremap_resource_byname(pdev, "vpu"); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + goto free_drm; + } + + priv->io_base = regs; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi"); + if (!res) { + ret = -EINVAL; + goto free_drm; + } + /* Simply ioremap since it may be a shared register zone */ + regs = devm_ioremap(dev, res->start, resource_size(res)); + if (!regs) { + ret = -EADDRNOTAVAIL; + goto free_drm; + } + + priv->hhi = devm_regmap_init_mmio(dev, regs, + &meson_regmap_config); + if (IS_ERR(priv->hhi)) { + dev_err(&pdev->dev, "Couldn't create the HHI regmap\n"); + ret = PTR_ERR(priv->hhi); + goto free_drm; + } + + priv->canvas = meson_canvas_get(dev); + if (IS_ERR(priv->canvas)) { + ret = PTR_ERR(priv->canvas); + goto free_drm; + } + + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1); + if (ret) + goto free_drm; + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0); + if (ret) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); + goto free_drm; + } + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1); + if (ret) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); + goto free_drm; + } + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2); + if (ret) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); + goto free_drm; + } + + priv->vsync_irq = platform_get_irq(pdev, 0); + + ret = drm_vblank_init(drm, 1); + if (ret) + goto free_drm; + + /* Assign limits per soc revision/package */ + for (i = 0 ; i < ARRAY_SIZE(meson_drm_soc_attrs) ; ++i) { + if (soc_device_match(meson_drm_soc_attrs[i].attrs)) { + priv->limits = &meson_drm_soc_attrs[i].limits; + break; + } + } + + /* + * Remove early framebuffers (ie. simplefb). The framebuffer can be + * located anywhere in RAM + */ + ret = drm_aperture_remove_framebuffers(&meson_driver); + if (ret) + goto free_drm; + + ret = drmm_mode_config_init(drm); + if (ret) + goto free_drm; + drm->mode_config.max_width = 3840; + drm->mode_config.max_height = 2160; + drm->mode_config.funcs = &meson_mode_config_funcs; + drm->mode_config.helper_private = &meson_mode_config_helpers; + + /* Hardware Initialization */ + + meson_vpu_init(priv); + meson_venc_init(priv); + meson_vpp_init(priv); + meson_viu_init(priv); + if (priv->afbcd.ops) { + ret = priv->afbcd.ops->init(priv); + if (ret) + goto free_drm; + } + + /* Encoder Initialization */ + + ret = meson_encoder_cvbs_init(priv); + if (ret) + goto exit_afbcd; + + if (has_components) { + ret = component_bind_all(dev, drm); + if (ret) { + dev_err(drm->dev, "Couldn't bind all components\n"); + /* Do not try to unbind */ + has_components = false; + goto exit_afbcd; + } + } + + ret = meson_encoder_hdmi_init(priv); + if (ret) + goto exit_afbcd; + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + ret = meson_encoder_dsi_init(priv); + if (ret) + goto exit_afbcd; + } + + ret = meson_plane_create(priv); + if (ret) + goto exit_afbcd; + + ret = meson_overlay_create(priv); + if (ret) + goto exit_afbcd; + + ret = meson_crtc_create(priv); + if (ret) + goto exit_afbcd; + + ret = request_irq(priv->vsync_irq, meson_irq, 0, drm->driver->name, drm); + if (ret) + goto exit_afbcd; + + drm_mode_config_reset(drm); + + drm_kms_helper_poll_init(drm); + + platform_set_drvdata(pdev, priv); + + ret = drm_dev_register(drm, 0); + if (ret) + goto uninstall_irq; + + drm_fbdev_dma_setup(drm, 32); + + return 0; + +uninstall_irq: + free_irq(priv->vsync_irq, drm); +exit_afbcd: + if (priv->afbcd.ops) + priv->afbcd.ops->exit(priv); +free_drm: + drm_dev_put(drm); + + meson_encoder_dsi_remove(priv); + meson_encoder_hdmi_remove(priv); + meson_encoder_cvbs_remove(priv); + + if (has_components) + component_unbind_all(dev, drm); + + return ret; +} + +static int meson_drv_bind(struct device *dev) +{ + return meson_drv_bind_master(dev, true); +} + +static void meson_drv_unbind(struct device *dev) +{ + struct meson_drm *priv = dev_get_drvdata(dev); + struct drm_device *drm = priv->drm; + + if (priv->canvas) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2); + } + + drm_dev_unregister(drm); + drm_kms_helper_poll_fini(drm); + drm_atomic_helper_shutdown(drm); + free_irq(priv->vsync_irq, drm); + drm_dev_put(drm); + + meson_encoder_dsi_remove(priv); + meson_encoder_hdmi_remove(priv); + meson_encoder_cvbs_remove(priv); + + component_unbind_all(dev, drm); + + if (priv->afbcd.ops) + priv->afbcd.ops->exit(priv); +} + +static const struct component_master_ops meson_drv_master_ops = { + .bind = meson_drv_bind, + .unbind = meson_drv_unbind, +}; + +static int __maybe_unused meson_drv_pm_suspend(struct device *dev) +{ + struct meson_drm *priv = dev_get_drvdata(dev); + + if (!priv) + return 0; + + return drm_mode_config_helper_suspend(priv->drm); +} + +static int __maybe_unused meson_drv_pm_resume(struct device *dev) +{ + struct meson_drm *priv = dev_get_drvdata(dev); + + if (!priv) + return 0; + + meson_vpu_init(priv); + meson_venc_init(priv); + meson_vpp_init(priv); + meson_viu_init(priv); + if (priv->afbcd.ops) + priv->afbcd.ops->init(priv); + + return drm_mode_config_helper_resume(priv->drm); +} + +static void meson_drv_shutdown(struct platform_device *pdev) +{ + struct meson_drm *priv = dev_get_drvdata(&pdev->dev); + + if (!priv) + return; + + drm_kms_helper_poll_fini(priv->drm); + drm_atomic_helper_shutdown(priv->drm); +} + +/* + * Only devices to use as components + * TOFIX: get rid of components when we can finally + * get meson_dx_hdmi to stop using the meson_drm + * private structure for HHI registers. + */ +static const struct of_device_id components_dev_match[] = { + { .compatible = "amlogic,meson-gxbb-dw-hdmi" }, + { .compatible = "amlogic,meson-gxl-dw-hdmi" }, + { .compatible = "amlogic,meson-gxm-dw-hdmi" }, + { .compatible = "amlogic,meson-g12a-dw-hdmi" }, + {} +}; + +static int meson_drv_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + struct device_node *np = pdev->dev.of_node; + struct device_node *ep, *remote; + int count = 0; + + for_each_endpoint_of_node(np, ep) { + remote = of_graph_get_remote_port_parent(ep); + if (!remote || !of_device_is_available(remote)) { + of_node_put(remote); + continue; + } + + if (of_match_node(components_dev_match, remote)) { + component_match_add(&pdev->dev, &match, component_compare_of, remote); + + dev_dbg(&pdev->dev, "parent %pOF remote match add %pOF parent %s\n", + np, remote, dev_name(&pdev->dev)); + } + + of_node_put(remote); + + ++count; + } + + if (count && !match) + return meson_drv_bind_master(&pdev->dev, false); + + /* If some endpoints were found, initialize the nodes */ + if (count) { + dev_info(&pdev->dev, "Queued %d outputs on vpu\n", count); + + return component_master_add_with_match(&pdev->dev, + &meson_drv_master_ops, + match); + } + + /* If no output endpoints were available, simply bail out */ + return 0; +}; + +static void meson_drv_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &meson_drv_master_ops); +} + +static struct meson_drm_match_data meson_drm_gxbb_data = { + .compat = VPU_COMPATIBLE_GXBB, +}; + +static struct meson_drm_match_data meson_drm_gxl_data = { + .compat = VPU_COMPATIBLE_GXL, +}; + +static struct meson_drm_match_data meson_drm_gxm_data = { + .compat = VPU_COMPATIBLE_GXM, + .afbcd_ops = &meson_afbcd_gxm_ops, +}; + +static struct meson_drm_match_data meson_drm_g12a_data = { + .compat = VPU_COMPATIBLE_G12A, + .afbcd_ops = &meson_afbcd_g12a_ops, +}; + +static const struct of_device_id dt_match[] = { + { .compatible = "amlogic,meson-gxbb-vpu", + .data = (void *)&meson_drm_gxbb_data }, + { .compatible = "amlogic,meson-gxl-vpu", + .data = (void *)&meson_drm_gxl_data }, + { .compatible = "amlogic,meson-gxm-vpu", + .data = (void *)&meson_drm_gxm_data }, + { .compatible = "amlogic,meson-g12a-vpu", + .data = (void *)&meson_drm_g12a_data }, + {} +}; +MODULE_DEVICE_TABLE(of, dt_match); + +static const struct dev_pm_ops meson_drv_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(meson_drv_pm_suspend, meson_drv_pm_resume) +}; + +static struct platform_driver meson_drm_platform_driver = { + .probe = meson_drv_probe, + .remove_new = meson_drv_remove, + .shutdown = meson_drv_shutdown, + .driver = { + .name = "meson-drm", + .of_match_table = dt_match, + .pm = &meson_drv_pm_ops, + }, +}; + +drm_module_platform_driver(meson_drm_platform_driver); + +MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>"); +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h new file mode 100644 index 0000000000..3f9345c14f --- /dev/null +++ b/drivers/gpu/drm/meson/meson_drv.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#ifndef __MESON_DRV_H +#define __MESON_DRV_H + +#include <linux/device.h> +#include <linux/of.h> +#include <linux/regmap.h> + +struct drm_crtc; +struct drm_device; +struct drm_plane; +struct meson_drm; +struct meson_afbcd_ops; + +enum vpu_compatible { + VPU_COMPATIBLE_GXBB = 0, + VPU_COMPATIBLE_GXL = 1, + VPU_COMPATIBLE_GXM = 2, + VPU_COMPATIBLE_G12A = 3, +}; + +enum { + MESON_ENC_CVBS = 0, + MESON_ENC_HDMI, + MESON_ENC_DSI, + MESON_ENC_LAST, +}; + +struct meson_drm_match_data { + enum vpu_compatible compat; + struct meson_afbcd_ops *afbcd_ops; +}; + +struct meson_drm_soc_limits { + unsigned int max_hdmi_phy_freq; +}; + +struct meson_drm { + struct device *dev; + enum vpu_compatible compat; + void __iomem *io_base; + struct regmap *hhi; + int vsync_irq; + + struct meson_canvas *canvas; + u8 canvas_id_osd1; + u8 canvas_id_vd1_0; + u8 canvas_id_vd1_1; + u8 canvas_id_vd1_2; + + struct drm_device *drm; + struct drm_crtc *crtc; + struct drm_plane *primary_plane; + struct drm_plane *overlay_plane; + void *encoders[MESON_ENC_LAST]; + + const struct meson_drm_soc_limits *limits; + + /* Components Data */ + struct { + bool osd1_enabled; + bool osd1_interlace; + bool osd1_commit; + bool osd1_afbcd; + uint32_t osd1_ctrl_stat; + uint32_t osd1_ctrl_stat2; + uint32_t osd1_blk0_cfg[5]; + uint32_t osd1_blk1_cfg4; + uint32_t osd1_blk2_cfg4; + uint32_t osd1_addr; + uint32_t osd1_stride; + uint32_t osd1_height; + uint32_t osd1_width; + uint32_t osd_sc_ctrl0; + uint32_t osd_sc_i_wh_m1; + uint32_t osd_sc_o_h_start_end; + uint32_t osd_sc_o_v_start_end; + uint32_t osd_sc_v_ini_phase; + uint32_t osd_sc_v_phase_step; + uint32_t osd_sc_h_ini_phase; + uint32_t osd_sc_h_phase_step; + uint32_t osd_sc_h_ctrl0; + uint32_t osd_sc_v_ctrl0; + uint32_t osd_blend_din0_scope_h; + uint32_t osd_blend_din0_scope_v; + uint32_t osb_blend0_size; + uint32_t osb_blend1_size; + + bool vd1_enabled; + bool vd1_commit; + bool vd1_afbc; + unsigned int vd1_planes; + uint32_t vd1_if0_gen_reg; + uint32_t vd1_if0_luma_x0; + uint32_t vd1_if0_luma_y0; + uint32_t vd1_if0_chroma_x0; + uint32_t vd1_if0_chroma_y0; + uint32_t vd1_if0_repeat_loop; + uint32_t vd1_if0_luma0_rpt_pat; + uint32_t vd1_if0_chroma0_rpt_pat; + uint32_t vd1_range_map_y; + uint32_t vd1_range_map_cb; + uint32_t vd1_range_map_cr; + uint32_t viu_vd1_fmt_w; + uint32_t vd1_if0_canvas0; + uint32_t vd1_if0_gen_reg2; + uint32_t viu_vd1_fmt_ctrl; + uint32_t vd1_addr0; + uint32_t vd1_addr1; + uint32_t vd1_addr2; + uint32_t vd1_stride0; + uint32_t vd1_stride1; + uint32_t vd1_stride2; + uint32_t vd1_height0; + uint32_t vd1_height1; + uint32_t vd1_height2; + uint32_t vd1_afbc_mode; + uint32_t vd1_afbc_en; + uint32_t vd1_afbc_head_addr; + uint32_t vd1_afbc_body_addr; + uint32_t vd1_afbc_conv_ctrl; + uint32_t vd1_afbc_dec_def_color; + uint32_t vd1_afbc_vd_cfmt_ctrl; + uint32_t vd1_afbc_vd_cfmt_w; + uint32_t vd1_afbc_vd_cfmt_h; + uint32_t vd1_afbc_mif_hor_scope; + uint32_t vd1_afbc_mif_ver_scope; + uint32_t vd1_afbc_size_out; + uint32_t vd1_afbc_pixel_hor_scope; + uint32_t vd1_afbc_pixel_ver_scope; + uint32_t vd1_afbc_size_in; + uint32_t vpp_pic_in_height; + uint32_t vpp_postblend_vd1_h_start_end; + uint32_t vpp_postblend_vd1_v_start_end; + uint32_t vpp_hsc_region12_startp; + uint32_t vpp_hsc_region34_startp; + uint32_t vpp_hsc_region4_endp; + uint32_t vpp_hsc_start_phase_step; + uint32_t vpp_hsc_region1_phase_slope; + uint32_t vpp_hsc_region3_phase_slope; + uint32_t vpp_line_in_length; + uint32_t vpp_preblend_h_size; + uint32_t vpp_vsc_region12_startp; + uint32_t vpp_vsc_region34_startp; + uint32_t vpp_vsc_region4_endp; + uint32_t vpp_vsc_start_phase_step; + uint32_t vpp_vsc_ini_phase; + uint32_t vpp_vsc_phase_ctrl; + uint32_t vpp_hsc_phase_ctrl; + uint32_t vpp_blend_vd2_h_start_end; + uint32_t vpp_blend_vd2_v_start_end; + } viu; + + struct { + unsigned int current_mode; + bool hdmi_repeat; + bool venc_repeat; + bool hdmi_use_enci; + } venc; + + struct { + dma_addr_t addr_dma; + uint32_t *addr; + unsigned int offset; + } rdma; + + struct { + struct meson_afbcd_ops *ops; + u64 modifier; + u32 format; + } afbcd; +}; + +static inline int meson_vpu_is_compatible(struct meson_drm *priv, + enum vpu_compatible family) +{ + return priv->compat == family; +} + +#endif /* __MESON_DRV_H */ diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c new file mode 100644 index 0000000000..5a9538bc0e --- /dev/null +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c @@ -0,0 +1,892 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> + +#include <drm/bridge/dw_hdmi.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_device.h> +#include <drm/drm_edid.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_print.h> + +#include <linux/videodev2.h> + +#include "meson_drv.h" +#include "meson_dw_hdmi.h" +#include "meson_registers.h" + +#define DRIVER_NAME "meson-dw-hdmi" +#define DRIVER_DESC "Amlogic Meson HDMI-TX DRM driver" + +/** + * DOC: HDMI Output + * + * HDMI Output is composed of : + * + * - A Synopsys DesignWare HDMI Controller IP + * - A TOP control block controlling the Clocks and PHY + * - A custom HDMI PHY in order convert video to TMDS signal + * + * .. code:: + * + * ___________________________________ + * | HDMI TOP |<= HPD + * |___________________________________| + * | | | + * | Synopsys HDMI | HDMI PHY |=> TMDS + * | Controller |________________| + * |___________________________________|<=> DDC + * + * + * The HDMI TOP block only supports HPD sensing. + * The Synopsys HDMI Controller interrupt is routed + * through the TOP Block interrupt. + * Communication to the TOP Block and the Synopsys + * HDMI Controller is done a pair of addr+read/write + * registers. + * The HDMI PHY is configured by registers in the + * HHI register block. + * + * Pixel data arrives in 4:4:4 format from the VENC + * block and the VPU HDMI mux selects either the ENCI + * encoder for the 576i or 480i formats or the ENCP + * encoder for all the other formats including + * interlaced HD formats. + * The VENC uses a DVI encoder on top of the ENCI + * or ENCP encoders to generate DVI timings for the + * HDMI controller. + * + * GXBB, GXL and GXM embeds the Synopsys DesignWare + * HDMI TX IP version 2.01a with HDCP and I2C & S/PDIF + * audio source interfaces. + * + * We handle the following features : + * + * - HPD Rise & Fall interrupt + * - HDMI Controller Interrupt + * - HDMI PHY Init for 480i to 1080p60 + * - VENC & HDMI Clock setup for 480i to 1080p60 + * - VENC Mode setup for 480i to 1080p60 + * + * What is missing : + * + * - PHY, Clock and Mode setup for 2k && 4k modes + * - SDDC Scrambling mode for HDMI 2.0a + * - HDCP Setup + * - CEC Management + */ + +/* TOP Block Communication Channel */ +#define HDMITX_TOP_ADDR_REG 0x0 +#define HDMITX_TOP_DATA_REG 0x4 +#define HDMITX_TOP_CTRL_REG 0x8 +#define HDMITX_TOP_G12A_OFFSET 0x8000 + +/* Controller Communication Channel */ +#define HDMITX_DWC_ADDR_REG 0x10 +#define HDMITX_DWC_DATA_REG 0x14 +#define HDMITX_DWC_CTRL_REG 0x18 + +/* HHI Registers */ +#define HHI_MEM_PD_REG0 0x100 /* 0x40 */ +#define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 */ +#define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 */ +#define HHI_HDMI_PHY_CNTL1 0x3a4 /* 0xe9 */ +#define HHI_HDMI_PHY_CNTL2 0x3a8 /* 0xea */ +#define HHI_HDMI_PHY_CNTL3 0x3ac /* 0xeb */ +#define HHI_HDMI_PHY_CNTL4 0x3b0 /* 0xec */ +#define HHI_HDMI_PHY_CNTL5 0x3b4 /* 0xed */ + +static DEFINE_SPINLOCK(reg_lock); + +enum meson_venc_source { + MESON_VENC_SOURCE_NONE = 0, + MESON_VENC_SOURCE_ENCI = 1, + MESON_VENC_SOURCE_ENCP = 2, +}; + +struct meson_dw_hdmi; + +struct meson_dw_hdmi_data { + unsigned int (*top_read)(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr); + void (*top_write)(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr, unsigned int data); + unsigned int (*dwc_read)(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr); + void (*dwc_write)(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr, unsigned int data); +}; + +struct meson_dw_hdmi { + struct dw_hdmi_plat_data dw_plat_data; + struct meson_drm *priv; + struct device *dev; + void __iomem *hdmitx; + const struct meson_dw_hdmi_data *data; + struct reset_control *hdmitx_apb; + struct reset_control *hdmitx_ctrl; + struct reset_control *hdmitx_phy; + u32 irq_stat; + struct dw_hdmi *hdmi; + struct drm_bridge *bridge; +}; + +static inline int dw_hdmi_is_compatible(struct meson_dw_hdmi *dw_hdmi, + const char *compat) +{ + return of_device_is_compatible(dw_hdmi->dev->of_node, compat); +} + +/* PHY (via TOP bridge) and Controller dedicated register interface */ + +static unsigned int dw_hdmi_top_read(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr) +{ + unsigned long flags; + unsigned int data; + + spin_lock_irqsave(®_lock, flags); + + /* ADDR must be written twice */ + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG); + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG); + + /* Read needs a second DATA read */ + data = readl(dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG); + data = readl(dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG); + + spin_unlock_irqrestore(®_lock, flags); + + return data; +} + +static unsigned int dw_hdmi_g12a_top_read(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr) +{ + return readl(dw_hdmi->hdmitx + HDMITX_TOP_G12A_OFFSET + (addr << 2)); +} + +static inline void dw_hdmi_top_write(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr, unsigned int data) +{ + unsigned long flags; + + spin_lock_irqsave(®_lock, flags); + + /* ADDR must be written twice */ + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG); + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG); + + /* Write needs single DATA write */ + writel(data, dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG); + + spin_unlock_irqrestore(®_lock, flags); +} + +static inline void dw_hdmi_g12a_top_write(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr, unsigned int data) +{ + writel(data, dw_hdmi->hdmitx + HDMITX_TOP_G12A_OFFSET + (addr << 2)); +} + +/* Helper to change specific bits in PHY registers */ +static inline void dw_hdmi_top_write_bits(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr, + unsigned int mask, + unsigned int val) +{ + unsigned int data = dw_hdmi->data->top_read(dw_hdmi, addr); + + data &= ~mask; + data |= val; + + dw_hdmi->data->top_write(dw_hdmi, addr, data); +} + +static unsigned int dw_hdmi_dwc_read(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr) +{ + unsigned long flags; + unsigned int data; + + spin_lock_irqsave(®_lock, flags); + + /* ADDR must be written twice */ + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG); + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG); + + /* Read needs a second DATA read */ + data = readl(dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG); + data = readl(dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG); + + spin_unlock_irqrestore(®_lock, flags); + + return data; +} + +static unsigned int dw_hdmi_g12a_dwc_read(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr) +{ + return readb(dw_hdmi->hdmitx + addr); +} + +static inline void dw_hdmi_dwc_write(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr, unsigned int data) +{ + unsigned long flags; + + spin_lock_irqsave(®_lock, flags); + + /* ADDR must be written twice */ + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG); + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG); + + /* Write needs single DATA write */ + writel(data, dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG); + + spin_unlock_irqrestore(®_lock, flags); +} + +static inline void dw_hdmi_g12a_dwc_write(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr, unsigned int data) +{ + writeb(data, dw_hdmi->hdmitx + addr); +} + +/* Helper to change specific bits in controller registers */ +static inline void dw_hdmi_dwc_write_bits(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr, + unsigned int mask, + unsigned int val) +{ + unsigned int data = dw_hdmi->data->dwc_read(dw_hdmi, addr); + + data &= ~mask; + data |= val; + + dw_hdmi->data->dwc_write(dw_hdmi, addr, data); +} + +/* Bridge */ + +/* Setup PHY bandwidth modes */ +static void meson_hdmi_phy_setup_mode(struct meson_dw_hdmi *dw_hdmi, + const struct drm_display_mode *mode, + bool mode_is_420) +{ + struct meson_drm *priv = dw_hdmi->priv; + unsigned int pixel_clock = mode->clock; + + /* For 420, pixel clock is half unlike venc clock */ + if (mode_is_420) pixel_clock /= 2; + + if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") || + dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi")) { + if (pixel_clock >= 371250) { + /* 5.94Gbps, 3.7125Gbps */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x333d3282); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2136315b); + } else if (pixel_clock >= 297000) { + /* 2.97Gbps */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33303382); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2036315b); + } else if (pixel_clock >= 148500) { + /* 1.485Gbps */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33303362); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2016315b); + } else { + /* 742.5Mbps, and below */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33604142); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x0016315b); + } + } else if (dw_hdmi_is_compatible(dw_hdmi, + "amlogic,meson-gxbb-dw-hdmi")) { + if (pixel_clock >= 371250) { + /* 5.94Gbps, 3.7125Gbps */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33353245); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2100115b); + } else if (pixel_clock >= 297000) { + /* 2.97Gbps */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33634283); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0xb000115b); + } else { + /* 1.485Gbps, and below */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33632122); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2000115b); + } + } else if (dw_hdmi_is_compatible(dw_hdmi, + "amlogic,meson-g12a-dw-hdmi")) { + if (pixel_clock >= 371250) { + /* 5.94Gbps, 3.7125Gbps */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x37eb65c4); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x0000080b); + } else if (pixel_clock >= 297000) { + /* 2.97Gbps */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33eb6262); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x00000003); + } else { + /* 1.485Gbps, and below */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33eb4242); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x00000003); + } + } +} + +static inline void meson_dw_hdmi_phy_reset(struct meson_dw_hdmi *dw_hdmi) +{ + struct meson_drm *priv = dw_hdmi->priv; + + /* Enable and software reset */ + regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0xf); + + mdelay(2); + + /* Enable and unreset */ + regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0xe); + + mdelay(2); +} + +static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data, + const struct drm_display_info *display, + const struct drm_display_mode *mode) +{ + struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data; + bool is_hdmi2_sink = display->hdmi.scdc.supported; + struct meson_drm *priv = dw_hdmi->priv; + unsigned int wr_clk = + readl_relaxed(priv->io_base + _REG(VPU_HDMI_SETTING)); + bool mode_is_420 = false; + + DRM_DEBUG_DRIVER("\"%s\" div%d\n", mode->name, + mode->clock > 340000 ? 40 : 10); + + if (drm_mode_is_420_only(display, mode) || + (!is_hdmi2_sink && drm_mode_is_420_also(display, mode)) || + dw_hdmi_bus_fmt_is_420(hdmi)) + mode_is_420 = true; + + /* Enable clocks */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, 0xffff, 0x100); + + /* Bring HDMITX MEM output of power down */ + regmap_update_bits(priv->hhi, HHI_MEM_PD_REG0, 0xff << 8, 0); + + /* Bring out of reset */ + dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_SW_RESET, 0); + + /* Enable internal pixclk, tmds_clk, spdif_clk, i2s_clk, cecclk */ + dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_CLK_CNTL, + 0x3, 0x3); + + /* Enable cec_clk and hdcp22_tmdsclk_en */ + dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_CLK_CNTL, + 0x3 << 4, 0x3 << 4); + + /* Enable normal output to PHY */ + dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12)); + + /* TMDS pattern setup */ + if (mode->clock > 340000 && !mode_is_420) { + dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01, + 0); + dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23, + 0x03ff03ff); + } else { + dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01, + 0x001f001f); + dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23, + 0x001f001f); + } + + /* Load TMDS pattern */ + dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x1); + msleep(20); + dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x2); + + /* Setup PHY parameters */ + meson_hdmi_phy_setup_mode(dw_hdmi, mode, mode_is_420); + + /* Setup PHY */ + regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, + 0xffff << 16, 0x0390 << 16); + + /* BIT_INVERT */ + if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") || + dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi") || + dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-g12a-dw-hdmi")) + regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, + BIT(17), 0); + else + regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, + BIT(17), BIT(17)); + + /* Disable clock, fifo, fifo_wr */ + regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0); + + dw_hdmi_set_high_tmds_clock_ratio(hdmi, display); + + msleep(100); + + /* Reset PHY 3 times in a row */ + meson_dw_hdmi_phy_reset(dw_hdmi); + meson_dw_hdmi_phy_reset(dw_hdmi); + meson_dw_hdmi_phy_reset(dw_hdmi); + + /* Temporary Disable VENC video stream */ + if (priv->venc.hdmi_use_enci) + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN)); + else + writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN)); + + /* Temporary Disable HDMI video stream to HDMI-TX */ + writel_bits_relaxed(0x3, 0, + priv->io_base + _REG(VPU_HDMI_SETTING)); + writel_bits_relaxed(0xf << 8, 0, + priv->io_base + _REG(VPU_HDMI_SETTING)); + + /* Re-Enable VENC video stream */ + if (priv->venc.hdmi_use_enci) + writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN)); + else + writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN)); + + /* Push back HDMI clock settings */ + writel_bits_relaxed(0xf << 8, wr_clk & (0xf << 8), + priv->io_base + _REG(VPU_HDMI_SETTING)); + + /* Enable and Select HDMI video source for HDMI-TX */ + if (priv->venc.hdmi_use_enci) + writel_bits_relaxed(0x3, MESON_VENC_SOURCE_ENCI, + priv->io_base + _REG(VPU_HDMI_SETTING)); + else + writel_bits_relaxed(0x3, MESON_VENC_SOURCE_ENCP, + priv->io_base + _REG(VPU_HDMI_SETTING)); + + return 0; +} + +static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi, + void *data) +{ + struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data; + struct meson_drm *priv = dw_hdmi->priv; + + DRM_DEBUG_DRIVER("\n"); + + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0); +} + +static enum drm_connector_status dw_hdmi_read_hpd(struct dw_hdmi *hdmi, + void *data) +{ + struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data; + + return !!dw_hdmi->data->top_read(dw_hdmi, HDMITX_TOP_STAT0) ? + connector_status_connected : connector_status_disconnected; +} + +static void dw_hdmi_setup_hpd(struct dw_hdmi *hdmi, + void *data) +{ + struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data; + + /* Setup HPD Filter */ + dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_HPD_FILTER, + (0xa << 12) | 0xa0); + + /* Clear interrupts */ + dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, + HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL); + + /* Unmask interrupts */ + dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_INTR_MASKN, + HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL, + HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL); +} + +static const struct dw_hdmi_phy_ops meson_dw_hdmi_phy_ops = { + .init = dw_hdmi_phy_init, + .disable = dw_hdmi_phy_disable, + .read_hpd = dw_hdmi_read_hpd, + .setup_hpd = dw_hdmi_setup_hpd, +}; + +static irqreturn_t dw_hdmi_top_irq(int irq, void *dev_id) +{ + struct meson_dw_hdmi *dw_hdmi = dev_id; + u32 stat; + + stat = dw_hdmi->data->top_read(dw_hdmi, HDMITX_TOP_INTR_STAT); + dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, stat); + + /* HPD Events, handle in the threaded interrupt handler */ + if (stat & (HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL)) { + dw_hdmi->irq_stat = stat; + return IRQ_WAKE_THREAD; + } + + /* HDMI Controller Interrupt */ + if (stat & 1) + return IRQ_NONE; + + /* TOFIX Handle HDCP Interrupts */ + + return IRQ_HANDLED; +} + +/* Threaded interrupt handler to manage HPD events */ +static irqreturn_t dw_hdmi_top_thread_irq(int irq, void *dev_id) +{ + struct meson_dw_hdmi *dw_hdmi = dev_id; + u32 stat = dw_hdmi->irq_stat; + + /* HPD Events */ + if (stat & (HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL)) { + bool hpd_connected = false; + + if (stat & HDMITX_TOP_INTR_HPD_RISE) + hpd_connected = true; + + dw_hdmi_setup_rx_sense(dw_hdmi->hdmi, hpd_connected, + hpd_connected); + + drm_helper_hpd_irq_event(dw_hdmi->bridge->dev); + drm_bridge_hpd_notify(dw_hdmi->bridge, + hpd_connected ? connector_status_connected + : connector_status_disconnected); + } + + return IRQ_HANDLED; +} + +/* DW HDMI Regmap */ + +static int meson_dw_hdmi_reg_read(void *context, unsigned int reg, + unsigned int *result) +{ + struct meson_dw_hdmi *dw_hdmi = context; + + *result = dw_hdmi->data->dwc_read(dw_hdmi, reg); + + return 0; + +} + +static int meson_dw_hdmi_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct meson_dw_hdmi *dw_hdmi = context; + + dw_hdmi->data->dwc_write(dw_hdmi, reg, val); + + return 0; +} + +static const struct regmap_config meson_dw_hdmi_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .reg_read = meson_dw_hdmi_reg_read, + .reg_write = meson_dw_hdmi_reg_write, + .max_register = 0x10000, + .fast_io = true, +}; + +static const struct meson_dw_hdmi_data meson_dw_hdmi_gx_data = { + .top_read = dw_hdmi_top_read, + .top_write = dw_hdmi_top_write, + .dwc_read = dw_hdmi_dwc_read, + .dwc_write = dw_hdmi_dwc_write, +}; + +static const struct meson_dw_hdmi_data meson_dw_hdmi_g12a_data = { + .top_read = dw_hdmi_g12a_top_read, + .top_write = dw_hdmi_g12a_top_write, + .dwc_read = dw_hdmi_g12a_dwc_read, + .dwc_write = dw_hdmi_g12a_dwc_write, +}; + +static void meson_dw_hdmi_init(struct meson_dw_hdmi *meson_dw_hdmi) +{ + struct meson_drm *priv = meson_dw_hdmi->priv; + + /* Enable clocks */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, 0xffff, 0x100); + + /* Bring HDMITX MEM output of power down */ + regmap_update_bits(priv->hhi, HHI_MEM_PD_REG0, 0xff << 8, 0); + + /* Reset HDMITX APB & TX & PHY */ + reset_control_reset(meson_dw_hdmi->hdmitx_apb); + reset_control_reset(meson_dw_hdmi->hdmitx_ctrl); + reset_control_reset(meson_dw_hdmi->hdmitx_phy); + + /* Enable APB3 fail on error */ + if (!meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + writel_bits_relaxed(BIT(15), BIT(15), + meson_dw_hdmi->hdmitx + HDMITX_TOP_CTRL_REG); + writel_bits_relaxed(BIT(15), BIT(15), + meson_dw_hdmi->hdmitx + HDMITX_DWC_CTRL_REG); + } + + /* Bring out of reset */ + meson_dw_hdmi->data->top_write(meson_dw_hdmi, + HDMITX_TOP_SW_RESET, 0); + + msleep(20); + + meson_dw_hdmi->data->top_write(meson_dw_hdmi, + HDMITX_TOP_CLK_CNTL, 0xff); + + /* Enable HDMI-TX Interrupt */ + meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, + HDMITX_TOP_INTR_CORE); + + meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_MASKN, + HDMITX_TOP_INTR_CORE); + +} + +static void meson_disable_clk(void *data) +{ + clk_disable_unprepare(data); +} + +static int meson_enable_clk(struct device *dev, char *name) +{ + struct clk *clk; + int ret; + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) { + dev_err(dev, "Unable to get %s pclk\n", name); + return PTR_ERR(clk); + } + + ret = clk_prepare_enable(clk); + if (!ret) + ret = devm_add_action_or_reset(dev, meson_disable_clk, clk); + + return ret; +} + +static int meson_dw_hdmi_bind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + const struct meson_dw_hdmi_data *match; + struct meson_dw_hdmi *meson_dw_hdmi; + struct drm_device *drm = data; + struct meson_drm *priv = drm->dev_private; + struct dw_hdmi_plat_data *dw_plat_data; + int irq; + int ret; + + DRM_DEBUG_DRIVER("\n"); + + match = of_device_get_match_data(&pdev->dev); + if (!match) { + dev_err(&pdev->dev, "failed to get match data\n"); + return -ENODEV; + } + + meson_dw_hdmi = devm_kzalloc(dev, sizeof(*meson_dw_hdmi), + GFP_KERNEL); + if (!meson_dw_hdmi) + return -ENOMEM; + + meson_dw_hdmi->priv = priv; + meson_dw_hdmi->dev = dev; + meson_dw_hdmi->data = match; + dw_plat_data = &meson_dw_hdmi->dw_plat_data; + + ret = devm_regulator_get_enable_optional(dev, "hdmi"); + if (ret < 0 && ret != -ENODEV) + return ret; + + meson_dw_hdmi->hdmitx_apb = devm_reset_control_get_exclusive(dev, + "hdmitx_apb"); + if (IS_ERR(meson_dw_hdmi->hdmitx_apb)) { + dev_err(dev, "Failed to get hdmitx_apb reset\n"); + return PTR_ERR(meson_dw_hdmi->hdmitx_apb); + } + + meson_dw_hdmi->hdmitx_ctrl = devm_reset_control_get_exclusive(dev, + "hdmitx"); + if (IS_ERR(meson_dw_hdmi->hdmitx_ctrl)) { + dev_err(dev, "Failed to get hdmitx reset\n"); + return PTR_ERR(meson_dw_hdmi->hdmitx_ctrl); + } + + meson_dw_hdmi->hdmitx_phy = devm_reset_control_get_exclusive(dev, + "hdmitx_phy"); + if (IS_ERR(meson_dw_hdmi->hdmitx_phy)) { + dev_err(dev, "Failed to get hdmitx_phy reset\n"); + return PTR_ERR(meson_dw_hdmi->hdmitx_phy); + } + + meson_dw_hdmi->hdmitx = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(meson_dw_hdmi->hdmitx)) + return PTR_ERR(meson_dw_hdmi->hdmitx); + + ret = meson_enable_clk(dev, "isfr"); + if (ret) + return ret; + + ret = meson_enable_clk(dev, "iahb"); + if (ret) + return ret; + + ret = meson_enable_clk(dev, "venci"); + if (ret) + return ret; + + dw_plat_data->regm = devm_regmap_init(dev, NULL, meson_dw_hdmi, + &meson_dw_hdmi_regmap_config); + if (IS_ERR(dw_plat_data->regm)) + return PTR_ERR(dw_plat_data->regm); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, dw_hdmi_top_irq, + dw_hdmi_top_thread_irq, IRQF_SHARED, + "dw_hdmi_top_irq", meson_dw_hdmi); + if (ret) { + dev_err(dev, "Failed to request hdmi top irq\n"); + return ret; + } + + meson_dw_hdmi_init(meson_dw_hdmi); + + /* Bridge / Connector */ + + dw_plat_data->priv_data = meson_dw_hdmi; + dw_plat_data->phy_ops = &meson_dw_hdmi_phy_ops; + dw_plat_data->phy_name = "meson_dw_hdmi_phy"; + dw_plat_data->phy_data = meson_dw_hdmi; + dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709; + dw_plat_data->ycbcr_420_allowed = true; + dw_plat_data->disable_cec = true; + dw_plat_data->output_port = 1; + + if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") || + dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxm-dw-hdmi") || + dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-g12a-dw-hdmi")) + dw_plat_data->use_drm_infoframe = true; + + platform_set_drvdata(pdev, meson_dw_hdmi); + + meson_dw_hdmi->hdmi = dw_hdmi_probe(pdev, &meson_dw_hdmi->dw_plat_data); + if (IS_ERR(meson_dw_hdmi->hdmi)) + return PTR_ERR(meson_dw_hdmi->hdmi); + + meson_dw_hdmi->bridge = of_drm_find_bridge(pdev->dev.of_node); + + DRM_DEBUG_DRIVER("HDMI controller initialized\n"); + + return 0; +} + +static void meson_dw_hdmi_unbind(struct device *dev, struct device *master, + void *data) +{ + struct meson_dw_hdmi *meson_dw_hdmi = dev_get_drvdata(dev); + + dw_hdmi_unbind(meson_dw_hdmi->hdmi); +} + +static const struct component_ops meson_dw_hdmi_ops = { + .bind = meson_dw_hdmi_bind, + .unbind = meson_dw_hdmi_unbind, +}; + +static int __maybe_unused meson_dw_hdmi_pm_suspend(struct device *dev) +{ + struct meson_dw_hdmi *meson_dw_hdmi = dev_get_drvdata(dev); + + if (!meson_dw_hdmi) + return 0; + + /* Reset TOP */ + meson_dw_hdmi->data->top_write(meson_dw_hdmi, + HDMITX_TOP_SW_RESET, 0); + + return 0; +} + +static int __maybe_unused meson_dw_hdmi_pm_resume(struct device *dev) +{ + struct meson_dw_hdmi *meson_dw_hdmi = dev_get_drvdata(dev); + + if (!meson_dw_hdmi) + return 0; + + meson_dw_hdmi_init(meson_dw_hdmi); + + dw_hdmi_resume(meson_dw_hdmi->hdmi); + + return 0; +} + +static int meson_dw_hdmi_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &meson_dw_hdmi_ops); +} + +static void meson_dw_hdmi_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &meson_dw_hdmi_ops); +} + +static const struct dev_pm_ops meson_dw_hdmi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(meson_dw_hdmi_pm_suspend, + meson_dw_hdmi_pm_resume) +}; + +static const struct of_device_id meson_dw_hdmi_of_table[] = { + { .compatible = "amlogic,meson-gxbb-dw-hdmi", + .data = &meson_dw_hdmi_gx_data }, + { .compatible = "amlogic,meson-gxl-dw-hdmi", + .data = &meson_dw_hdmi_gx_data }, + { .compatible = "amlogic,meson-gxm-dw-hdmi", + .data = &meson_dw_hdmi_gx_data }, + { .compatible = "amlogic,meson-g12a-dw-hdmi", + .data = &meson_dw_hdmi_g12a_data }, + { } +}; +MODULE_DEVICE_TABLE(of, meson_dw_hdmi_of_table); + +static struct platform_driver meson_dw_hdmi_platform_driver = { + .probe = meson_dw_hdmi_probe, + .remove_new = meson_dw_hdmi_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = meson_dw_hdmi_of_table, + .pm = &meson_dw_hdmi_pm_ops, + }, +}; +module_platform_driver(meson_dw_hdmi_platform_driver); + +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.h b/drivers/gpu/drm/meson/meson_dw_hdmi.h new file mode 100644 index 0000000000..08e1c14e4e --- /dev/null +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#ifndef __MESON_DW_HDMI_H +#define __MESON_DW_HDMI_H + +/* + * Bit 15-10: RW Reserved. Default 1 starting from G12A + * Bit 9 RW sw_reset_i2c starting from G12A + * Bit 8 RW sw_reset_axiarb starting from G12A + * Bit 7 RW Reserved. Default 1, sw_reset_emp starting from G12A + * Bit 6 RW Reserved. Default 1, sw_reset_flt starting from G12A + * Bit 5 RW Reserved. Default 1, sw_reset_hdcp22 starting from G12A + * Bit 4 RW sw_reset_phyif: PHY interface. 1=Apply reset; 0=Release from reset. + * Default 1. + * Bit 3 RW sw_reset_intr: interrupt module. 1=Apply reset; + * 0=Release from reset. + * Default 1. + * Bit 2 RW sw_reset_mem: KSV/REVOC mem. 1=Apply reset; 0=Release from reset. + * Default 1. + * Bit 1 RW sw_reset_rnd: random number interface to HDCP. 1=Apply reset; + * 0=Release from reset. Default 1. + * Bit 0 RW sw_reset_core: connects to IP's ~irstz. 1=Apply reset; + * 0=Release from reset. Default 1. + */ +#define HDMITX_TOP_SW_RESET (0x000) + +/* + * Bit 31 RW free_clk_en: 0=Enable clock gating for power saving; 1= Disable + * Bit 12 RW i2s_ws_inv:1=Invert i2s_ws; 0=No invert. Default 0. + * Bit 11 RW i2s_clk_inv: 1=Invert i2s_clk; 0=No invert. Default 0. + * Bit 10 RW spdif_clk_inv: 1=Invert spdif_clk; 0=No invert. Default 0. + * Bit 9 RW tmds_clk_inv: 1=Invert tmds_clk; 0=No invert. Default 0. + * Bit 8 RW pixel_clk_inv: 1=Invert pixel_clk; 0=No invert. Default 0. + * Bit 7 RW hdcp22_skpclk_en: starting from G12A, 1=enable; 0=disable + * Bit 6 RW hdcp22_esmclk_en: starting from G12A, 1=enable; 0=disable + * Bit 5 RW hdcp22_tmdsclk_en: starting from G12A, 1=enable; 0=disable + * Bit 4 RW cec_clk_en: 1=enable cec_clk; 0=disable. Default 0. Reserved for G12A + * Bit 3 RW i2s_clk_en: 1=enable i2s_clk; 0=disable. Default 0. + * Bit 2 RW spdif_clk_en: 1=enable spdif_clk; 0=disable. Default 0. + * Bit 1 RW tmds_clk_en: 1=enable tmds_clk; 0=disable. Default 0. + * Bit 0 RW pixel_clk_en: 1=enable pixel_clk; 0=disable. Default 0. + */ +#define HDMITX_TOP_CLK_CNTL (0x001) + +/* + * Bit 31:28 RW rxsense_glitch_width: starting from G12A + * Bit 27:16 RW rxsense_valid_width: starting from G12A + * Bit 11: 0 RW hpd_valid_width: filter out width <= M*1024. Default 0. + * Bit 15:12 RW hpd_glitch_width: filter out glitch <= N. Default 0. + */ +#define HDMITX_TOP_HPD_FILTER (0x002) + +/* + * intr_maskn: MASK_N, one bit per interrupt source. + * 1=Enable interrupt source; 0=Disable interrupt source. Default 0. + * [ 7] rxsense_fall starting from G12A + * [ 6] rxsense_rise starting from G12A + * [ 5] err_i2c_timeout starting from G12A + * [ 4] hdcp22_rndnum_err + * [ 3] nonce_rfrsh_rise + * [ 2] hpd_fall_intr + * [ 1] hpd_rise_intr + * [ 0] core_intr + */ +#define HDMITX_TOP_INTR_MASKN (0x003) + +/* + * Bit 30: 0 RW intr_stat: For each bit, write 1 to manually set the interrupt + * bit, read back the interrupt status. + * Bit 31 R IP interrupt status + * Bit 7 RW rxsense_fall starting from G12A + * Bit 6 RW rxsense_rise starting from G12A + * Bit 5 RW err_i2c_timeout starting from G12A + * Bit 2 RW hpd_fall + * Bit 1 RW hpd_rise + * Bit 0 RW IP interrupt + */ +#define HDMITX_TOP_INTR_STAT (0x004) + +/* + * [7] rxsense_fall starting from G12A + * [6] rxsense_rise starting from G12A + * [5] err_i2c_timeout starting from G12A + * [4] hdcp22_rndnum_err + * [3] nonce_rfrsh_rise + * [2] hpd_fall + * [1] hpd_rise + * [0] core_intr_rise + */ +#define HDMITX_TOP_INTR_STAT_CLR (0x005) + +#define HDMITX_TOP_INTR_CORE BIT(0) +#define HDMITX_TOP_INTR_HPD_RISE BIT(1) +#define HDMITX_TOP_INTR_HPD_FALL BIT(2) +#define HDMITX_TOP_INTR_RXSENSE_RISE BIT(6) +#define HDMITX_TOP_INTR_RXSENSE_FALL BIT(7) + +/* + * Bit 14:12 RW tmds_sel: 3'b000=Output zero; 3'b001=Output normal TMDS data; + * 3'b010=Output PRBS data; 3'b100=Output shift pattern. Default 0. + * Bit 11: 9 RW shift_pttn_repeat: 0=New pattern every clk cycle; 1=New pattern + * every 2 clk cycles; ...; 7=New pattern every 8 clk cycles. Default 0. + * Bit 8 RW shift_pttn_en: 1= Enable shift pattern generator; 0=Disable. + * Default 0. + * Bit 4: 3 RW prbs_pttn_mode: 0=PRBS11; 1=PRBS15; 2=PRBS7; 3=PRBS31. Default 0. + * Bit 2: 1 RW prbs_pttn_width: 0=idle; 1=output 8-bit pattern; + * 2=Output 1-bit pattern; 3=output 10-bit pattern. Default 0. + * Bit 0 RW prbs_pttn_en: 1=Enable PRBS generator; 0=Disable. Default 0. + */ +#define HDMITX_TOP_BIST_CNTL (0x006) + +/* Bit 29:20 RW shift_pttn_data[59:50]. Default 0. */ +/* Bit 19:10 RW shift_pttn_data[69:60]. Default 0. */ +/* Bit 9: 0 RW shift_pttn_data[79:70]. Default 0. */ +#define HDMITX_TOP_SHIFT_PTTN_012 (0x007) + +/* Bit 29:20 RW shift_pttn_data[29:20]. Default 0. */ +/* Bit 19:10 RW shift_pttn_data[39:30]. Default 0. */ +/* Bit 9: 0 RW shift_pttn_data[49:40]. Default 0. */ +#define HDMITX_TOP_SHIFT_PTTN_345 (0x008) + +/* Bit 19:10 RW shift_pttn_data[ 9: 0]. Default 0. */ +/* Bit 9: 0 RW shift_pttn_data[19:10]. Default 0. */ +#define HDMITX_TOP_SHIFT_PTTN_67 (0x009) + +/* Bit 25:16 RW tmds_clk_pttn[19:10]. Default 0. */ +/* Bit 9: 0 RW tmds_clk_pttn[ 9: 0]. Default 0. */ +#define HDMITX_TOP_TMDS_CLK_PTTN_01 (0x00A) + +/* Bit 25:16 RW tmds_clk_pttn[39:30]. Default 0. */ +/* Bit 9: 0 RW tmds_clk_pttn[29:20]. Default 0. */ +#define HDMITX_TOP_TMDS_CLK_PTTN_23 (0x00B) + +/* + * Bit 1 RW shift_tmds_clk_pttn:1=Enable shifting clk pattern, + * used when TMDS CLK rate = TMDS character rate /4. Default 0. + * Bit 0 R Reserved. Default 0. + * [ 1] shift_tmds_clk_pttn + * [ 0] load_tmds_clk_pttn + */ +#define HDMITX_TOP_TMDS_CLK_PTTN_CNTL (0x00C) + +/* + * Bit 0 RW revocmem_wr_fail: Read back 1 to indicate Host write REVOC MEM + * failure, write 1 to clear the failure flag. Default 0. + */ +#define HDMITX_TOP_REVOCMEM_STAT (0x00D) + +/* + * Bit 1 R filtered RxSense status + * Bit 0 R filtered HPD status. + */ +#define HDMITX_TOP_STAT0 (0x00E) + +#endif /* __MESON_DW_HDMI_H */ diff --git a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c new file mode 100644 index 0000000000..e5fe4e994f --- /dev/null +++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/phy/phy.h> +#include <linux/bitfield.h> + +#include <video/mipi_display.h> + +#include <drm/bridge/dw_mipi_dsi.h> +#include <drm/drm_mipi_dsi.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_device.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_print.h> + +#include "meson_drv.h" +#include "meson_dw_mipi_dsi.h" +#include "meson_registers.h" +#include "meson_venc.h" + +#define DRIVER_NAME "meson-dw-mipi-dsi" +#define DRIVER_DESC "Amlogic Meson MIPI-DSI DRM driver" + +struct meson_dw_mipi_dsi { + struct meson_drm *priv; + struct device *dev; + void __iomem *base; + struct phy *phy; + union phy_configure_opts phy_opts; + struct dw_mipi_dsi *dmd; + struct dw_mipi_dsi_plat_data pdata; + struct mipi_dsi_device *dsi_device; + const struct drm_display_mode *mode; + struct clk *bit_clk; + struct clk *px_clk; + struct reset_control *top_rst; +}; + +#define encoder_to_meson_dw_mipi_dsi(x) \ + container_of(x, struct meson_dw_mipi_dsi, encoder) + +static void meson_dw_mipi_dsi_hw_init(struct meson_dw_mipi_dsi *mipi_dsi) +{ + /* Software reset */ + writel_bits_relaxed(MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR | + MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING, + MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR | + MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING, + mipi_dsi->base + MIPI_DSI_TOP_SW_RESET); + writel_bits_relaxed(MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR | + MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING, + 0, mipi_dsi->base + MIPI_DSI_TOP_SW_RESET); + + /* Enable clocks */ + writel_bits_relaxed(MIPI_DSI_TOP_CLK_SYSCLK_EN | MIPI_DSI_TOP_CLK_PIXCLK_EN, + MIPI_DSI_TOP_CLK_SYSCLK_EN | MIPI_DSI_TOP_CLK_PIXCLK_EN, + mipi_dsi->base + MIPI_DSI_TOP_CLK_CNTL); + + /* Take memory out of power down */ + writel_relaxed(0, mipi_dsi->base + MIPI_DSI_TOP_MEM_PD); +} + +static int dw_mipi_dsi_phy_init(void *priv_data) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + unsigned int dpi_data_format, venc_data_width; + int ret; + + /* Set the bit clock rate to hs_clk_rate */ + ret = clk_set_rate(mipi_dsi->bit_clk, + mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate); + if (ret) { + dev_err(mipi_dsi->dev, "Failed to set DSI Bit clock rate %lu (ret %d)\n", + mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate, ret); + return ret; + } + + /* Make sure the rate of the bit clock is not modified by someone else */ + ret = clk_rate_exclusive_get(mipi_dsi->bit_clk); + if (ret) { + dev_err(mipi_dsi->dev, + "Failed to set the exclusivity on the bit clock rate (ret %d)\n", ret); + return ret; + } + + ret = clk_set_rate(mipi_dsi->px_clk, mipi_dsi->mode->clock * 1000); + + if (ret) { + dev_err(mipi_dsi->dev, "Failed to set DSI Pixel clock rate %u (%d)\n", + mipi_dsi->mode->clock * 1000, ret); + return ret; + } + + switch (mipi_dsi->dsi_device->format) { + case MIPI_DSI_FMT_RGB888: + dpi_data_format = DPI_COLOR_24BIT; + venc_data_width = VENC_IN_COLOR_24B; + break; + case MIPI_DSI_FMT_RGB666: + dpi_data_format = DPI_COLOR_18BIT_CFG_2; + venc_data_width = VENC_IN_COLOR_18B; + break; + case MIPI_DSI_FMT_RGB666_PACKED: + case MIPI_DSI_FMT_RGB565: + return -EINVAL; + } + + /* Configure color format for DPI register */ + writel_relaxed(FIELD_PREP(MIPI_DSI_TOP_DPI_COLOR_MODE, dpi_data_format) | + FIELD_PREP(MIPI_DSI_TOP_IN_COLOR_MODE, venc_data_width) | + FIELD_PREP(MIPI_DSI_TOP_COMP2_SEL, 2) | + FIELD_PREP(MIPI_DSI_TOP_COMP1_SEL, 1) | + FIELD_PREP(MIPI_DSI_TOP_COMP0_SEL, 0), + mipi_dsi->base + MIPI_DSI_TOP_CNTL); + + return phy_configure(mipi_dsi->phy, &mipi_dsi->phy_opts); +} + +static void dw_mipi_dsi_phy_power_on(void *priv_data) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + + if (phy_power_on(mipi_dsi->phy)) + dev_warn(mipi_dsi->dev, "Failed to power on PHY\n"); +} + +static void dw_mipi_dsi_phy_power_off(void *priv_data) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + + if (phy_power_off(mipi_dsi->phy)) + dev_warn(mipi_dsi->dev, "Failed to power off PHY\n"); + + /* Remove the exclusivity on the bit clock rate */ + clk_rate_exclusive_put(mipi_dsi->bit_clk); +} + +static int +dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, + unsigned long mode_flags, u32 lanes, u32 format, + unsigned int *lane_mbps) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + int bpp; + + mipi_dsi->mode = mode; + + bpp = mipi_dsi_pixel_format_to_bpp(mipi_dsi->dsi_device->format); + + phy_mipi_dphy_get_default_config(mode->clock * 1000, + bpp, mipi_dsi->dsi_device->lanes, + &mipi_dsi->phy_opts.mipi_dphy); + + *lane_mbps = DIV_ROUND_UP(mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate, USEC_PER_SEC); + + return 0; +} + +static int +dw_mipi_dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps, + struct dw_mipi_dsi_dphy_timing *timing) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + + switch (mipi_dsi->mode->hdisplay) { + case 240: + case 768: + case 1920: + case 2560: + timing->clk_lp2hs = 23; + timing->clk_hs2lp = 38; + timing->data_lp2hs = 15; + timing->data_hs2lp = 9; + break; + + default: + timing->clk_lp2hs = 37; + timing->clk_hs2lp = 135; + timing->data_lp2hs = 50; + timing->data_hs2lp = 3; + } + + return 0; +} + +static int +dw_mipi_dsi_get_esc_clk_rate(void *priv_data, unsigned int *esc_clk_rate) +{ + *esc_clk_rate = 4; /* Mhz */ + + return 0; +} + +static const struct dw_mipi_dsi_phy_ops meson_dw_mipi_dsi_phy_ops = { + .init = dw_mipi_dsi_phy_init, + .power_on = dw_mipi_dsi_phy_power_on, + .power_off = dw_mipi_dsi_phy_power_off, + .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, + .get_timing = dw_mipi_dsi_phy_get_timing, + .get_esc_clk_rate = dw_mipi_dsi_get_esc_clk_rate, +}; + +static int meson_dw_mipi_dsi_host_attach(void *priv_data, + struct mipi_dsi_device *device) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + int ret; + + mipi_dsi->dsi_device = device; + + switch (device->format) { + case MIPI_DSI_FMT_RGB888: + break; + case MIPI_DSI_FMT_RGB666: + break; + case MIPI_DSI_FMT_RGB666_PACKED: + case MIPI_DSI_FMT_RGB565: + dev_err(mipi_dsi->dev, "invalid pixel format %d\n", device->format); + return -EINVAL; + } + + ret = phy_init(mipi_dsi->phy); + if (ret) + return ret; + + meson_dw_mipi_dsi_hw_init(mipi_dsi); + + return 0; +} + +static int meson_dw_mipi_dsi_host_detach(void *priv_data, + struct mipi_dsi_device *device) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + + if (device == mipi_dsi->dsi_device) + mipi_dsi->dsi_device = NULL; + else + return -EINVAL; + + return phy_exit(mipi_dsi->phy); +} + +static const struct dw_mipi_dsi_host_ops meson_dw_mipi_dsi_host_ops = { + .attach = meson_dw_mipi_dsi_host_attach, + .detach = meson_dw_mipi_dsi_host_detach, +}; + +static int meson_dw_mipi_dsi_probe(struct platform_device *pdev) +{ + struct meson_dw_mipi_dsi *mipi_dsi; + struct device *dev = &pdev->dev; + + mipi_dsi = devm_kzalloc(dev, sizeof(*mipi_dsi), GFP_KERNEL); + if (!mipi_dsi) + return -ENOMEM; + + mipi_dsi->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mipi_dsi->base)) + return PTR_ERR(mipi_dsi->base); + + mipi_dsi->phy = devm_phy_get(dev, "dphy"); + if (IS_ERR(mipi_dsi->phy)) + return dev_err_probe(dev, PTR_ERR(mipi_dsi->phy), + "failed to get mipi dphy\n"); + + mipi_dsi->bit_clk = devm_clk_get_enabled(dev, "bit"); + if (IS_ERR(mipi_dsi->bit_clk)) { + int ret = PTR_ERR(mipi_dsi->bit_clk); + + /* TOFIX GP0 on some platforms fails to lock in early boot, defer probe */ + if (ret == -EIO) + ret = -EPROBE_DEFER; + + return dev_err_probe(dev, ret, "Unable to get enabled bit_clk\n"); + } + + mipi_dsi->px_clk = devm_clk_get_enabled(dev, "px"); + if (IS_ERR(mipi_dsi->px_clk)) + return dev_err_probe(dev, PTR_ERR(mipi_dsi->px_clk), + "Unable to get enabled px_clk\n"); + + /* + * We use a TOP reset signal because the APB reset signal + * is handled by the TOP control registers. + */ + mipi_dsi->top_rst = devm_reset_control_get_exclusive(dev, "top"); + if (IS_ERR(mipi_dsi->top_rst)) + return dev_err_probe(dev, PTR_ERR(mipi_dsi->top_rst), + "Unable to get reset control\n"); + + reset_control_assert(mipi_dsi->top_rst); + usleep_range(10, 20); + reset_control_deassert(mipi_dsi->top_rst); + + /* MIPI DSI Controller */ + + mipi_dsi->dev = dev; + mipi_dsi->pdata.base = mipi_dsi->base; + mipi_dsi->pdata.max_data_lanes = 4; + mipi_dsi->pdata.phy_ops = &meson_dw_mipi_dsi_phy_ops; + mipi_dsi->pdata.host_ops = &meson_dw_mipi_dsi_host_ops; + mipi_dsi->pdata.priv_data = mipi_dsi; + platform_set_drvdata(pdev, mipi_dsi); + + mipi_dsi->dmd = dw_mipi_dsi_probe(pdev, &mipi_dsi->pdata); + if (IS_ERR(mipi_dsi->dmd)) + return dev_err_probe(dev, PTR_ERR(mipi_dsi->dmd), + "Failed to probe dw_mipi_dsi\n"); + + return 0; +} + +static int meson_dw_mipi_dsi_remove(struct platform_device *pdev) +{ + struct meson_dw_mipi_dsi *mipi_dsi = platform_get_drvdata(pdev); + + dw_mipi_dsi_remove(mipi_dsi->dmd); + + return 0; +} + +static const struct of_device_id meson_dw_mipi_dsi_of_table[] = { + { .compatible = "amlogic,meson-g12a-dw-mipi-dsi", }, + { } +}; +MODULE_DEVICE_TABLE(of, meson_dw_mipi_dsi_of_table); + +static struct platform_driver meson_dw_mipi_dsi_platform_driver = { + .probe = meson_dw_mipi_dsi_probe, + .remove = meson_dw_mipi_dsi_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = meson_dw_mipi_dsi_of_table, + }, +}; +module_platform_driver(meson_dw_mipi_dsi_platform_driver); + +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.h b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.h new file mode 100644 index 0000000000..e1bd6b85d6 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2018 Amlogic, Inc. All rights reserved. + */ + +#ifndef __MESON_DW_MIPI_DSI_H +#define __MESON_DW_MIPI_DSI_H + +/* Top-level registers */ +/* [31: 4] Reserved. Default 0. + * [3] RW timing_rst_n: Default 1. + * 1=Assert SW reset of timing feature. 0=Release reset. + * [2] RW dpi_rst_n: Default 1. + * 1=Assert SW reset on mipi_dsi_host_dpi block. 0=Release reset. + * [1] RW intr_rst_n: Default 1. + * 1=Assert SW reset on mipi_dsi_host_intr block. 0=Release reset. + * [0] RW dwc_rst_n: Default 1. + * 1=Assert SW reset on IP core. 0=Release reset. + */ +#define MIPI_DSI_TOP_SW_RESET 0x3c0 + +#define MIPI_DSI_TOP_SW_RESET_DWC BIT(0) +#define MIPI_DSI_TOP_SW_RESET_INTR BIT(1) +#define MIPI_DSI_TOP_SW_RESET_DPI BIT(2) +#define MIPI_DSI_TOP_SW_RESET_TIMING BIT(3) + +/* [31: 5] Reserved. Default 0. + * [4] RW manual_edpihalt: Default 0. + * 1=Manual suspend VencL; 0=do not suspend VencL. + * [3] RW auto_edpihalt_en: Default 0. + * 1=Enable IP's edpihalt signal to suspend VencL; + * 0=IP's edpihalt signal does not affect VencL. + * [2] RW clock_freerun: Apply to auto-clock gate only. Default 0. + * 0=Default, use auto-clock gating to save power; + * 1=use free-run clock, disable auto-clock gating, for debug mode. + * [1] RW enable_pixclk: A manual clock gate option, due to DWC IP does not + * have auto-clock gating. 1=Enable pixclk. Default 0. + * [0] RW enable_sysclk: A manual clock gate option, due to DWC IP does not + * have auto-clock gating. 1=Enable sysclk. Default 0. + */ +#define MIPI_DSI_TOP_CLK_CNTL 0x3c4 + +#define MIPI_DSI_TOP_CLK_SYSCLK_EN BIT(0) +#define MIPI_DSI_TOP_CLK_PIXCLK_EN BIT(1) + +/* [31:24] Reserved. Default 0. + * [23:20] RW dpi_color_mode: Define DPI pixel format. Default 0. + * 0=16-bit RGB565 config 1; + * 1=16-bit RGB565 config 2; + * 2=16-bit RGB565 config 3; + * 3=18-bit RGB666 config 1; + * 4=18-bit RGB666 config 2; + * 5=24-bit RGB888; + * 6=20-bit YCbCr 4:2:2; + * 7=24-bit YCbCr 4:2:2; + * 8=16-bit YCbCr 4:2:2; + * 9=30-bit RGB; + * 10=36-bit RGB; + * 11=12-bit YCbCr 4:2:0. + * [19] Reserved. Default 0. + * [18:16] RW in_color_mode: Define VENC data width. Default 0. + * 0=30-bit pixel; + * 1=24-bit pixel; + * 2=18-bit pixel, RGB666; + * 3=16-bit pixel, RGB565. + * [15:14] RW chroma_subsample: Define method of chroma subsampling. Default 0. + * Applicable to YUV422 or YUV420 only. + * 0=Use even pixel's chroma; + * 1=Use odd pixel's chroma; + * 2=Use averaged value between even and odd pair. + * [13:12] RW comp2_sel: Select which component to be Cr or B: Default 2. + * 0=comp0; 1=comp1; 2=comp2. + * [11:10] RW comp1_sel: Select which component to be Cb or G: Default 1. + * 0=comp0; 1=comp1; 2=comp2. + * [9: 8] RW comp0_sel: Select which component to be Y or R: Default 0. + * 0=comp0; 1=comp1; 2=comp2. + * [7] Reserved. Default 0. + * [6] RW de_pol: Default 0. + * If DE input is active low, set to 1 to invert to active high. + * [5] RW hsync_pol: Default 0. + * If HS input is active low, set to 1 to invert to active high. + * [4] RW vsync_pol: Default 0. + * If VS input is active low, set to 1 to invert to active high. + * [3] RW dpicolorm: Signal to IP. Default 0. + * [2] RW dpishutdn: Signal to IP. Default 0. + * [1] Reserved. Default 0. + * [0] Reserved. Default 0. + */ +#define MIPI_DSI_TOP_CNTL 0x3c8 + +/* VENC data width */ +#define VENC_IN_COLOR_30B 0x0 +#define VENC_IN_COLOR_24B 0x1 +#define VENC_IN_COLOR_18B 0x2 +#define VENC_IN_COLOR_16B 0x3 + +/* DPI pixel format */ +#define DPI_COLOR_16BIT_CFG_1 0 +#define DPI_COLOR_16BIT_CFG_2 1 +#define DPI_COLOR_16BIT_CFG_3 2 +#define DPI_COLOR_18BIT_CFG_1 3 +#define DPI_COLOR_18BIT_CFG_2 4 +#define DPI_COLOR_24BIT 5 +#define DPI_COLOR_20BIT_YCBCR_422 6 +#define DPI_COLOR_24BIT_YCBCR_422 7 +#define DPI_COLOR_16BIT_YCBCR_422 8 +#define DPI_COLOR_30BIT 9 +#define DPI_COLOR_36BIT 10 +#define DPI_COLOR_12BIT_YCBCR_420 11 + +#define MIPI_DSI_TOP_DPI_COLOR_MODE GENMASK(23, 20) +#define MIPI_DSI_TOP_IN_COLOR_MODE GENMASK(18, 16) +#define MIPI_DSI_TOP_CHROMA_SUBSAMPLE GENMASK(15, 14) +#define MIPI_DSI_TOP_COMP2_SEL GENMASK(13, 12) +#define MIPI_DSI_TOP_COMP1_SEL GENMASK(11, 10) +#define MIPI_DSI_TOP_COMP0_SEL GENMASK(9, 8) +#define MIPI_DSI_TOP_DE_INVERT BIT(6) +#define MIPI_DSI_TOP_HSYNC_INVERT BIT(5) +#define MIPI_DSI_TOP_VSYNC_INVERT BIT(4) +#define MIPI_DSI_TOP_DPICOLORM BIT(3) +#define MIPI_DSI_TOP_DPISHUTDN BIT(2) + +#define MIPI_DSI_TOP_SUSPEND_CNTL 0x3cc +#define MIPI_DSI_TOP_SUSPEND_LINE 0x3d0 +#define MIPI_DSI_TOP_SUSPEND_PIX 0x3d4 +#define MIPI_DSI_TOP_MEAS_CNTL 0x3d8 +/* [0] R stat_edpihalt: edpihalt signal from IP. Default 0. */ +#define MIPI_DSI_TOP_STAT 0x3dc +#define MIPI_DSI_TOP_MEAS_STAT_TE0 0x3e0 +#define MIPI_DSI_TOP_MEAS_STAT_TE1 0x3e4 +#define MIPI_DSI_TOP_MEAS_STAT_VS0 0x3e8 +#define MIPI_DSI_TOP_MEAS_STAT_VS1 0x3ec +/* [31:16] RW intr_stat/clr. Default 0. + * For each bit, read as this interrupt level status, + * write 1 to clear. + * [31:22] Reserved + * [ 21] stat/clr of eof interrupt + * [ 21] vde_fall interrupt + * [ 19] stat/clr of de_rise interrupt + * [ 18] stat/clr of vs_fall interrupt + * [ 17] stat/clr of vs_rise interrupt + * [ 16] stat/clr of dwc_edpite interrupt + * [15: 0] RW intr_enable. Default 0. + * For each bit, 1=enable this interrupt, 0=disable. + * [15: 6] Reserved + * [ 5] eof interrupt + * [ 4] de_fall interrupt + * [ 3] de_rise interrupt + * [ 2] vs_fall interrupt + * [ 1] vs_rise interrupt + * [ 0] dwc_edpite interrupt + */ +#define MIPI_DSI_TOP_INTR_CNTL_STAT 0x3f0 +// 31: 2 Reserved. Default 0. +// 1: 0 RW mem_pd. Default 3. +#define MIPI_DSI_TOP_MEM_PD 0x3f4 + +#endif /* __MESON_DW_MIPI_DSI_H */ diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c new file mode 100644 index 0000000000..3f73b211fa --- /dev/null +++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#include <linux/export.h> +#include <linux/of_graph.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_bridge_connector.h> +#include <drm/drm_device.h> +#include <drm/drm_edid.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> + +#include "meson_registers.h" +#include "meson_vclk.h" +#include "meson_encoder_cvbs.h" + +/* HHI VDAC Registers */ +#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ +#define HHI_VDAC_CNTL0_G12A 0x2EC /* 0xbd offset in data sheet */ +#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ +#define HHI_VDAC_CNTL1_G12A 0x2F0 /* 0xbe offset in data sheet */ + +struct meson_encoder_cvbs { + struct drm_encoder encoder; + struct drm_bridge bridge; + struct drm_bridge *next_bridge; + struct meson_drm *priv; +}; + +#define bridge_to_meson_encoder_cvbs(x) \ + container_of(x, struct meson_encoder_cvbs, bridge) + +/* Supported Modes */ + +struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT] = { + { /* PAL */ + .enci = &meson_cvbs_enci_pal, + .mode = { + DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, + 720, 732, 795, 864, 0, 576, 580, 586, 625, 0, + DRM_MODE_FLAG_INTERLACE), + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, + }, + }, + { /* NTSC */ + .enci = &meson_cvbs_enci_ntsc, + .mode = { + DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, + 720, 739, 801, 858, 0, 480, 488, 494, 525, 0, + DRM_MODE_FLAG_INTERLACE), + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, + }, + }, +}; + +static const struct meson_cvbs_mode * +meson_cvbs_get_mode(const struct drm_display_mode *req_mode) +{ + int i; + + for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) { + struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i]; + + if (drm_mode_match(req_mode, &meson_mode->mode, + DRM_MODE_MATCH_TIMINGS | + DRM_MODE_MATCH_CLOCK | + DRM_MODE_MATCH_FLAGS | + DRM_MODE_MATCH_3D_FLAGS)) + return meson_mode; + } + + return NULL; +} + +static int meson_encoder_cvbs_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct meson_encoder_cvbs *meson_encoder_cvbs = + bridge_to_meson_encoder_cvbs(bridge); + + return drm_bridge_attach(bridge->encoder, meson_encoder_cvbs->next_bridge, + &meson_encoder_cvbs->bridge, flags); +} + +static int meson_encoder_cvbs_get_modes(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct meson_encoder_cvbs *meson_encoder_cvbs = + bridge_to_meson_encoder_cvbs(bridge); + struct meson_drm *priv = meson_encoder_cvbs->priv; + struct drm_display_mode *mode; + int i; + + for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) { + struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i]; + + mode = drm_mode_duplicate(priv->drm, &meson_mode->mode); + if (!mode) { + dev_err(priv->dev, "Failed to create a new display mode\n"); + return 0; + } + + drm_mode_probed_add(connector, mode); + } + + return i; +} + +static enum drm_mode_status +meson_encoder_cvbs_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *display_info, + const struct drm_display_mode *mode) +{ + if (meson_cvbs_get_mode(mode)) + return MODE_OK; + + return MODE_BAD; +} + +static int meson_encoder_cvbs_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + if (meson_cvbs_get_mode(&crtc_state->mode)) + return 0; + + return -EINVAL; +} + +static void meson_encoder_cvbs_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct meson_encoder_cvbs *encoder_cvbs = bridge_to_meson_encoder_cvbs(bridge); + struct drm_atomic_state *state = bridge_state->base.state; + struct meson_drm *priv = encoder_cvbs->priv; + const struct meson_cvbs_mode *meson_mode; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + struct drm_connector *connector; + + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + if (WARN_ON(!connector)) + return; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; + + meson_mode = meson_cvbs_get_mode(&crtc_state->adjusted_mode); + if (WARN_ON(!meson_mode)) + return; + + meson_venci_cvbs_mode_set(priv, meson_mode->enci); + + /* Setup 27MHz vclk2 for ENCI and VDAC */ + meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, + MESON_VCLK_CVBS, MESON_VCLK_CVBS, + MESON_VCLK_CVBS, MESON_VCLK_CVBS, + true); + + /* VDAC0 source is not from ATV */ + writel_bits_relaxed(VENC_VDAC_SEL_ATV_DMD, 0, + priv->io_base + _REG(VENC_VDAC_DACSEL0)); + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) { + regmap_write(priv->hhi, HHI_VDAC_CNTL0, 1); + regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0); + } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) { + regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0xf0001); + regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0); + } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0x906001); + regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0); + } +} + +static void meson_encoder_cvbs_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct meson_encoder_cvbs *meson_encoder_cvbs = + bridge_to_meson_encoder_cvbs(bridge); + struct meson_drm *priv = meson_encoder_cvbs->priv; + + /* Disable CVBS VDAC */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0); + regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0); + } else { + regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0); + regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8); + } +} + +static const struct drm_bridge_funcs meson_encoder_cvbs_bridge_funcs = { + .attach = meson_encoder_cvbs_attach, + .mode_valid = meson_encoder_cvbs_mode_valid, + .get_modes = meson_encoder_cvbs_get_modes, + .atomic_enable = meson_encoder_cvbs_atomic_enable, + .atomic_disable = meson_encoder_cvbs_atomic_disable, + .atomic_check = meson_encoder_cvbs_atomic_check, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, +}; + +int meson_encoder_cvbs_init(struct meson_drm *priv) +{ + struct drm_device *drm = priv->drm; + struct meson_encoder_cvbs *meson_encoder_cvbs; + struct drm_connector *connector; + struct device_node *remote; + int ret; + + meson_encoder_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_encoder_cvbs), GFP_KERNEL); + if (!meson_encoder_cvbs) + return -ENOMEM; + + /* CVBS Connector Bridge */ + remote = of_graph_get_remote_node(priv->dev->of_node, 0, 0); + if (!remote) { + dev_info(drm->dev, "CVBS Output connector not available\n"); + return 0; + } + + meson_encoder_cvbs->next_bridge = of_drm_find_bridge(remote); + of_node_put(remote); + if (!meson_encoder_cvbs->next_bridge) { + dev_err(priv->dev, "Failed to find CVBS Connector bridge\n"); + return -EPROBE_DEFER; + } + + /* CVBS Encoder Bridge */ + meson_encoder_cvbs->bridge.funcs = &meson_encoder_cvbs_bridge_funcs; + meson_encoder_cvbs->bridge.of_node = priv->dev->of_node; + meson_encoder_cvbs->bridge.type = DRM_MODE_CONNECTOR_Composite; + meson_encoder_cvbs->bridge.ops = DRM_BRIDGE_OP_MODES; + meson_encoder_cvbs->bridge.interlace_allowed = true; + + drm_bridge_add(&meson_encoder_cvbs->bridge); + + meson_encoder_cvbs->priv = priv; + + /* Encoder */ + ret = drm_simple_encoder_init(priv->drm, &meson_encoder_cvbs->encoder, + DRM_MODE_ENCODER_TVDAC); + if (ret) { + dev_err(priv->dev, "Failed to init CVBS encoder: %d\n", ret); + return ret; + } + + meson_encoder_cvbs->encoder.possible_crtcs = BIT(0); + + /* Attach CVBS Encoder Bridge to Encoder */ + ret = drm_bridge_attach(&meson_encoder_cvbs->encoder, &meson_encoder_cvbs->bridge, NULL, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) { + dev_err(priv->dev, "Failed to attach bridge: %d\n", ret); + return ret; + } + + /* Initialize & attach Bridge Connector */ + connector = drm_bridge_connector_init(priv->drm, &meson_encoder_cvbs->encoder); + if (IS_ERR(connector)) { + dev_err(priv->dev, "Unable to create CVBS bridge connector\n"); + return PTR_ERR(connector); + } + drm_connector_attach_encoder(connector, &meson_encoder_cvbs->encoder); + + priv->encoders[MESON_ENC_CVBS] = meson_encoder_cvbs; + + return 0; +} + +void meson_encoder_cvbs_remove(struct meson_drm *priv) +{ + struct meson_encoder_cvbs *meson_encoder_cvbs; + + if (priv->encoders[MESON_ENC_CVBS]) { + meson_encoder_cvbs = priv->encoders[MESON_ENC_CVBS]; + drm_bridge_remove(&meson_encoder_cvbs->bridge); + drm_bridge_remove(meson_encoder_cvbs->next_bridge); + } +} diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.h b/drivers/gpu/drm/meson/meson_encoder_cvbs.h new file mode 100644 index 0000000000..09710fec3c --- /dev/null +++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#ifndef __MESON_VENC_CVBS_H +#define __MESON_VENC_CVBS_H + +#include "meson_drv.h" +#include "meson_venc.h" + +struct meson_cvbs_mode { + struct meson_cvbs_enci_mode *enci; + struct drm_display_mode mode; +}; + +#define MESON_CVBS_MODES_COUNT 2 + +/* Modes supported by the CVBS output */ +extern struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT]; + +int meson_encoder_cvbs_init(struct meson_drm *priv); +void meson_encoder_cvbs_remove(struct meson_drm *priv); + +#endif /* __MESON_VENC_CVBS_H */ diff --git a/drivers/gpu/drm/meson/meson_encoder_dsi.c b/drivers/gpu/drm/meson/meson_encoder_dsi.c new file mode 100644 index 0000000000..3f93c70488 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_encoder_dsi.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_graph.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_simple_kms_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_bridge_connector.h> +#include <drm/drm_device.h> +#include <drm/drm_probe_helper.h> + +#include "meson_drv.h" +#include "meson_encoder_dsi.h" +#include "meson_registers.h" +#include "meson_venc.h" +#include "meson_vclk.h" + +struct meson_encoder_dsi { + struct drm_encoder encoder; + struct drm_bridge bridge; + struct drm_bridge *next_bridge; + struct meson_drm *priv; +}; + +#define bridge_to_meson_encoder_dsi(x) \ + container_of(x, struct meson_encoder_dsi, bridge) + +static int meson_encoder_dsi_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct meson_encoder_dsi *encoder_dsi = bridge_to_meson_encoder_dsi(bridge); + + return drm_bridge_attach(bridge->encoder, encoder_dsi->next_bridge, + &encoder_dsi->bridge, flags); +} + +static void meson_encoder_dsi_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct meson_encoder_dsi *encoder_dsi = bridge_to_meson_encoder_dsi(bridge); + struct drm_atomic_state *state = bridge_state->base.state; + struct meson_drm *priv = encoder_dsi->priv; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + struct drm_connector *connector; + + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + if (WARN_ON(!connector)) + return; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; + + /* ENCL clock setup is handled by CCF */ + + meson_venc_mipi_dsi_mode_set(priv, &crtc_state->adjusted_mode); + meson_encl_load_gamma(priv); + + writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN)); + + writel_bits_relaxed(ENCL_VIDEO_MODE_ADV_VFIFO_EN, ENCL_VIDEO_MODE_ADV_VFIFO_EN, + priv->io_base + _REG(ENCL_VIDEO_MODE_ADV)); + writel_relaxed(0, priv->io_base + _REG(ENCL_TST_EN)); + + writel_bits_relaxed(BIT(0), 0, priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL)); + + writel_relaxed(1, priv->io_base + _REG(ENCL_VIDEO_EN)); +} + +static void meson_encoder_dsi_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct meson_encoder_dsi *meson_encoder_dsi = + bridge_to_meson_encoder_dsi(bridge); + struct meson_drm *priv = meson_encoder_dsi->priv; + + writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN)); + + writel_bits_relaxed(BIT(0), BIT(0), priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL)); +} + +static const struct drm_bridge_funcs meson_encoder_dsi_bridge_funcs = { + .attach = meson_encoder_dsi_attach, + .atomic_enable = meson_encoder_dsi_atomic_enable, + .atomic_disable = meson_encoder_dsi_atomic_disable, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, +}; + +int meson_encoder_dsi_init(struct meson_drm *priv) +{ + struct meson_encoder_dsi *meson_encoder_dsi; + struct device_node *remote; + int ret; + + meson_encoder_dsi = devm_kzalloc(priv->dev, sizeof(*meson_encoder_dsi), GFP_KERNEL); + if (!meson_encoder_dsi) + return -ENOMEM; + + /* DSI Transceiver Bridge */ + remote = of_graph_get_remote_node(priv->dev->of_node, 2, 0); + if (!remote) { + dev_err(priv->dev, "DSI transceiver device is disabled"); + return 0; + } + + meson_encoder_dsi->next_bridge = of_drm_find_bridge(remote); + if (!meson_encoder_dsi->next_bridge) { + dev_dbg(priv->dev, "Failed to find DSI transceiver bridge\n"); + return -EPROBE_DEFER; + } + + /* DSI Encoder Bridge */ + meson_encoder_dsi->bridge.funcs = &meson_encoder_dsi_bridge_funcs; + meson_encoder_dsi->bridge.of_node = priv->dev->of_node; + meson_encoder_dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; + + drm_bridge_add(&meson_encoder_dsi->bridge); + + meson_encoder_dsi->priv = priv; + + /* Encoder */ + ret = drm_simple_encoder_init(priv->drm, &meson_encoder_dsi->encoder, + DRM_MODE_ENCODER_DSI); + if (ret) { + dev_err(priv->dev, "Failed to init DSI encoder: %d\n", ret); + return ret; + } + + meson_encoder_dsi->encoder.possible_crtcs = BIT(0); + + /* Attach DSI Encoder Bridge to Encoder */ + ret = drm_bridge_attach(&meson_encoder_dsi->encoder, &meson_encoder_dsi->bridge, NULL, 0); + if (ret) { + dev_err(priv->dev, "Failed to attach bridge: %d\n", ret); + return ret; + } + + /* + * We should have now in place: + * encoder->[dsi encoder bridge]->[dw-mipi-dsi bridge]->[panel bridge]->[panel] + */ + + priv->encoders[MESON_ENC_DSI] = meson_encoder_dsi; + + dev_dbg(priv->dev, "DSI encoder initialized\n"); + + return 0; +} + +void meson_encoder_dsi_remove(struct meson_drm *priv) +{ + struct meson_encoder_dsi *meson_encoder_dsi; + + if (priv->encoders[MESON_ENC_DSI]) { + meson_encoder_dsi = priv->encoders[MESON_ENC_DSI]; + drm_bridge_remove(&meson_encoder_dsi->bridge); + drm_bridge_remove(meson_encoder_dsi->next_bridge); + } +} diff --git a/drivers/gpu/drm/meson/meson_encoder_dsi.h b/drivers/gpu/drm/meson/meson_encoder_dsi.h new file mode 100644 index 0000000000..9277d70151 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_encoder_dsi.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#ifndef __MESON_ENCODER_DSI_H +#define __MESON_ENCODER_DSI_H + +int meson_encoder_dsi_init(struct meson_drm *priv); +void meson_encoder_dsi_remove(struct meson_drm *priv); + +#endif /* __MESON_ENCODER_DSI_H */ diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c new file mode 100644 index 0000000000..25ea765586 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> + +#include <media/cec-notifier.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_bridge_connector.h> +#include <drm/drm_device.h> +#include <drm/drm_edid.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> + +#include <linux/media-bus-format.h> +#include <linux/videodev2.h> + +#include "meson_drv.h" +#include "meson_registers.h" +#include "meson_vclk.h" +#include "meson_venc.h" +#include "meson_encoder_hdmi.h" + +struct meson_encoder_hdmi { + struct drm_encoder encoder; + struct drm_bridge bridge; + struct drm_bridge *next_bridge; + struct drm_connector *connector; + struct meson_drm *priv; + unsigned long output_bus_fmt; + struct cec_notifier *cec_notifier; +}; + +#define bridge_to_meson_encoder_hdmi(x) \ + container_of(x, struct meson_encoder_hdmi, bridge) + +static int meson_encoder_hdmi_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); + + return drm_bridge_attach(bridge->encoder, encoder_hdmi->next_bridge, + &encoder_hdmi->bridge, flags); +} + +static void meson_encoder_hdmi_detach(struct drm_bridge *bridge) +{ + struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); + + cec_notifier_conn_unregister(encoder_hdmi->cec_notifier); + encoder_hdmi->cec_notifier = NULL; +} + +static void meson_encoder_hdmi_set_vclk(struct meson_encoder_hdmi *encoder_hdmi, + const struct drm_display_mode *mode) +{ + struct meson_drm *priv = encoder_hdmi->priv; + int vic = drm_match_cea_mode(mode); + unsigned int phy_freq; + unsigned int vclk_freq; + unsigned int venc_freq; + unsigned int hdmi_freq; + + vclk_freq = mode->clock; + + /* For 420, pixel clock is half unlike venc clock */ + if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) + vclk_freq /= 2; + + /* TMDS clock is pixel_clock * 10 */ + phy_freq = vclk_freq * 10; + + if (!vic) { + meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, phy_freq, + vclk_freq, vclk_freq, vclk_freq, false); + return; + } + + /* 480i/576i needs global pixel doubling */ + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + vclk_freq *= 2; + + venc_freq = vclk_freq; + hdmi_freq = vclk_freq; + + /* VENC double pixels for 1080i, 720p and YUV420 modes */ + if (meson_venc_hdmi_venc_repeat(vic) || + encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) + venc_freq *= 2; + + vclk_freq = max(venc_freq, hdmi_freq); + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + venc_freq /= 2; + + dev_dbg(priv->dev, "vclk:%d phy=%d venc=%d hdmi=%d enci=%d\n", + phy_freq, vclk_freq, venc_freq, hdmi_freq, + priv->venc.hdmi_use_enci); + + meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, phy_freq, vclk_freq, + venc_freq, hdmi_freq, priv->venc.hdmi_use_enci); +} + +static enum drm_mode_status meson_encoder_hdmi_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *display_info, + const struct drm_display_mode *mode) +{ + struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); + struct meson_drm *priv = encoder_hdmi->priv; + bool is_hdmi2_sink = display_info->hdmi.scdc.supported; + unsigned int phy_freq; + unsigned int vclk_freq; + unsigned int venc_freq; + unsigned int hdmi_freq; + int vic = drm_match_cea_mode(mode); + enum drm_mode_status status; + + dev_dbg(priv->dev, "Modeline " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); + + /* If sink does not support 540MHz, reject the non-420 HDMI2 modes */ + if (display_info->max_tmds_clock && + mode->clock > display_info->max_tmds_clock && + !drm_mode_is_420_only(display_info, mode) && + !drm_mode_is_420_also(display_info, mode)) + return MODE_BAD; + + /* Check against non-VIC supported modes */ + if (!vic) { + status = meson_venc_hdmi_supported_mode(mode); + if (status != MODE_OK) + return status; + + return meson_vclk_dmt_supported_freq(priv, mode->clock); + /* Check against supported VIC modes */ + } else if (!meson_venc_hdmi_supported_vic(vic)) + return MODE_BAD; + + vclk_freq = mode->clock; + + /* For 420, pixel clock is half unlike venc clock */ + if (drm_mode_is_420_only(display_info, mode) || + (!is_hdmi2_sink && + drm_mode_is_420_also(display_info, mode))) + vclk_freq /= 2; + + /* TMDS clock is pixel_clock * 10 */ + phy_freq = vclk_freq * 10; + + /* 480i/576i needs global pixel doubling */ + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + vclk_freq *= 2; + + venc_freq = vclk_freq; + hdmi_freq = vclk_freq; + + /* VENC double pixels for 1080i, 720p and YUV420 modes */ + if (meson_venc_hdmi_venc_repeat(vic) || + drm_mode_is_420_only(display_info, mode) || + (!is_hdmi2_sink && + drm_mode_is_420_also(display_info, mode))) + venc_freq *= 2; + + vclk_freq = max(venc_freq, hdmi_freq); + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + venc_freq /= 2; + + dev_dbg(priv->dev, "%s: vclk:%d phy=%d venc=%d hdmi=%d\n", + __func__, phy_freq, vclk_freq, venc_freq, hdmi_freq); + + return meson_vclk_vic_supported_freq(priv, phy_freq, vclk_freq); +} + +static void meson_encoder_hdmi_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); + struct drm_atomic_state *state = bridge_state->base.state; + unsigned int ycrcb_map = VPU_HDMI_OUTPUT_CBYCR; + struct meson_drm *priv = encoder_hdmi->priv; + struct drm_connector_state *conn_state; + const struct drm_display_mode *mode; + struct drm_crtc_state *crtc_state; + struct drm_connector *connector; + bool yuv420_mode = false; + int vic; + + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + if (WARN_ON(!connector)) + return; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; + + mode = &crtc_state->adjusted_mode; + + vic = drm_match_cea_mode(mode); + + dev_dbg(priv->dev, "\"%s\" vic %d\n", mode->name, vic); + + if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) { + ycrcb_map = VPU_HDMI_OUTPUT_CRYCB; + yuv420_mode = true; + } else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYVY8_1X16) + ycrcb_map = VPU_HDMI_OUTPUT_CRYCB; + + /* VENC + VENC-DVI Mode setup */ + meson_venc_hdmi_mode_set(priv, vic, ycrcb_map, yuv420_mode, mode); + + /* VCLK Set clock */ + meson_encoder_hdmi_set_vclk(encoder_hdmi, mode); + + if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) + /* Setup YUV420 to HDMI-TX, no 10bit diphering */ + writel_relaxed(2 | (2 << 2), + priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); + else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYVY8_1X16) + /* Setup YUV422 to HDMI-TX, no 10bit diphering */ + writel_relaxed(1 | (2 << 2), + priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); + else + /* Setup YUV444 to HDMI-TX, no 10bit diphering */ + writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); + + dev_dbg(priv->dev, "%s\n", priv->venc.hdmi_use_enci ? "VENCI" : "VENCP"); + + if (priv->venc.hdmi_use_enci) + writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN)); + else + writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN)); +} + +static void meson_encoder_hdmi_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); + struct meson_drm *priv = encoder_hdmi->priv; + + writel_bits_relaxed(0x3, 0, + priv->io_base + _REG(VPU_HDMI_SETTING)); + + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN)); + writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN)); +} + +static const u32 meson_encoder_hdmi_out_bus_fmts[] = { + MEDIA_BUS_FMT_YUV8_1X24, + MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_UYYVYY8_0_5X24, +}; + +static u32 * +meson_encoder_hdmi_get_inp_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + u32 *input_fmts = NULL; + int i; + + *num_input_fmts = 0; + + for (i = 0 ; i < ARRAY_SIZE(meson_encoder_hdmi_out_bus_fmts) ; ++i) { + if (output_fmt == meson_encoder_hdmi_out_bus_fmts[i]) { + *num_input_fmts = 1; + input_fmts = kcalloc(*num_input_fmts, + sizeof(*input_fmts), + GFP_KERNEL); + if (!input_fmts) + return NULL; + + input_fmts[0] = output_fmt; + + break; + } + } + + return input_fmts; +} + +static int meson_encoder_hdmi_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); + struct drm_connector_state *old_conn_state = + drm_atomic_get_old_connector_state(conn_state->state, conn_state->connector); + struct meson_drm *priv = encoder_hdmi->priv; + + encoder_hdmi->output_bus_fmt = bridge_state->output_bus_cfg.format; + + dev_dbg(priv->dev, "output_bus_fmt %lx\n", encoder_hdmi->output_bus_fmt); + + if (!drm_connector_atomic_hdr_metadata_equal(old_conn_state, conn_state)) + crtc_state->mode_changed = true; + + return 0; +} + +static void meson_encoder_hdmi_hpd_notify(struct drm_bridge *bridge, + enum drm_connector_status status) +{ + struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); + struct edid *edid; + + if (!encoder_hdmi->cec_notifier) + return; + + if (status == connector_status_connected) { + edid = drm_bridge_get_edid(encoder_hdmi->next_bridge, encoder_hdmi->connector); + if (!edid) + return; + + cec_notifier_set_phys_addr_from_edid(encoder_hdmi->cec_notifier, edid); + + kfree(edid); + } else + cec_notifier_phys_addr_invalidate(encoder_hdmi->cec_notifier); +} + +static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = { + .attach = meson_encoder_hdmi_attach, + .detach = meson_encoder_hdmi_detach, + .mode_valid = meson_encoder_hdmi_mode_valid, + .hpd_notify = meson_encoder_hdmi_hpd_notify, + .atomic_enable = meson_encoder_hdmi_atomic_enable, + .atomic_disable = meson_encoder_hdmi_atomic_disable, + .atomic_get_input_bus_fmts = meson_encoder_hdmi_get_inp_bus_fmts, + .atomic_check = meson_encoder_hdmi_atomic_check, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, +}; + +int meson_encoder_hdmi_init(struct meson_drm *priv) +{ + struct meson_encoder_hdmi *meson_encoder_hdmi; + struct platform_device *pdev; + struct device_node *remote; + int ret; + + meson_encoder_hdmi = devm_kzalloc(priv->dev, sizeof(*meson_encoder_hdmi), GFP_KERNEL); + if (!meson_encoder_hdmi) + return -ENOMEM; + + /* HDMI Transceiver Bridge */ + remote = of_graph_get_remote_node(priv->dev->of_node, 1, 0); + if (!remote) { + dev_err(priv->dev, "HDMI transceiver device is disabled"); + return 0; + } + + meson_encoder_hdmi->next_bridge = of_drm_find_bridge(remote); + if (!meson_encoder_hdmi->next_bridge) { + dev_err(priv->dev, "Failed to find HDMI transceiver bridge\n"); + ret = -EPROBE_DEFER; + goto err_put_node; + } + + /* HDMI Encoder Bridge */ + meson_encoder_hdmi->bridge.funcs = &meson_encoder_hdmi_bridge_funcs; + meson_encoder_hdmi->bridge.of_node = priv->dev->of_node; + meson_encoder_hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; + meson_encoder_hdmi->bridge.interlace_allowed = true; + + drm_bridge_add(&meson_encoder_hdmi->bridge); + + meson_encoder_hdmi->priv = priv; + + /* Encoder */ + ret = drm_simple_encoder_init(priv->drm, &meson_encoder_hdmi->encoder, + DRM_MODE_ENCODER_TMDS); + if (ret) { + dev_err(priv->dev, "Failed to init HDMI encoder: %d\n", ret); + goto err_put_node; + } + + meson_encoder_hdmi->encoder.possible_crtcs = BIT(0); + + /* Attach HDMI Encoder Bridge to Encoder */ + ret = drm_bridge_attach(&meson_encoder_hdmi->encoder, &meson_encoder_hdmi->bridge, NULL, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) { + dev_err(priv->dev, "Failed to attach bridge: %d\n", ret); + goto err_put_node; + } + + /* Initialize & attach Bridge Connector */ + meson_encoder_hdmi->connector = drm_bridge_connector_init(priv->drm, + &meson_encoder_hdmi->encoder); + if (IS_ERR(meson_encoder_hdmi->connector)) { + dev_err(priv->dev, "Unable to create HDMI bridge connector\n"); + ret = PTR_ERR(meson_encoder_hdmi->connector); + goto err_put_node; + } + drm_connector_attach_encoder(meson_encoder_hdmi->connector, + &meson_encoder_hdmi->encoder); + + /* + * We should have now in place: + * encoder->[hdmi encoder bridge]->[dw-hdmi bridge]->[display connector bridge]->[display connector] + */ + + /* + * drm_connector_attach_max_bpc_property() requires the + * connector to have a state. + */ + drm_atomic_helper_connector_reset(meson_encoder_hdmi->connector); + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + drm_connector_attach_hdr_output_metadata_property(meson_encoder_hdmi->connector); + + drm_connector_attach_max_bpc_property(meson_encoder_hdmi->connector, 8, 8); + + /* Handle this here until handled by drm_bridge_connector_init() */ + meson_encoder_hdmi->connector->ycbcr_420_allowed = true; + + pdev = of_find_device_by_node(remote); + of_node_put(remote); + if (pdev) { + struct cec_connector_info conn_info; + struct cec_notifier *notifier; + + cec_fill_conn_info_from_drm(&conn_info, meson_encoder_hdmi->connector); + + notifier = cec_notifier_conn_register(&pdev->dev, NULL, &conn_info); + if (!notifier) { + put_device(&pdev->dev); + return -ENOMEM; + } + + meson_encoder_hdmi->cec_notifier = notifier; + } + + priv->encoders[MESON_ENC_HDMI] = meson_encoder_hdmi; + + dev_dbg(priv->dev, "HDMI encoder initialized\n"); + + return 0; + +err_put_node: + of_node_put(remote); + return ret; +} + +void meson_encoder_hdmi_remove(struct meson_drm *priv) +{ + struct meson_encoder_hdmi *meson_encoder_hdmi; + + if (priv->encoders[MESON_ENC_HDMI]) { + meson_encoder_hdmi = priv->encoders[MESON_ENC_HDMI]; + drm_bridge_remove(&meson_encoder_hdmi->bridge); + drm_bridge_remove(meson_encoder_hdmi->next_bridge); + } +} diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.h b/drivers/gpu/drm/meson/meson_encoder_hdmi.h new file mode 100644 index 0000000000..a6cd38eb5f --- /dev/null +++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#ifndef __MESON_ENCODER_HDMI_H +#define __MESON_ENCODER_HDMI_H + +int meson_encoder_hdmi_init(struct meson_drm *priv); +void meson_encoder_hdmi_remove(struct meson_drm *priv); + +#endif /* __MESON_ENCODER_HDMI_H */ diff --git a/drivers/gpu/drm/meson/meson_osd_afbcd.c b/drivers/gpu/drm/meson/meson_osd_afbcd.c new file mode 100644 index 0000000000..0cdbe89940 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_osd_afbcd.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#include <linux/bitfield.h> + +#include <drm/drm_print.h> +#include <drm/drm_fourcc.h> + +#include "meson_drv.h" +#include "meson_registers.h" +#include "meson_viu.h" +#include "meson_rdma.h" +#include "meson_osd_afbcd.h" + +/* + * DOC: Driver for the ARM FrameBuffer Compression Decoders + * + * The Amlogic GXM and G12A SoC families embeds an AFBC Decoder, + * to decode compressed buffers generated by the ARM Mali GPU. + * + * For the GXM Family, Amlogic designed their own Decoder, named in + * the vendor source as "MESON_AFBC", and a single decoder is available + * for the 2 OSD planes. + * This decoder is compatible with the AFBC 1.0 specifications and the + * Mali T820 GPU capabilities. + * It supports : + * - basic AFBC buffer for RGB32 only, thus YTR feature is mandatory + * - SPARSE layout and SPLIT layout + * - only 16x16 superblock + * + * The decoder reads the data from the SDRAM, decodes and sends the + * decoded pixel stream to the OSD1 Plane pixel composer. + * + * For the G12A Family, Amlogic integrated an ARM AFBC Decoder, named + * in the vendor source as "MALI_AFBC", and the decoder can decode up + * to 4 surfaces, one for each of the 4 available OSDs. + * This decoder is compatible with the AFBC 1.2 specifications for the + * Mali G31 and G52 GPUs. + * Is supports : + * - basic AFBC buffer for multiple RGB and YUV pixel formats + * - SPARSE layout and SPLIT layout + * - 16x16 and 32x8 "wideblk" superblocks + * - Tiled header + * + * The ARM AFBC Decoder independent from the VPU Pixel Pipeline, so + * the ARM AFBC Decoder reads the data from the SDRAM then decodes + * into a private internal physical address where the OSD1 Plane pixel + * composer unpacks the decoded data. + */ + +/* Amlogic AFBC Decoder for GXM Family */ + +#define OSD1_AFBCD_RGB32 0x15 + +static int meson_gxm_afbcd_pixel_fmt(u64 modifier, uint32_t format) +{ + switch (format) { + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return OSD1_AFBCD_RGB32; + /* TOFIX support mode formats */ + default: + DRM_DEBUG("unsupported afbc format[%08x]\n", format); + return -EINVAL; + } +} + +static bool meson_gxm_afbcd_supported_fmt(u64 modifier, uint32_t format) +{ + if (modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) + return false; + + if (!(modifier & AFBC_FORMAT_MOD_YTR)) + return false; + + return meson_gxm_afbcd_pixel_fmt(modifier, format) >= 0; +} + +static int meson_gxm_afbcd_reset(struct meson_drm *priv) +{ + writel_relaxed(VIU_SW_RESET_OSD1_AFBCD, + priv->io_base + _REG(VIU_SW_RESET)); + writel_relaxed(0, priv->io_base + _REG(VIU_SW_RESET)); + + return 0; +} + +static int meson_gxm_afbcd_init(struct meson_drm *priv) +{ + return 0; +} + +static void meson_gxm_afbcd_exit(struct meson_drm *priv) +{ + meson_gxm_afbcd_reset(priv); +} + +static int meson_gxm_afbcd_enable(struct meson_drm *priv) +{ + writel_relaxed(FIELD_PREP(OSD1_AFBCD_ID_FIFO_THRD, 0x40) | + OSD1_AFBCD_DEC_ENABLE, + priv->io_base + _REG(OSD1_AFBCD_ENABLE)); + + return 0; +} + +static int meson_gxm_afbcd_disable(struct meson_drm *priv) +{ + writel_bits_relaxed(OSD1_AFBCD_DEC_ENABLE, 0, + priv->io_base + _REG(OSD1_AFBCD_ENABLE)); + + return 0; +} + +static int meson_gxm_afbcd_setup(struct meson_drm *priv) +{ + u32 conv_lbuf_len; + u32 mode = FIELD_PREP(OSD1_AFBCD_MIF_URGENT, 3) | + FIELD_PREP(OSD1_AFBCD_HOLD_LINE_NUM, 4) | + FIELD_PREP(OSD1_AFBCD_RGBA_EXCHAN_CTRL, 0x34) | + meson_gxm_afbcd_pixel_fmt(priv->afbcd.modifier, + priv->afbcd.format); + + if (priv->afbcd.modifier & AFBC_FORMAT_MOD_SPARSE) + mode |= OSD1_AFBCD_HREG_HALF_BLOCK; + + if (priv->afbcd.modifier & AFBC_FORMAT_MOD_SPLIT) + mode |= OSD1_AFBCD_HREG_BLOCK_SPLIT; + + writel_relaxed(mode, priv->io_base + _REG(OSD1_AFBCD_MODE)); + + writel_relaxed(FIELD_PREP(OSD1_AFBCD_HREG_VSIZE_IN, + priv->viu.osd1_width) | + FIELD_PREP(OSD1_AFBCD_HREG_HSIZE_IN, + priv->viu.osd1_height), + priv->io_base + _REG(OSD1_AFBCD_SIZE_IN)); + + writel_relaxed(priv->viu.osd1_addr >> 4, + priv->io_base + _REG(OSD1_AFBCD_HDR_PTR)); + writel_relaxed(priv->viu.osd1_addr >> 4, + priv->io_base + _REG(OSD1_AFBCD_FRAME_PTR)); + /* TOFIX: bits 31:24 are not documented, nor the meaning of 0xe4 */ + writel_relaxed((0xe4 << 24) | (priv->viu.osd1_addr & 0xffffff), + priv->io_base + _REG(OSD1_AFBCD_CHROMA_PTR)); + + if (priv->viu.osd1_width <= 128) + conv_lbuf_len = 32; + else if (priv->viu.osd1_width <= 256) + conv_lbuf_len = 64; + else if (priv->viu.osd1_width <= 512) + conv_lbuf_len = 128; + else if (priv->viu.osd1_width <= 1024) + conv_lbuf_len = 256; + else if (priv->viu.osd1_width <= 2048) + conv_lbuf_len = 512; + else + conv_lbuf_len = 1024; + + writel_relaxed(conv_lbuf_len, + priv->io_base + _REG(OSD1_AFBCD_CONV_CTRL)); + + writel_relaxed(FIELD_PREP(OSD1_AFBCD_DEC_PIXEL_BGN_H, 0) | + FIELD_PREP(OSD1_AFBCD_DEC_PIXEL_END_H, + priv->viu.osd1_width - 1), + priv->io_base + _REG(OSD1_AFBCD_PIXEL_HSCOPE)); + + writel_relaxed(FIELD_PREP(OSD1_AFBCD_DEC_PIXEL_BGN_V, 0) | + FIELD_PREP(OSD1_AFBCD_DEC_PIXEL_END_V, + priv->viu.osd1_height - 1), + priv->io_base + _REG(OSD1_AFBCD_PIXEL_VSCOPE)); + + return 0; +} + +struct meson_afbcd_ops meson_afbcd_gxm_ops = { + .init = meson_gxm_afbcd_init, + .exit = meson_gxm_afbcd_exit, + .reset = meson_gxm_afbcd_reset, + .enable = meson_gxm_afbcd_enable, + .disable = meson_gxm_afbcd_disable, + .setup = meson_gxm_afbcd_setup, + .supported_fmt = meson_gxm_afbcd_supported_fmt, +}; + +/* ARM AFBC Decoder for G12A Family */ + +/* Amlogic G12A Mali AFBC Decoder supported formats */ +enum { + MAFBC_FMT_RGB565 = 0, + MAFBC_FMT_RGBA5551, + MAFBC_FMT_RGBA1010102, + MAFBC_FMT_YUV420_10B, + MAFBC_FMT_RGB888, + MAFBC_FMT_RGBA8888, + MAFBC_FMT_RGBA4444, + MAFBC_FMT_R8, + MAFBC_FMT_RG88, + MAFBC_FMT_YUV420_8B, + MAFBC_FMT_YUV422_8B = 11, + MAFBC_FMT_YUV422_10B = 14, +}; + +static int meson_g12a_afbcd_pixel_fmt(u64 modifier, uint32_t format) +{ + switch (format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + /* YTR is forbidden for non XBGR formats */ + if (modifier & AFBC_FORMAT_MOD_YTR) + return -EINVAL; + fallthrough; + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return MAFBC_FMT_RGBA8888; + case DRM_FORMAT_RGB888: + /* YTR is forbidden for non XBGR formats */ + if (modifier & AFBC_FORMAT_MOD_YTR) + return -EINVAL; + return MAFBC_FMT_RGB888; + case DRM_FORMAT_RGB565: + /* YTR is forbidden for non XBGR formats */ + if (modifier & AFBC_FORMAT_MOD_YTR) + return -EINVAL; + return MAFBC_FMT_RGB565; + /* TOFIX support mode formats */ + default: + DRM_DEBUG("unsupported afbc format[%08x]\n", format); + return -EINVAL; + } +} + +static int meson_g12a_afbcd_bpp(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return 32; + case DRM_FORMAT_RGB888: + return 24; + case DRM_FORMAT_RGB565: + return 16; + /* TOFIX support mode formats */ + default: + DRM_ERROR("unsupported afbc format[%08x]\n", format); + return 0; + } +} + +static int meson_g12a_afbcd_fmt_to_blk_mode(u64 modifier, uint32_t format) +{ + switch (format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return OSD_MALI_COLOR_MODE_RGBA8888; + case DRM_FORMAT_RGB888: + return OSD_MALI_COLOR_MODE_RGB888; + case DRM_FORMAT_RGB565: + return OSD_MALI_COLOR_MODE_RGB565; + /* TOFIX support mode formats */ + default: + DRM_DEBUG("unsupported afbc format[%08x]\n", format); + return -EINVAL; + } +} + +static bool meson_g12a_afbcd_supported_fmt(u64 modifier, uint32_t format) +{ + return meson_g12a_afbcd_pixel_fmt(modifier, format) >= 0; +} + +static int meson_g12a_afbcd_reset(struct meson_drm *priv) +{ + meson_rdma_reset(priv); + + meson_rdma_writel_sync(priv, VIU_SW_RESET_G12A_AFBC_ARB | + VIU_SW_RESET_G12A_OSD1_AFBCD, + VIU_SW_RESET); + meson_rdma_writel_sync(priv, 0, VIU_SW_RESET); + + return 0; +} + +static int meson_g12a_afbcd_init(struct meson_drm *priv) +{ + int ret; + + ret = meson_rdma_init(priv); + if (ret) + return ret; + + meson_rdma_setup(priv); + + /* Handle AFBC Decoder reset manually */ + writel_bits_relaxed(MALI_AFBCD_MANUAL_RESET, MALI_AFBCD_MANUAL_RESET, + priv->io_base + _REG(MALI_AFBCD_TOP_CTRL)); + + return 0; +} + +static void meson_g12a_afbcd_exit(struct meson_drm *priv) +{ + meson_g12a_afbcd_reset(priv); + meson_rdma_free(priv); +} + +static int meson_g12a_afbcd_enable(struct meson_drm *priv) +{ + meson_rdma_writel_sync(priv, VPU_MAFBC_IRQ_SURFACES_COMPLETED | + VPU_MAFBC_IRQ_CONFIGURATION_SWAPPED | + VPU_MAFBC_IRQ_DECODE_ERROR | + VPU_MAFBC_IRQ_DETILING_ERROR, + VPU_MAFBC_IRQ_MASK); + + meson_rdma_writel_sync(priv, VPU_MAFBC_S0_ENABLE, + VPU_MAFBC_SURFACE_CFG); + + meson_rdma_writel_sync(priv, VPU_MAFBC_DIRECT_SWAP, + VPU_MAFBC_COMMAND); + + /* This will enable the RDMA replaying the register writes on vsync */ + meson_rdma_flush(priv); + + return 0; +} + +static int meson_g12a_afbcd_disable(struct meson_drm *priv) +{ + writel_bits_relaxed(VPU_MAFBC_S0_ENABLE, 0, + priv->io_base + _REG(VPU_MAFBC_SURFACE_CFG)); + + return 0; +} + +static int meson_g12a_afbcd_setup(struct meson_drm *priv) +{ + u32 format = meson_g12a_afbcd_pixel_fmt(priv->afbcd.modifier, + priv->afbcd.format); + + if (priv->afbcd.modifier & AFBC_FORMAT_MOD_YTR) + format |= VPU_MAFBC_YUV_TRANSFORM; + + if (priv->afbcd.modifier & AFBC_FORMAT_MOD_SPLIT) + format |= VPU_MAFBC_BLOCK_SPLIT; + + if (priv->afbcd.modifier & AFBC_FORMAT_MOD_TILED) + format |= VPU_MAFBC_TILED_HEADER_EN; + + if ((priv->afbcd.modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) == + AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) + format |= FIELD_PREP(VPU_MAFBC_SUPER_BLOCK_ASPECT, 1); + + meson_rdma_writel_sync(priv, format, + VPU_MAFBC_FORMAT_SPECIFIER_S0); + + meson_rdma_writel_sync(priv, priv->viu.osd1_addr, + VPU_MAFBC_HEADER_BUF_ADDR_LOW_S0); + meson_rdma_writel_sync(priv, 0, + VPU_MAFBC_HEADER_BUF_ADDR_HIGH_S0); + + meson_rdma_writel_sync(priv, priv->viu.osd1_width, + VPU_MAFBC_BUFFER_WIDTH_S0); + meson_rdma_writel_sync(priv, ALIGN(priv->viu.osd1_height, 32), + VPU_MAFBC_BUFFER_HEIGHT_S0); + + meson_rdma_writel_sync(priv, 0, + VPU_MAFBC_BOUNDING_BOX_X_START_S0); + meson_rdma_writel_sync(priv, priv->viu.osd1_width - 1, + VPU_MAFBC_BOUNDING_BOX_X_END_S0); + meson_rdma_writel_sync(priv, 0, + VPU_MAFBC_BOUNDING_BOX_Y_START_S0); + meson_rdma_writel_sync(priv, priv->viu.osd1_height - 1, + VPU_MAFBC_BOUNDING_BOX_Y_END_S0); + + meson_rdma_writel_sync(priv, MESON_G12A_AFBCD_OUT_ADDR, + VPU_MAFBC_OUTPUT_BUF_ADDR_LOW_S0); + meson_rdma_writel_sync(priv, 0, + VPU_MAFBC_OUTPUT_BUF_ADDR_HIGH_S0); + + meson_rdma_writel_sync(priv, priv->viu.osd1_width * + (meson_g12a_afbcd_bpp(priv->afbcd.format) / 8), + VPU_MAFBC_OUTPUT_BUF_STRIDE_S0); + + return 0; +} + +struct meson_afbcd_ops meson_afbcd_g12a_ops = { + .init = meson_g12a_afbcd_init, + .exit = meson_g12a_afbcd_exit, + .reset = meson_g12a_afbcd_reset, + .enable = meson_g12a_afbcd_enable, + .disable = meson_g12a_afbcd_disable, + .setup = meson_g12a_afbcd_setup, + .fmt_to_blk_mode = meson_g12a_afbcd_fmt_to_blk_mode, + .supported_fmt = meson_g12a_afbcd_supported_fmt, +}; diff --git a/drivers/gpu/drm/meson/meson_osd_afbcd.h b/drivers/gpu/drm/meson/meson_osd_afbcd.h new file mode 100644 index 0000000000..e77ddeb641 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_osd_afbcd.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#ifndef __MESON_OSD_AFBCD_H +#define __MESON_OSD_AFBCD_H + +#include "meson_drv.h" + +/* This is an internal address used to transfer pixel from AFBC to the VIU */ +#define MESON_G12A_AFBCD_OUT_ADDR 0x1000000 + +struct meson_afbcd_ops { + int (*init)(struct meson_drm *priv); + void (*exit)(struct meson_drm *priv); + int (*reset)(struct meson_drm *priv); + int (*enable)(struct meson_drm *priv); + int (*disable)(struct meson_drm *priv); + int (*setup)(struct meson_drm *priv); + int (*fmt_to_blk_mode)(u64 modifier, uint32_t format); + bool (*supported_fmt)(u64 modifier, uint32_t format); +}; + +extern struct meson_afbcd_ops meson_afbcd_gxm_ops; +extern struct meson_afbcd_ops meson_afbcd_g12a_ops; + +#endif /* __MESON_OSD_AFBCD_H */ diff --git a/drivers/gpu/drm/meson/meson_overlay.c b/drivers/gpu/drm/meson/meson_overlay.c new file mode 100644 index 0000000000..7f98de3884 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_overlay.c @@ -0,0 +1,866 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#include <linux/bitfield.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> +#include <drm/drm_device.h> +#include <drm/drm_fb_dma_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_gem_dma_helper.h> + +#include "meson_overlay.h" +#include "meson_registers.h" +#include "meson_viu.h" +#include "meson_vpp.h" + +/* VD1_IF0_GEN_REG */ +#define VD_URGENT_CHROMA BIT(28) +#define VD_URGENT_LUMA BIT(27) +#define VD_HOLD_LINES(lines) FIELD_PREP(GENMASK(24, 19), lines) +#define VD_DEMUX_MODE_RGB BIT(16) +#define VD_BYTES_PER_PIXEL(val) FIELD_PREP(GENMASK(15, 14), val) +#define VD_CHRO_RPT_LASTL_CTRL BIT(6) +#define VD_LITTLE_ENDIAN BIT(4) +#define VD_SEPARATE_EN BIT(1) +#define VD_ENABLE BIT(0) + +/* VD1_IF0_CANVAS0 */ +#define CANVAS_ADDR2(addr) FIELD_PREP(GENMASK(23, 16), addr) +#define CANVAS_ADDR1(addr) FIELD_PREP(GENMASK(15, 8), addr) +#define CANVAS_ADDR0(addr) FIELD_PREP(GENMASK(7, 0), addr) + +/* VD1_IF0_LUMA_X0 VD1_IF0_CHROMA_X0 */ +#define VD_X_START(value) FIELD_PREP(GENMASK(14, 0), value) +#define VD_X_END(value) FIELD_PREP(GENMASK(30, 16), value) + +/* VD1_IF0_LUMA_Y0 VD1_IF0_CHROMA_Y0 */ +#define VD_Y_START(value) FIELD_PREP(GENMASK(12, 0), value) +#define VD_Y_END(value) FIELD_PREP(GENMASK(28, 16), value) + +/* VD1_IF0_GEN_REG2 */ +#define VD_COLOR_MAP(value) FIELD_PREP(GENMASK(1, 0), value) + +/* VIU_VD1_FMT_CTRL */ +#define VD_HORZ_Y_C_RATIO(value) FIELD_PREP(GENMASK(22, 21), value) +#define VD_HORZ_FMT_EN BIT(20) +#define VD_VERT_RPT_LINE0 BIT(16) +#define VD_VERT_INITIAL_PHASE(value) FIELD_PREP(GENMASK(11, 8), value) +#define VD_VERT_PHASE_STEP(value) FIELD_PREP(GENMASK(7, 1), value) +#define VD_VERT_FMT_EN BIT(0) + +/* VPP_POSTBLEND_VD1_H_START_END */ +#define VD_H_END(value) FIELD_PREP(GENMASK(11, 0), value) +#define VD_H_START(value) FIELD_PREP(GENMASK(27, 16), \ + ((value) & GENMASK(13, 0))) + +/* VPP_POSTBLEND_VD1_V_START_END */ +#define VD_V_END(value) FIELD_PREP(GENMASK(11, 0), value) +#define VD_V_START(value) FIELD_PREP(GENMASK(27, 16), value) + +/* VPP_BLEND_VD2_V_START_END */ +#define VD2_V_END(value) FIELD_PREP(GENMASK(11, 0), value) +#define VD2_V_START(value) FIELD_PREP(GENMASK(27, 16), value) + +/* VIU_VD1_FMT_W */ +#define VD_V_WIDTH(value) FIELD_PREP(GENMASK(11, 0), value) +#define VD_H_WIDTH(value) FIELD_PREP(GENMASK(27, 16), value) + +/* VPP_HSC_REGION12_STARTP VPP_HSC_REGION34_STARTP */ +#define VD_REGION24_START(value) FIELD_PREP(GENMASK(11, 0), value) +#define VD_REGION13_END(value) FIELD_PREP(GENMASK(27, 16), value) + +/* AFBC_ENABLE */ +#define AFBC_DEC_ENABLE BIT(8) +#define AFBC_FRM_START BIT(0) + +/* AFBC_MODE */ +#define AFBC_HORZ_SKIP_UV(value) FIELD_PREP(GENMASK(1, 0), value) +#define AFBC_VERT_SKIP_UV(value) FIELD_PREP(GENMASK(3, 2), value) +#define AFBC_HORZ_SKIP_Y(value) FIELD_PREP(GENMASK(5, 4), value) +#define AFBC_VERT_SKIP_Y(value) FIELD_PREP(GENMASK(7, 6), value) +#define AFBC_COMPBITS_YUV(value) FIELD_PREP(GENMASK(13, 8), value) +#define AFBC_COMPBITS_8BIT 0 +#define AFBC_COMPBITS_10BIT (2 | (2 << 2) | (2 << 4)) +#define AFBC_BURST_LEN(value) FIELD_PREP(GENMASK(15, 14), value) +#define AFBC_HOLD_LINE_NUM(value) FIELD_PREP(GENMASK(22, 16), value) +#define AFBC_MIF_URGENT(value) FIELD_PREP(GENMASK(25, 24), value) +#define AFBC_REV_MODE(value) FIELD_PREP(GENMASK(27, 26), value) +#define AFBC_BLK_MEM_MODE BIT(28) +#define AFBC_SCATTER_MODE BIT(29) +#define AFBC_SOFT_RESET BIT(31) + +/* AFBC_SIZE_IN */ +#define AFBC_HSIZE_IN(value) FIELD_PREP(GENMASK(28, 16), value) +#define AFBC_VSIZE_IN(value) FIELD_PREP(GENMASK(12, 0), value) + +/* AFBC_DEC_DEF_COLOR */ +#define AFBC_DEF_COLOR_Y(value) FIELD_PREP(GENMASK(29, 20), value) +#define AFBC_DEF_COLOR_U(value) FIELD_PREP(GENMASK(19, 10), value) +#define AFBC_DEF_COLOR_V(value) FIELD_PREP(GENMASK(9, 0), value) + +/* AFBC_CONV_CTRL */ +#define AFBC_CONV_LBUF_LEN(value) FIELD_PREP(GENMASK(11, 0), value) + +/* AFBC_LBUF_DEPTH */ +#define AFBC_DEC_LBUF_DEPTH(value) FIELD_PREP(GENMASK(27, 16), value) +#define AFBC_MIF_LBUF_DEPTH(value) FIELD_PREP(GENMASK(11, 0), value) + +/* AFBC_OUT_XSCOPE/AFBC_SIZE_OUT */ +#define AFBC_HSIZE_OUT(value) FIELD_PREP(GENMASK(28, 16), value) +#define AFBC_VSIZE_OUT(value) FIELD_PREP(GENMASK(12, 0), value) +#define AFBC_OUT_HORZ_BGN(value) FIELD_PREP(GENMASK(28, 16), value) +#define AFBC_OUT_HORZ_END(value) FIELD_PREP(GENMASK(12, 0), value) + +/* AFBC_OUT_YSCOPE */ +#define AFBC_OUT_VERT_BGN(value) FIELD_PREP(GENMASK(28, 16), value) +#define AFBC_OUT_VERT_END(value) FIELD_PREP(GENMASK(12, 0), value) + +/* AFBC_VD_CFMT_CTRL */ +#define AFBC_HORZ_RPT_PIXEL0 BIT(23) +#define AFBC_HORZ_Y_C_RATIO(value) FIELD_PREP(GENMASK(22, 21), value) +#define AFBC_HORZ_FMT_EN BIT(20) +#define AFBC_VERT_RPT_LINE0 BIT(16) +#define AFBC_VERT_INITIAL_PHASE(value) FIELD_PREP(GENMASK(11, 8), value) +#define AFBC_VERT_PHASE_STEP(value) FIELD_PREP(GENMASK(7, 1), value) +#define AFBC_VERT_FMT_EN BIT(0) + +/* AFBC_VD_CFMT_W */ +#define AFBC_VD_V_WIDTH(value) FIELD_PREP(GENMASK(11, 0), value) +#define AFBC_VD_H_WIDTH(value) FIELD_PREP(GENMASK(27, 16), value) + +/* AFBC_MIF_HOR_SCOPE */ +#define AFBC_MIF_BLK_BGN_H(value) FIELD_PREP(GENMASK(25, 16), value) +#define AFBC_MIF_BLK_END_H(value) FIELD_PREP(GENMASK(9, 0), value) + +/* AFBC_MIF_VER_SCOPE */ +#define AFBC_MIF_BLK_BGN_V(value) FIELD_PREP(GENMASK(27, 16), value) +#define AFBC_MIF_BLK_END_V(value) FIELD_PREP(GENMASK(11, 0), value) + +/* AFBC_PIXEL_HOR_SCOPE */ +#define AFBC_DEC_PIXEL_BGN_H(value) FIELD_PREP(GENMASK(28, 16), \ + ((value) & GENMASK(12, 0))) +#define AFBC_DEC_PIXEL_END_H(value) FIELD_PREP(GENMASK(12, 0), value) + +/* AFBC_PIXEL_VER_SCOPE */ +#define AFBC_DEC_PIXEL_BGN_V(value) FIELD_PREP(GENMASK(28, 16), value) +#define AFBC_DEC_PIXEL_END_V(value) FIELD_PREP(GENMASK(12, 0), value) + +/* AFBC_VD_CFMT_H */ +#define AFBC_VD_HEIGHT(value) FIELD_PREP(GENMASK(12, 0), value) + +struct meson_overlay { + struct drm_plane base; + struct meson_drm *priv; +}; +#define to_meson_overlay(x) container_of(x, struct meson_overlay, base) + +#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) + +static int meson_overlay_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_crtc_state *crtc_state; + + if (!new_plane_state->crtc) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state, + new_plane_state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + return drm_atomic_helper_check_plane_state(new_plane_state, + crtc_state, + FRAC_16_16(1, 5), + FRAC_16_16(5, 1), + true, true); +} + +/* Takes a fixed 16.16 number and converts it to integer. */ +static inline int64_t fixed16_to_int(int64_t value) +{ + return value >> 16; +} + +static const uint8_t skip_tab[6] = { + 0x24, 0x04, 0x68, 0x48, 0x28, 0x08, +}; + +static void meson_overlay_get_vertical_phase(unsigned int ratio_y, int *phase, + int *repeat, bool interlace) +{ + int offset_in = 0; + int offset_out = 0; + int repeat_skip = 0; + + if (!interlace && ratio_y > (1 << 18)) + offset_out = (1 * ratio_y) >> 10; + + while ((offset_in + (4 << 8)) <= offset_out) { + repeat_skip++; + offset_in += 4 << 8; + } + + *phase = (offset_out - offset_in) >> 2; + + if (*phase > 0x100) + repeat_skip++; + + *phase = *phase & 0xff; + + if (repeat_skip > 5) + repeat_skip = 5; + + *repeat = skip_tab[repeat_skip]; +} + +static void meson_overlay_setup_scaler_params(struct meson_drm *priv, + struct drm_plane *plane, + bool interlace_mode) +{ + struct drm_crtc_state *crtc_state = priv->crtc->state; + int video_top, video_left, video_width, video_height; + struct drm_plane_state *state = plane->state; + unsigned int vd_start_lines, vd_end_lines; + unsigned int hd_start_lines, hd_end_lines; + unsigned int crtc_height, crtc_width; + unsigned int vsc_startp, vsc_endp; + unsigned int hsc_startp, hsc_endp; + unsigned int crop_top, crop_left; + int vphase, vphase_repeat_skip; + unsigned int ratio_x, ratio_y; + int temp_height, temp_width; + unsigned int w_in, h_in; + int afbc_left, afbc_right; + int afbc_top_src, afbc_bottom_src; + int afbc_top, afbc_bottom; + int temp, start, end; + + if (!crtc_state) { + DRM_ERROR("Invalid crtc_state\n"); + return; + } + + crtc_height = crtc_state->mode.vdisplay; + crtc_width = crtc_state->mode.hdisplay; + + w_in = fixed16_to_int(state->src_w); + h_in = fixed16_to_int(state->src_h); + crop_top = fixed16_to_int(state->src_y); + crop_left = fixed16_to_int(state->src_x); + + video_top = state->crtc_y; + video_left = state->crtc_x; + video_width = state->crtc_w; + video_height = state->crtc_h; + + DRM_DEBUG("crtc_width %d crtc_height %d interlace %d\n", + crtc_width, crtc_height, interlace_mode); + DRM_DEBUG("w_in %d h_in %d crop_top %d crop_left %d\n", + w_in, h_in, crop_top, crop_left); + DRM_DEBUG("video top %d left %d width %d height %d\n", + video_top, video_left, video_width, video_height); + + ratio_x = (w_in << 18) / video_width; + ratio_y = (h_in << 18) / video_height; + + if (ratio_x * video_width < (w_in << 18)) + ratio_x++; + + DRM_DEBUG("ratio x 0x%x y 0x%x\n", ratio_x, ratio_y); + + meson_overlay_get_vertical_phase(ratio_y, &vphase, &vphase_repeat_skip, + interlace_mode); + + DRM_DEBUG("vphase 0x%x skip %d\n", vphase, vphase_repeat_skip); + + /* Vertical */ + + start = video_top + video_height / 2 - ((h_in << 17) / ratio_y); + end = (h_in << 18) / ratio_y + start - 1; + + if (video_top < 0 && start < 0) + vd_start_lines = (-(start) * ratio_y) >> 18; + else if (start < video_top) + vd_start_lines = ((video_top - start) * ratio_y) >> 18; + else + vd_start_lines = 0; + + if (video_top < 0) + temp_height = min_t(unsigned int, + video_top + video_height - 1, + crtc_height - 1); + else + temp_height = min_t(unsigned int, + video_top + video_height - 1, + crtc_height - 1) - video_top + 1; + + temp = vd_start_lines + (temp_height * ratio_y >> 18); + vd_end_lines = (temp <= (h_in - 1)) ? temp : (h_in - 1); + + vd_start_lines += crop_left; + vd_end_lines += crop_left; + + /* + * TOFIX: Input frames are handled and scaled like progressive frames, + * proper handling of interlaced field input frames need to be figured + * out using the proper framebuffer flags set by userspace. + */ + if (interlace_mode) { + start >>= 1; + end >>= 1; + } + + vsc_startp = max_t(int, start, + max_t(int, 0, video_top)); + vsc_endp = min_t(int, end, + min_t(int, crtc_height - 1, + video_top + video_height - 1)); + + DRM_DEBUG("vsc startp %d endp %d start_lines %d end_lines %d\n", + vsc_startp, vsc_endp, vd_start_lines, vd_end_lines); + + afbc_top = round_down(vd_start_lines, 4); + afbc_bottom = round_up(vd_end_lines + 1, 4); + afbc_top_src = 0; + afbc_bottom_src = round_up(h_in + 1, 4); + + DRM_DEBUG("afbc top %d (src %d) bottom %d (src %d)\n", + afbc_top, afbc_top_src, afbc_bottom, afbc_bottom_src); + + /* Horizontal */ + + start = video_left + video_width / 2 - ((w_in << 17) / ratio_x); + end = (w_in << 18) / ratio_x + start - 1; + + if (video_left < 0 && start < 0) + hd_start_lines = (-(start) * ratio_x) >> 18; + else if (start < video_left) + hd_start_lines = ((video_left - start) * ratio_x) >> 18; + else + hd_start_lines = 0; + + if (video_left < 0) + temp_width = min_t(unsigned int, + video_left + video_width - 1, + crtc_width - 1); + else + temp_width = min_t(unsigned int, + video_left + video_width - 1, + crtc_width - 1) - video_left + 1; + + temp = hd_start_lines + (temp_width * ratio_x >> 18); + hd_end_lines = (temp <= (w_in - 1)) ? temp : (w_in - 1); + + priv->viu.vpp_line_in_length = hd_end_lines - hd_start_lines + 1; + hsc_startp = max_t(int, start, max_t(int, 0, video_left)); + hsc_endp = min_t(int, end, min_t(int, crtc_width - 1, + video_left + video_width - 1)); + + hd_start_lines += crop_top; + hd_end_lines += crop_top; + + DRM_DEBUG("hsc startp %d endp %d start_lines %d end_lines %d\n", + hsc_startp, hsc_endp, hd_start_lines, hd_end_lines); + + if (hd_start_lines > 0 || (hd_end_lines < w_in)) { + afbc_left = 0; + afbc_right = round_up(w_in, 32); + } else { + afbc_left = round_down(hd_start_lines, 32); + afbc_right = round_up(hd_end_lines + 1, 32); + } + + DRM_DEBUG("afbc left %d right %d\n", afbc_left, afbc_right); + + priv->viu.vpp_vsc_start_phase_step = ratio_y << 6; + + priv->viu.vpp_vsc_ini_phase = vphase << 8; + priv->viu.vpp_vsc_phase_ctrl = (1 << 13) | (4 << 8) | + vphase_repeat_skip; + + priv->viu.vd1_if0_luma_x0 = VD_X_START(hd_start_lines) | + VD_X_END(hd_end_lines); + priv->viu.vd1_if0_chroma_x0 = VD_X_START(hd_start_lines >> 1) | + VD_X_END(hd_end_lines >> 1); + + priv->viu.viu_vd1_fmt_w = + VD_H_WIDTH(hd_end_lines - hd_start_lines + 1) | + VD_V_WIDTH(hd_end_lines/2 - hd_start_lines/2 + 1); + + priv->viu.vd1_afbc_vd_cfmt_w = + AFBC_VD_H_WIDTH(afbc_right - afbc_left) | + AFBC_VD_V_WIDTH(afbc_right / 2 - afbc_left / 2); + + priv->viu.vd1_afbc_vd_cfmt_h = + AFBC_VD_HEIGHT((afbc_bottom - afbc_top) / 2); + + priv->viu.vd1_afbc_mif_hor_scope = AFBC_MIF_BLK_BGN_H(afbc_left / 32) | + AFBC_MIF_BLK_END_H((afbc_right / 32) - 1); + + priv->viu.vd1_afbc_mif_ver_scope = AFBC_MIF_BLK_BGN_V(afbc_top / 4) | + AFBC_MIF_BLK_END_H((afbc_bottom / 4) - 1); + + priv->viu.vd1_afbc_size_out = + AFBC_HSIZE_OUT(afbc_right - afbc_left) | + AFBC_VSIZE_OUT(afbc_bottom - afbc_top); + + priv->viu.vd1_afbc_pixel_hor_scope = + AFBC_DEC_PIXEL_BGN_H(hd_start_lines - afbc_left) | + AFBC_DEC_PIXEL_END_H(hd_end_lines - afbc_left); + + priv->viu.vd1_afbc_pixel_ver_scope = + AFBC_DEC_PIXEL_BGN_V(vd_start_lines - afbc_top) | + AFBC_DEC_PIXEL_END_V(vd_end_lines - afbc_top); + + priv->viu.vd1_afbc_size_in = + AFBC_HSIZE_IN(afbc_right - afbc_left) | + AFBC_VSIZE_IN(afbc_bottom_src - afbc_top_src); + + priv->viu.vd1_if0_luma_y0 = VD_Y_START(vd_start_lines) | + VD_Y_END(vd_end_lines); + + priv->viu.vd1_if0_chroma_y0 = VD_Y_START(vd_start_lines >> 1) | + VD_Y_END(vd_end_lines >> 1); + + priv->viu.vpp_pic_in_height = h_in; + + priv->viu.vpp_postblend_vd1_h_start_end = VD_H_START(hsc_startp) | + VD_H_END(hsc_endp); + priv->viu.vpp_blend_vd2_h_start_end = VD_H_START(hd_start_lines) | + VD_H_END(hd_end_lines); + priv->viu.vpp_hsc_region12_startp = VD_REGION13_END(0) | + VD_REGION24_START(hsc_startp); + priv->viu.vpp_hsc_region34_startp = + VD_REGION13_END(hsc_startp) | + VD_REGION24_START(hsc_endp - hsc_startp); + priv->viu.vpp_hsc_region4_endp = hsc_endp - hsc_startp; + priv->viu.vpp_hsc_start_phase_step = ratio_x << 6; + priv->viu.vpp_hsc_region1_phase_slope = 0; + priv->viu.vpp_hsc_region3_phase_slope = 0; + priv->viu.vpp_hsc_phase_ctrl = (1 << 21) | (4 << 16); + + priv->viu.vpp_line_in_length = hd_end_lines - hd_start_lines + 1; + priv->viu.vpp_preblend_h_size = hd_end_lines - hd_start_lines + 1; + + priv->viu.vpp_postblend_vd1_v_start_end = VD_V_START(vsc_startp) | + VD_V_END(vsc_endp); + priv->viu.vpp_blend_vd2_v_start_end = + VD2_V_START((vd_end_lines + 1) >> 1) | + VD2_V_END(vd_end_lines); + + priv->viu.vpp_vsc_region12_startp = 0; + priv->viu.vpp_vsc_region34_startp = + VD_REGION13_END(vsc_endp - vsc_startp) | + VD_REGION24_START(vsc_endp - vsc_startp); + priv->viu.vpp_vsc_region4_endp = vsc_endp - vsc_startp; + priv->viu.vpp_vsc_start_phase_step = ratio_y << 6; +} + +static void meson_overlay_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct meson_overlay *meson_overlay = to_meson_overlay(plane); + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_framebuffer *fb = new_state->fb; + struct meson_drm *priv = meson_overlay->priv; + struct drm_gem_dma_object *gem; + unsigned long flags; + bool interlace_mode; + + DRM_DEBUG_DRIVER("\n"); + + interlace_mode = new_state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE; + + spin_lock_irqsave(&priv->drm->event_lock, flags); + + if ((fb->modifier & DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) == + DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) { + priv->viu.vd1_afbc = true; + + priv->viu.vd1_afbc_mode = AFBC_MIF_URGENT(3) | + AFBC_HOLD_LINE_NUM(8) | + AFBC_BURST_LEN(2); + + if (fb->modifier & DRM_FORMAT_MOD_AMLOGIC_FBC(0, + AMLOGIC_FBC_OPTION_MEM_SAVING)) + priv->viu.vd1_afbc_mode |= AFBC_BLK_MEM_MODE; + + if ((fb->modifier & __fourcc_mod_amlogic_layout_mask) == + AMLOGIC_FBC_LAYOUT_SCATTER) + priv->viu.vd1_afbc_mode |= AFBC_SCATTER_MODE; + + priv->viu.vd1_afbc_en = 0x1600 | AFBC_DEC_ENABLE; + + priv->viu.vd1_afbc_conv_ctrl = AFBC_CONV_LBUF_LEN(256); + + priv->viu.vd1_afbc_dec_def_color = AFBC_DEF_COLOR_Y(1023); + + /* 420: horizontal / 2, vertical / 4 */ + priv->viu.vd1_afbc_vd_cfmt_ctrl = AFBC_HORZ_RPT_PIXEL0 | + AFBC_HORZ_Y_C_RATIO(1) | + AFBC_HORZ_FMT_EN | + AFBC_VERT_RPT_LINE0 | + AFBC_VERT_INITIAL_PHASE(12) | + AFBC_VERT_PHASE_STEP(8) | + AFBC_VERT_FMT_EN; + + switch (fb->format->format) { + /* AFBC Only formats */ + case DRM_FORMAT_YUV420_10BIT: + priv->viu.vd1_afbc_mode |= + AFBC_COMPBITS_YUV(AFBC_COMPBITS_10BIT); + priv->viu.vd1_afbc_dec_def_color |= + AFBC_DEF_COLOR_U(512) | + AFBC_DEF_COLOR_V(512); + break; + case DRM_FORMAT_YUV420_8BIT: + priv->viu.vd1_afbc_dec_def_color |= + AFBC_DEF_COLOR_U(128) | + AFBC_DEF_COLOR_V(128); + break; + } + + priv->viu.vd1_if0_gen_reg = 0; + priv->viu.vd1_if0_canvas0 = 0; + priv->viu.viu_vd1_fmt_ctrl = 0; + } else { + priv->viu.vd1_afbc = false; + + priv->viu.vd1_if0_gen_reg = VD_URGENT_CHROMA | + VD_URGENT_LUMA | + VD_HOLD_LINES(9) | + VD_CHRO_RPT_LASTL_CTRL | + VD_ENABLE; + } + + /* Setup scaler params */ + meson_overlay_setup_scaler_params(priv, plane, interlace_mode); + + priv->viu.vd1_if0_repeat_loop = 0; + priv->viu.vd1_if0_luma0_rpt_pat = interlace_mode ? 8 : 0; + priv->viu.vd1_if0_chroma0_rpt_pat = interlace_mode ? 8 : 0; + priv->viu.vd1_range_map_y = 0; + priv->viu.vd1_range_map_cb = 0; + priv->viu.vd1_range_map_cr = 0; + + /* Default values for RGB888/YUV444 */ + priv->viu.vd1_if0_gen_reg2 = 0; + priv->viu.viu_vd1_fmt_ctrl = 0; + + /* None will match for AFBC Only formats */ + switch (fb->format->format) { + /* TOFIX DRM_FORMAT_RGB888 should be supported */ + case DRM_FORMAT_YUYV: + priv->viu.vd1_if0_gen_reg |= VD_BYTES_PER_PIXEL(1); + priv->viu.vd1_if0_canvas0 = + CANVAS_ADDR2(priv->canvas_id_vd1_0) | + CANVAS_ADDR1(priv->canvas_id_vd1_0) | + CANVAS_ADDR0(priv->canvas_id_vd1_0); + priv->viu.viu_vd1_fmt_ctrl = VD_HORZ_Y_C_RATIO(1) | /* /2 */ + VD_HORZ_FMT_EN | + VD_VERT_RPT_LINE0 | + VD_VERT_INITIAL_PHASE(12) | + VD_VERT_PHASE_STEP(16) | /* /2 */ + VD_VERT_FMT_EN; + break; + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + priv->viu.vd1_if0_gen_reg |= VD_SEPARATE_EN; + priv->viu.vd1_if0_canvas0 = + CANVAS_ADDR2(priv->canvas_id_vd1_1) | + CANVAS_ADDR1(priv->canvas_id_vd1_1) | + CANVAS_ADDR0(priv->canvas_id_vd1_0); + if (fb->format->format == DRM_FORMAT_NV12) + priv->viu.vd1_if0_gen_reg2 = VD_COLOR_MAP(1); + else + priv->viu.vd1_if0_gen_reg2 = VD_COLOR_MAP(2); + priv->viu.viu_vd1_fmt_ctrl = VD_HORZ_Y_C_RATIO(1) | /* /2 */ + VD_HORZ_FMT_EN | + VD_VERT_RPT_LINE0 | + VD_VERT_INITIAL_PHASE(12) | + VD_VERT_PHASE_STEP(8) | /* /4 */ + VD_VERT_FMT_EN; + break; + case DRM_FORMAT_YUV444: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YUV411: + case DRM_FORMAT_YUV410: + priv->viu.vd1_if0_gen_reg |= VD_SEPARATE_EN; + priv->viu.vd1_if0_canvas0 = + CANVAS_ADDR2(priv->canvas_id_vd1_2) | + CANVAS_ADDR1(priv->canvas_id_vd1_1) | + CANVAS_ADDR0(priv->canvas_id_vd1_0); + switch (fb->format->format) { + case DRM_FORMAT_YUV422: + priv->viu.viu_vd1_fmt_ctrl = + VD_HORZ_Y_C_RATIO(1) | /* /2 */ + VD_HORZ_FMT_EN | + VD_VERT_RPT_LINE0 | + VD_VERT_INITIAL_PHASE(12) | + VD_VERT_PHASE_STEP(16) | /* /2 */ + VD_VERT_FMT_EN; + break; + case DRM_FORMAT_YUV420: + priv->viu.viu_vd1_fmt_ctrl = + VD_HORZ_Y_C_RATIO(1) | /* /2 */ + VD_HORZ_FMT_EN | + VD_VERT_RPT_LINE0 | + VD_VERT_INITIAL_PHASE(12) | + VD_VERT_PHASE_STEP(8) | /* /4 */ + VD_VERT_FMT_EN; + break; + case DRM_FORMAT_YUV411: + priv->viu.viu_vd1_fmt_ctrl = + VD_HORZ_Y_C_RATIO(2) | /* /4 */ + VD_HORZ_FMT_EN | + VD_VERT_RPT_LINE0 | + VD_VERT_INITIAL_PHASE(12) | + VD_VERT_PHASE_STEP(16) | /* /2 */ + VD_VERT_FMT_EN; + break; + case DRM_FORMAT_YUV410: + priv->viu.viu_vd1_fmt_ctrl = + VD_HORZ_Y_C_RATIO(2) | /* /4 */ + VD_HORZ_FMT_EN | + VD_VERT_RPT_LINE0 | + VD_VERT_INITIAL_PHASE(12) | + VD_VERT_PHASE_STEP(8) | /* /4 */ + VD_VERT_FMT_EN; + break; + } + break; + } + + /* Update Canvas with buffer address */ + priv->viu.vd1_planes = fb->format->num_planes; + + switch (priv->viu.vd1_planes) { + case 3: + gem = drm_fb_dma_get_gem_obj(fb, 2); + priv->viu.vd1_addr2 = gem->dma_addr + fb->offsets[2]; + priv->viu.vd1_stride2 = fb->pitches[2]; + priv->viu.vd1_height2 = + drm_format_info_plane_height(fb->format, + fb->height, 2); + DRM_DEBUG("plane 2 addr 0x%x stride %d height %d\n", + priv->viu.vd1_addr2, + priv->viu.vd1_stride2, + priv->viu.vd1_height2); + fallthrough; + case 2: + gem = drm_fb_dma_get_gem_obj(fb, 1); + priv->viu.vd1_addr1 = gem->dma_addr + fb->offsets[1]; + priv->viu.vd1_stride1 = fb->pitches[1]; + priv->viu.vd1_height1 = + drm_format_info_plane_height(fb->format, + fb->height, 1); + DRM_DEBUG("plane 1 addr 0x%x stride %d height %d\n", + priv->viu.vd1_addr1, + priv->viu.vd1_stride1, + priv->viu.vd1_height1); + fallthrough; + case 1: + gem = drm_fb_dma_get_gem_obj(fb, 0); + priv->viu.vd1_addr0 = gem->dma_addr + fb->offsets[0]; + priv->viu.vd1_stride0 = fb->pitches[0]; + priv->viu.vd1_height0 = + drm_format_info_plane_height(fb->format, + fb->height, 0); + DRM_DEBUG("plane 0 addr 0x%x stride %d height %d\n", + priv->viu.vd1_addr0, + priv->viu.vd1_stride0, + priv->viu.vd1_height0); + } + + if (priv->viu.vd1_afbc) { + if (priv->viu.vd1_afbc_mode & AFBC_SCATTER_MODE) { + /* + * In Scatter mode, the header contains the physical + * body content layout, thus the body content + * size isn't needed. + */ + priv->viu.vd1_afbc_head_addr = priv->viu.vd1_addr0 >> 4; + priv->viu.vd1_afbc_body_addr = 0; + } else { + /* Default mode is 4k per superblock */ + unsigned long block_size = 4096; + unsigned long body_size; + + /* 8bit mem saving mode is 3072bytes per superblock */ + if (priv->viu.vd1_afbc_mode & AFBC_BLK_MEM_MODE) + block_size = 3072; + + body_size = (ALIGN(priv->viu.vd1_stride0, 64) / 64) * + (ALIGN(priv->viu.vd1_height0, 32) / 32) * + block_size; + + priv->viu.vd1_afbc_body_addr = priv->viu.vd1_addr0 >> 4; + /* Header is after body content */ + priv->viu.vd1_afbc_head_addr = (priv->viu.vd1_addr0 + + body_size) >> 4; + } + } + + priv->viu.vd1_enabled = true; + + spin_unlock_irqrestore(&priv->drm->event_lock, flags); + + DRM_DEBUG_DRIVER("\n"); +} + +static void meson_overlay_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct meson_overlay *meson_overlay = to_meson_overlay(plane); + struct meson_drm *priv = meson_overlay->priv; + + DRM_DEBUG_DRIVER("\n"); + + priv->viu.vd1_enabled = false; + + /* Disable VD1 */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + writel_relaxed(0, priv->io_base + _REG(VD1_BLEND_SRC_CTRL)); + writel_relaxed(0, priv->io_base + _REG(VD2_BLEND_SRC_CTRL)); + writel_relaxed(0, priv->io_base + _REG(VD1_IF0_GEN_REG + 0x17b0)); + writel_relaxed(0, priv->io_base + _REG(VD2_IF0_GEN_REG + 0x17b0)); + } else + writel_bits_relaxed(VPP_VD1_POSTBLEND | VPP_VD1_PREBLEND, 0, + priv->io_base + _REG(VPP_MISC)); + +} + +static const struct drm_plane_helper_funcs meson_overlay_helper_funcs = { + .atomic_check = meson_overlay_atomic_check, + .atomic_disable = meson_overlay_atomic_disable, + .atomic_update = meson_overlay_atomic_update, +}; + +static bool meson_overlay_format_mod_supported(struct drm_plane *plane, + u32 format, u64 modifier) +{ + if (modifier == DRM_FORMAT_MOD_LINEAR && + format != DRM_FORMAT_YUV420_8BIT && + format != DRM_FORMAT_YUV420_10BIT) + return true; + + if ((modifier & DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) == + DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) { + unsigned int layout = modifier & + DRM_FORMAT_MOD_AMLOGIC_FBC( + __fourcc_mod_amlogic_layout_mask, 0); + unsigned int options = + (modifier >> __fourcc_mod_amlogic_options_shift) & + __fourcc_mod_amlogic_options_mask; + + if (format != DRM_FORMAT_YUV420_8BIT && + format != DRM_FORMAT_YUV420_10BIT) { + DRM_DEBUG_KMS("%llx invalid format 0x%08x\n", + modifier, format); + return false; + } + + if (layout != AMLOGIC_FBC_LAYOUT_BASIC && + layout != AMLOGIC_FBC_LAYOUT_SCATTER) { + DRM_DEBUG_KMS("%llx invalid layout %x\n", + modifier, layout); + return false; + } + + if (options && + options != AMLOGIC_FBC_OPTION_MEM_SAVING) { + DRM_DEBUG_KMS("%llx invalid layout %x\n", + modifier, layout); + return false; + } + + return true; + } + + DRM_DEBUG_KMS("invalid modifier %llx for format 0x%08x\n", + modifier, format); + + return false; +} + +static const struct drm_plane_funcs meson_overlay_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .format_mod_supported = meson_overlay_format_mod_supported, +}; + +static const uint32_t supported_drm_formats[] = { + DRM_FORMAT_YUYV, + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, + DRM_FORMAT_YUV444, + DRM_FORMAT_YUV422, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV411, + DRM_FORMAT_YUV410, + DRM_FORMAT_YUV420_8BIT, /* Amlogic FBC Only */ + DRM_FORMAT_YUV420_10BIT, /* Amlogic FBC Only */ +}; + +static const uint64_t format_modifiers[] = { + DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_SCATTER, + AMLOGIC_FBC_OPTION_MEM_SAVING), + DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_BASIC, + AMLOGIC_FBC_OPTION_MEM_SAVING), + DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_SCATTER, 0), + DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_BASIC, 0), + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID, +}; + +int meson_overlay_create(struct meson_drm *priv) +{ + struct meson_overlay *meson_overlay; + struct drm_plane *plane; + + DRM_DEBUG_DRIVER("\n"); + + meson_overlay = devm_kzalloc(priv->drm->dev, sizeof(*meson_overlay), + GFP_KERNEL); + if (!meson_overlay) + return -ENOMEM; + + meson_overlay->priv = priv; + plane = &meson_overlay->base; + + drm_universal_plane_init(priv->drm, plane, 0xFF, + &meson_overlay_funcs, + supported_drm_formats, + ARRAY_SIZE(supported_drm_formats), + format_modifiers, + DRM_PLANE_TYPE_OVERLAY, "meson_overlay_plane"); + + drm_plane_helper_add(plane, &meson_overlay_helper_funcs); + + /* For now, VD Overlay plane is always on the back */ + drm_plane_create_zpos_immutable_property(plane, 0); + + priv->overlay_plane = plane; + + DRM_DEBUG_DRIVER("\n"); + + return 0; +} diff --git a/drivers/gpu/drm/meson/meson_overlay.h b/drivers/gpu/drm/meson/meson_overlay.h new file mode 100644 index 0000000000..dae24f5ac6 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_overlay.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#ifndef __MESON_OVERLAY_H +#define __MESON_OVERLAY_H + +#include "meson_drv.h" + +int meson_overlay_create(struct meson_drm *priv); + +#endif /* __MESON_OVERLAY_H */ diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c new file mode 100644 index 0000000000..815dfe3049 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_plane.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#include <linux/bitfield.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> +#include <drm/drm_device.h> +#include <drm/drm_fb_dma_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_gem_dma_helper.h> + +#include "meson_plane.h" +#include "meson_registers.h" +#include "meson_viu.h" +#include "meson_osd_afbcd.h" + +/* OSD_SCI_WH_M1 */ +#define SCI_WH_M1_W(w) FIELD_PREP(GENMASK(28, 16), w) +#define SCI_WH_M1_H(h) FIELD_PREP(GENMASK(12, 0), h) + +/* OSD_SCO_H_START_END */ +/* OSD_SCO_V_START_END */ +#define SCO_HV_START(start) FIELD_PREP(GENMASK(27, 16), start) +#define SCO_HV_END(end) FIELD_PREP(GENMASK(11, 0), end) + +/* OSD_SC_CTRL0 */ +#define SC_CTRL0_PATH_EN BIT(3) +#define SC_CTRL0_SEL_OSD1 BIT(2) + +/* OSD_VSC_CTRL0 */ +#define VSC_BANK_LEN(value) FIELD_PREP(GENMASK(2, 0), value) +#define VSC_TOP_INI_RCV_NUM(value) FIELD_PREP(GENMASK(6, 3), value) +#define VSC_TOP_RPT_L0_NUM(value) FIELD_PREP(GENMASK(9, 8), value) +#define VSC_BOT_INI_RCV_NUM(value) FIELD_PREP(GENMASK(14, 11), value) +#define VSC_BOT_RPT_L0_NUM(value) FIELD_PREP(GENMASK(17, 16), value) +#define VSC_PROG_INTERLACE BIT(23) +#define VSC_VERTICAL_SCALER_EN BIT(24) + +/* OSD_VSC_INI_PHASE */ +#define VSC_INI_PHASE_BOT(bottom) FIELD_PREP(GENMASK(31, 16), bottom) +#define VSC_INI_PHASE_TOP(top) FIELD_PREP(GENMASK(15, 0), top) + +/* OSD_HSC_CTRL0 */ +#define HSC_BANK_LENGTH(value) FIELD_PREP(GENMASK(2, 0), value) +#define HSC_INI_RCV_NUM0(value) FIELD_PREP(GENMASK(6, 3), value) +#define HSC_RPT_P0_NUM0(value) FIELD_PREP(GENMASK(9, 8), value) +#define HSC_HORIZ_SCALER_EN BIT(22) + +/* VPP_OSD_VSC_PHASE_STEP */ +/* VPP_OSD_HSC_PHASE_STEP */ +#define SC_PHASE_STEP(value) FIELD_PREP(GENMASK(27, 0), value) + +struct meson_plane { + struct drm_plane base; + struct meson_drm *priv; + bool enabled; +}; +#define to_meson_plane(x) container_of(x, struct meson_plane, base) + +#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) + +static int meson_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_crtc_state *crtc_state; + + if (!new_plane_state->crtc) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state, + new_plane_state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + /* + * Only allow : + * - Upscaling up to 5x, vertical and horizontal + * - Final coordinates must match crtc size + */ + return drm_atomic_helper_check_plane_state(new_plane_state, + crtc_state, + FRAC_16_16(1, 5), + DRM_PLANE_NO_SCALING, + false, true); +} + +#define MESON_MOD_AFBC_VALID_BITS (AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | \ + AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | \ + AFBC_FORMAT_MOD_YTR | \ + AFBC_FORMAT_MOD_SPARSE | \ + AFBC_FORMAT_MOD_SPLIT) + +/* Takes a fixed 16.16 number and converts it to integer. */ +static inline int64_t fixed16_to_int(int64_t value) +{ + return value >> 16; +} + +static u32 meson_g12a_afbcd_line_stride(struct meson_drm *priv) +{ + u32 line_stride = 0; + + switch (priv->afbcd.format) { + case DRM_FORMAT_RGB565: + line_stride = ((priv->viu.osd1_width << 4) + 127) >> 7; + break; + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + line_stride = ((priv->viu.osd1_width << 5) + 127) >> 7; + break; + } + + return ((line_stride + 1) >> 1) << 1; +} + +static void meson_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct meson_plane *meson_plane = to_meson_plane(plane); + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_rect dest = drm_plane_state_dest(new_state); + struct meson_drm *priv = meson_plane->priv; + struct drm_framebuffer *fb = new_state->fb; + struct drm_gem_dma_object *gem; + unsigned long flags; + int vsc_ini_rcv_num, vsc_ini_rpt_p0_num; + int vsc_bot_rcv_num, vsc_bot_rpt_p0_num; + int hsc_ini_rcv_num, hsc_ini_rpt_p0_num; + int hf_phase_step, vf_phase_step; + int src_w, src_h, dst_w, dst_h; + int bot_ini_phase; + int hf_bank_len; + int vf_bank_len; + u8 canvas_id_osd1; + + /* + * Update Coordinates + * Update Formats + * Update Buffer + * Enable Plane + */ + spin_lock_irqsave(&priv->drm->event_lock, flags); + + /* Check if AFBC decoder is required for this buffer */ + if ((meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) && + fb->modifier & DRM_FORMAT_MOD_ARM_AFBC(MESON_MOD_AFBC_VALID_BITS)) + priv->viu.osd1_afbcd = true; + else + priv->viu.osd1_afbcd = false; + + /* Enable OSD and BLK0, set max global alpha */ + priv->viu.osd1_ctrl_stat = OSD_ENABLE | + (0x100 << OSD_GLOBAL_ALPHA_SHIFT) | + OSD_BLK0_ENABLE; + + priv->viu.osd1_ctrl_stat2 = readl(priv->io_base + + _REG(VIU_OSD1_CTRL_STAT2)); + + canvas_id_osd1 = priv->canvas_id_osd1; + + /* Set up BLK0 to point to the right canvas */ + priv->viu.osd1_blk0_cfg[0] = canvas_id_osd1 << OSD_CANVAS_SEL; + + if (priv->viu.osd1_afbcd) { + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + /* This is the internal decoding memory address */ + priv->viu.osd1_blk1_cfg4 = MESON_G12A_AFBCD_OUT_ADDR; + priv->viu.osd1_blk0_cfg[0] |= OSD_ENDIANNESS_BE; + priv->viu.osd1_ctrl_stat2 |= OSD_PENDING_STAT_CLEAN; + priv->viu.osd1_ctrl_stat |= VIU_OSD1_CFG_SYN_EN; + } + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM)) { + priv->viu.osd1_blk0_cfg[0] |= OSD_ENDIANNESS_LE; + priv->viu.osd1_ctrl_stat2 |= OSD_DPATH_MALI_AFBCD; + } + } else { + priv->viu.osd1_blk0_cfg[0] |= OSD_ENDIANNESS_LE; + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM)) + priv->viu.osd1_ctrl_stat2 &= ~OSD_DPATH_MALI_AFBCD; + } + + /* On GXBB, Use the old non-HDR RGB2YUV converter */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) + priv->viu.osd1_blk0_cfg[0] |= OSD_OUTPUT_COLOR_RGB; + + if (priv->viu.osd1_afbcd && + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + priv->viu.osd1_blk0_cfg[0] |= OSD_MALI_SRC_EN | + priv->afbcd.ops->fmt_to_blk_mode(fb->modifier, + fb->format->format); + } else { + switch (fb->format->format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 | + OSD_COLOR_MATRIX_32_ARGB; + break; + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 | + OSD_COLOR_MATRIX_32_ABGR; + break; + case DRM_FORMAT_RGB888: + priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_24 | + OSD_COLOR_MATRIX_24_RGB; + break; + case DRM_FORMAT_RGB565: + priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_16 | + OSD_COLOR_MATRIX_16_RGB565; + break; + } + } + + switch (fb->format->format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + /* For XRGB, replace the pixel's alpha by 0xFF */ + priv->viu.osd1_ctrl_stat2 |= OSD_REPLACE_EN; + break; + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + /* For ARGB, use the pixel's alpha */ + priv->viu.osd1_ctrl_stat2 &= ~OSD_REPLACE_EN; + break; + } + + /* Default scaler parameters */ + vsc_bot_rcv_num = 0; + vsc_bot_rpt_p0_num = 0; + hf_bank_len = 4; + vf_bank_len = 4; + + if (new_state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) { + vsc_bot_rcv_num = 6; + vsc_bot_rpt_p0_num = 2; + } + + hsc_ini_rcv_num = hf_bank_len; + vsc_ini_rcv_num = vf_bank_len; + hsc_ini_rpt_p0_num = (hf_bank_len / 2) - 1; + vsc_ini_rpt_p0_num = (vf_bank_len / 2) - 1; + + src_w = fixed16_to_int(new_state->src_w); + src_h = fixed16_to_int(new_state->src_h); + dst_w = new_state->crtc_w; + dst_h = new_state->crtc_h; + + /* + * When the output is interlaced, the OSD must switch between + * each field using the INTERLACE_SEL_ODD (0) of VIU_OSD1_BLK0_CFG_W0 + * at each vsync. + * But the vertical scaler can provide such funtionnality if + * is configured for 2:1 scaling with interlace options enabled. + */ + if (new_state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) { + dest.y1 /= 2; + dest.y2 /= 2; + dst_h /= 2; + } + + hf_phase_step = ((src_w << 18) / dst_w) << 6; + vf_phase_step = (src_h << 20) / dst_h; + + if (new_state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) + bot_ini_phase = ((vf_phase_step / 2) >> 4); + else + bot_ini_phase = 0; + + vf_phase_step = (vf_phase_step << 4); + + /* In interlaced mode, scaler is always active */ + if (src_h != dst_h || src_w != dst_w) { + priv->viu.osd_sc_i_wh_m1 = SCI_WH_M1_W(src_w - 1) | + SCI_WH_M1_H(src_h - 1); + priv->viu.osd_sc_o_h_start_end = SCO_HV_START(dest.x1) | + SCO_HV_END(dest.x2 - 1); + priv->viu.osd_sc_o_v_start_end = SCO_HV_START(dest.y1) | + SCO_HV_END(dest.y2 - 1); + /* Enable OSD Scaler */ + priv->viu.osd_sc_ctrl0 = SC_CTRL0_PATH_EN | SC_CTRL0_SEL_OSD1; + } else { + priv->viu.osd_sc_i_wh_m1 = 0; + priv->viu.osd_sc_o_h_start_end = 0; + priv->viu.osd_sc_o_v_start_end = 0; + priv->viu.osd_sc_ctrl0 = 0; + } + + /* In interlaced mode, vertical scaler is always active */ + if (src_h != dst_h) { + priv->viu.osd_sc_v_ctrl0 = + VSC_BANK_LEN(vf_bank_len) | + VSC_TOP_INI_RCV_NUM(vsc_ini_rcv_num) | + VSC_TOP_RPT_L0_NUM(vsc_ini_rpt_p0_num) | + VSC_VERTICAL_SCALER_EN; + + if (new_state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) + priv->viu.osd_sc_v_ctrl0 |= + VSC_BOT_INI_RCV_NUM(vsc_bot_rcv_num) | + VSC_BOT_RPT_L0_NUM(vsc_bot_rpt_p0_num) | + VSC_PROG_INTERLACE; + + priv->viu.osd_sc_v_phase_step = SC_PHASE_STEP(vf_phase_step); + priv->viu.osd_sc_v_ini_phase = VSC_INI_PHASE_BOT(bot_ini_phase); + } else { + priv->viu.osd_sc_v_ctrl0 = 0; + priv->viu.osd_sc_v_phase_step = 0; + priv->viu.osd_sc_v_ini_phase = 0; + } + + /* Horizontal scaler is only used if width does not match */ + if (src_w != dst_w) { + priv->viu.osd_sc_h_ctrl0 = + HSC_BANK_LENGTH(hf_bank_len) | + HSC_INI_RCV_NUM0(hsc_ini_rcv_num) | + HSC_RPT_P0_NUM0(hsc_ini_rpt_p0_num) | + HSC_HORIZ_SCALER_EN; + priv->viu.osd_sc_h_phase_step = SC_PHASE_STEP(hf_phase_step); + priv->viu.osd_sc_h_ini_phase = 0; + } else { + priv->viu.osd_sc_h_ctrl0 = 0; + priv->viu.osd_sc_h_phase_step = 0; + priv->viu.osd_sc_h_ini_phase = 0; + } + + /* + * The format of these registers is (x2 << 16 | x1), + * where x2 is exclusive. + * e.g. +30x1920 would be (1919 << 16) | 30 + */ + priv->viu.osd1_blk0_cfg[1] = + ((fixed16_to_int(new_state->src.x2) - 1) << 16) | + fixed16_to_int(new_state->src.x1); + priv->viu.osd1_blk0_cfg[2] = + ((fixed16_to_int(new_state->src.y2) - 1) << 16) | + fixed16_to_int(new_state->src.y1); + priv->viu.osd1_blk0_cfg[3] = ((dest.x2 - 1) << 16) | dest.x1; + priv->viu.osd1_blk0_cfg[4] = ((dest.y2 - 1) << 16) | dest.y1; + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + priv->viu.osd_blend_din0_scope_h = ((dest.x2 - 1) << 16) | dest.x1; + priv->viu.osd_blend_din0_scope_v = ((dest.y2 - 1) << 16) | dest.y1; + priv->viu.osb_blend0_size = dst_h << 16 | dst_w; + priv->viu.osb_blend1_size = dst_h << 16 | dst_w; + } + + /* Update Canvas with buffer address */ + gem = drm_fb_dma_get_gem_obj(fb, 0); + + priv->viu.osd1_addr = gem->dma_addr; + priv->viu.osd1_stride = fb->pitches[0]; + priv->viu.osd1_height = fb->height; + priv->viu.osd1_width = fb->width; + + if (priv->viu.osd1_afbcd) { + priv->afbcd.modifier = fb->modifier; + priv->afbcd.format = fb->format->format; + + /* Calculate decoder write stride */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + priv->viu.osd1_blk2_cfg4 = + meson_g12a_afbcd_line_stride(priv); + } + + if (!meson_plane->enabled) { + /* Reset OSD1 before enabling it on GXL+ SoCs */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) + meson_viu_osd1_reset(priv); + + meson_plane->enabled = true; + } + + priv->viu.osd1_enabled = true; + + spin_unlock_irqrestore(&priv->drm->event_lock, flags); +} + +static void meson_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct meson_plane *meson_plane = to_meson_plane(plane); + struct meson_drm *priv = meson_plane->priv; + + if (priv->afbcd.ops) { + priv->afbcd.ops->reset(priv); + priv->afbcd.ops->disable(priv); + } + + /* Disable OSD1 */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + writel_bits_relaxed(VIU_OSD1_POSTBLD_SRC_OSD1, 0, + priv->io_base + _REG(OSD1_BLEND_SRC_CTRL)); + else + writel_bits_relaxed(VPP_OSD1_POSTBLEND, 0, + priv->io_base + _REG(VPP_MISC)); + + meson_plane->enabled = false; + priv->viu.osd1_enabled = false; +} + +static const struct drm_plane_helper_funcs meson_plane_helper_funcs = { + .atomic_check = meson_plane_atomic_check, + .atomic_disable = meson_plane_atomic_disable, + .atomic_update = meson_plane_atomic_update, +}; + +static bool meson_plane_format_mod_supported(struct drm_plane *plane, + u32 format, u64 modifier) +{ + struct meson_plane *meson_plane = to_meson_plane(plane); + struct meson_drm *priv = meson_plane->priv; + int i; + + if (modifier == DRM_FORMAT_MOD_INVALID) + return false; + + if (modifier == DRM_FORMAT_MOD_LINEAR) + return true; + + if (!meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) && + !meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + return false; + + if (modifier & ~DRM_FORMAT_MOD_ARM_AFBC(MESON_MOD_AFBC_VALID_BITS)) + return false; + + for (i = 0 ; i < plane->modifier_count ; ++i) + if (plane->modifiers[i] == modifier) + break; + + if (i == plane->modifier_count) { + DRM_DEBUG_KMS("Unsupported modifier\n"); + return false; + } + + if (priv->afbcd.ops && priv->afbcd.ops->supported_fmt) + return priv->afbcd.ops->supported_fmt(modifier, format); + + DRM_DEBUG_KMS("AFBC Unsupported\n"); + return false; +} + +static const struct drm_plane_funcs meson_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .format_mod_supported = meson_plane_format_mod_supported, +}; + +static const uint32_t supported_drm_formats[] = { + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGB565, +}; + +static const uint64_t format_modifiers_afbc_gxm[] = { + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | + AFBC_FORMAT_MOD_SPARSE | + AFBC_FORMAT_MOD_YTR), + /* SPLIT mandates SPARSE, RGB modes mandates YTR */ + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | + AFBC_FORMAT_MOD_YTR | + AFBC_FORMAT_MOD_SPARSE | + AFBC_FORMAT_MOD_SPLIT), + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID, +}; + +static const uint64_t format_modifiers_afbc_g12a[] = { + /* + * - TOFIX Support AFBC modifiers for YUV formats (16x16 + TILED) + * - SPLIT is mandatory for performances reasons when in 16x16 + * block size + * - 32x8 block size + SPLIT is mandatory with 4K frame size + * for performances reasons + */ + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | + AFBC_FORMAT_MOD_SPARSE | + AFBC_FORMAT_MOD_SPLIT), + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | + AFBC_FORMAT_MOD_YTR | + AFBC_FORMAT_MOD_SPARSE | + AFBC_FORMAT_MOD_SPLIT), + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | + AFBC_FORMAT_MOD_SPARSE), + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | + AFBC_FORMAT_MOD_YTR | + AFBC_FORMAT_MOD_SPARSE), + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | + AFBC_FORMAT_MOD_SPARSE | + AFBC_FORMAT_MOD_SPLIT), + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | + AFBC_FORMAT_MOD_YTR | + AFBC_FORMAT_MOD_SPARSE | + AFBC_FORMAT_MOD_SPLIT), + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID, +}; + +static const uint64_t format_modifiers_default[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID, +}; + +int meson_plane_create(struct meson_drm *priv) +{ + struct meson_plane *meson_plane; + struct drm_plane *plane; + const uint64_t *format_modifiers = format_modifiers_default; + + meson_plane = devm_kzalloc(priv->drm->dev, sizeof(*meson_plane), + GFP_KERNEL); + if (!meson_plane) + return -ENOMEM; + + meson_plane->priv = priv; + plane = &meson_plane->base; + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM)) + format_modifiers = format_modifiers_afbc_gxm; + else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + format_modifiers = format_modifiers_afbc_g12a; + + drm_universal_plane_init(priv->drm, plane, 0xFF, + &meson_plane_funcs, + supported_drm_formats, + ARRAY_SIZE(supported_drm_formats), + format_modifiers, + DRM_PLANE_TYPE_PRIMARY, "meson_primary_plane"); + + drm_plane_helper_add(plane, &meson_plane_helper_funcs); + + /* For now, OSD Primary plane is always on the front */ + drm_plane_create_zpos_immutable_property(plane, 1); + + priv->primary_plane = plane; + + return 0; +} diff --git a/drivers/gpu/drm/meson/meson_plane.h b/drivers/gpu/drm/meson/meson_plane.h new file mode 100644 index 0000000000..1460e182cd --- /dev/null +++ b/drivers/gpu/drm/meson/meson_plane.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#ifndef __MESON_PLANE_H +#define __MESON_PLANE_H + +#include "meson_drv.h" + +int meson_plane_create(struct meson_drm *priv); + +#endif /* __MESON_PLANE_H */ diff --git a/drivers/gpu/drm/meson/meson_rdma.c b/drivers/gpu/drm/meson/meson_rdma.c new file mode 100644 index 0000000000..130382178c --- /dev/null +++ b/drivers/gpu/drm/meson/meson_rdma.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#include <linux/bitfield.h> +#include <linux/dma-mapping.h> + +#include "meson_drv.h" +#include "meson_registers.h" +#include "meson_rdma.h" + +/* + * The VPU embeds a "Register DMA" that can write a sequence of registers + * on the VPU AHB bus, either manually or triggered by an internal IRQ + * event like VSYNC or a line input counter. + * The initial implementation handles a single channel (over 8), triggered + * by the VSYNC irq and does not handle the RDMA irq. + */ + +#define RDMA_DESC_SIZE (sizeof(uint32_t) * 2) + +int meson_rdma_init(struct meson_drm *priv) +{ + if (!priv->rdma.addr) { + /* Allocate a PAGE buffer */ + priv->rdma.addr = + dma_alloc_coherent(priv->dev, SZ_4K, + &priv->rdma.addr_dma, + GFP_KERNEL); + if (!priv->rdma.addr) + return -ENOMEM; + } + + priv->rdma.offset = 0; + + writel_relaxed(RDMA_CTRL_SW_RESET, + priv->io_base + _REG(RDMA_CTRL)); + writel_relaxed(RDMA_DEFAULT_CONFIG | + FIELD_PREP(RDMA_CTRL_AHB_WR_BURST, 3) | + FIELD_PREP(RDMA_CTRL_AHB_RD_BURST, 0), + priv->io_base + _REG(RDMA_CTRL)); + + return 0; +} + +void meson_rdma_free(struct meson_drm *priv) +{ + if (!priv->rdma.addr && !priv->rdma.addr_dma) + return; + + meson_rdma_stop(priv); + + dma_free_coherent(priv->dev, SZ_4K, + priv->rdma.addr, priv->rdma.addr_dma); + + priv->rdma.addr = NULL; + priv->rdma.addr_dma = (dma_addr_t)0; +} + +void meson_rdma_setup(struct meson_drm *priv) +{ + /* Channel 1: Write Flag, No Address Increment */ + writel_bits_relaxed(RDMA_ACCESS_RW_FLAG_CHAN1 | + RDMA_ACCESS_ADDR_INC_CHAN1, + RDMA_ACCESS_RW_FLAG_CHAN1, + priv->io_base + _REG(RDMA_ACCESS_AUTO)); +} + +void meson_rdma_stop(struct meson_drm *priv) +{ + writel_bits_relaxed(RDMA_IRQ_CLEAR_CHAN1, + RDMA_IRQ_CLEAR_CHAN1, + priv->io_base + _REG(RDMA_CTRL)); + + /* Stop Channel 1 */ + writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1, + FIELD_PREP(RDMA_ACCESS_ADDR_INC_CHAN1, + RDMA_ACCESS_TRIGGER_STOP), + priv->io_base + _REG(RDMA_ACCESS_AUTO)); +} + +void meson_rdma_reset(struct meson_drm *priv) +{ + meson_rdma_stop(priv); + + priv->rdma.offset = 0; +} + +static void meson_rdma_writel(struct meson_drm *priv, uint32_t val, + uint32_t reg) +{ + if (priv->rdma.offset >= (SZ_4K / RDMA_DESC_SIZE)) { + dev_warn_once(priv->dev, "%s: overflow\n", __func__); + return; + } + + priv->rdma.addr[priv->rdma.offset++] = reg; + priv->rdma.addr[priv->rdma.offset++] = val; +} + +/* + * This will add the register to the RDMA buffer and write it to the + * hardware at the same time. + * When meson_rdma_flush is called, the RDMA will replay the register + * writes in order. + */ +void meson_rdma_writel_sync(struct meson_drm *priv, uint32_t val, uint32_t reg) +{ + meson_rdma_writel(priv, val, reg); + + writel_relaxed(val, priv->io_base + _REG(reg)); +} + +void meson_rdma_flush(struct meson_drm *priv) +{ + meson_rdma_stop(priv); + + /* Start of Channel 1 register writes buffer */ + writel(priv->rdma.addr_dma, + priv->io_base + _REG(RDMA_AHB_START_ADDR_1)); + + /* Last byte on Channel 1 register writes buffer */ + writel(priv->rdma.addr_dma + (priv->rdma.offset * RDMA_DESC_SIZE) - 1, + priv->io_base + _REG(RDMA_AHB_END_ADDR_1)); + + /* Trigger Channel 1 on VSYNC event */ + writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1, + FIELD_PREP(RDMA_ACCESS_TRIGGER_CHAN1, + RDMA_ACCESS_TRIGGER_VSYNC), + priv->io_base + _REG(RDMA_ACCESS_AUTO)); + + priv->rdma.offset = 0; +} diff --git a/drivers/gpu/drm/meson/meson_rdma.h b/drivers/gpu/drm/meson/meson_rdma.h new file mode 100644 index 0000000000..3870bff7b4 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_rdma.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#ifndef __MESON_RDMA_H +#define __MESON_RDMA_H + +#include "meson_drv.h" + +int meson_rdma_init(struct meson_drm *priv); +void meson_rdma_free(struct meson_drm *priv); +void meson_rdma_setup(struct meson_drm *priv); +void meson_rdma_reset(struct meson_drm *priv); +void meson_rdma_stop(struct meson_drm *priv); + +void meson_rdma_writel_sync(struct meson_drm *priv, uint32_t val, uint32_t reg); +void meson_rdma_flush(struct meson_drm *priv); + +#endif /* __MESON_RDMA_H */ diff --git a/drivers/gpu/drm/meson/meson_registers.h b/drivers/gpu/drm/meson/meson_registers.h new file mode 100644 index 0000000000..3d73d00a1f --- /dev/null +++ b/drivers/gpu/drm/meson/meson_registers.h @@ -0,0 +1,1910 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#ifndef __MESON_REGISTERS_H +#define __MESON_REGISTERS_H + +#include <linux/io.h> + +/* Shift all registers by 2 */ +#define _REG(reg) ((reg) << 2) + +#define writel_bits_relaxed(mask, val, addr) \ + writel_relaxed((readl_relaxed(addr) & ~(mask)) | ((val) & (mask)), addr) + +/* vpp2 */ +#define VPP2_DUMMY_DATA 0x1900 +#define VPP2_LINE_IN_LENGTH 0x1901 +#define VPP2_PIC_IN_HEIGHT 0x1902 +#define VPP2_SCALE_COEF_IDX 0x1903 +#define VPP2_SCALE_COEF 0x1904 +#define VPP2_VSC_REGION12_STARTP 0x1905 +#define VPP2_VSC_REGION34_STARTP 0x1906 +#define VPP2_VSC_REGION4_ENDP 0x1907 +#define VPP2_VSC_START_PHASE_STEP 0x1908 +#define VPP2_VSC_REGION0_PHASE_SLOPE 0x1909 +#define VPP2_VSC_REGION1_PHASE_SLOPE 0x190a +#define VPP2_VSC_REGION3_PHASE_SLOPE 0x190b +#define VPP2_VSC_REGION4_PHASE_SLOPE 0x190c +#define VPP2_VSC_PHASE_CTRL 0x190d +#define VPP2_VSC_INI_PHASE 0x190e +#define VPP2_HSC_REGION12_STARTP 0x1910 +#define VPP2_HSC_REGION34_STARTP 0x1911 +#define VPP2_HSC_REGION4_ENDP 0x1912 +#define VPP2_HSC_START_PHASE_STEP 0x1913 +#define VPP2_HSC_REGION0_PHASE_SLOPE 0x1914 +#define VPP2_HSC_REGION1_PHASE_SLOPE 0x1915 +#define VPP2_HSC_REGION3_PHASE_SLOPE 0x1916 +#define VPP2_HSC_REGION4_PHASE_SLOPE 0x1917 +#define VPP2_HSC_PHASE_CTRL 0x1918 +#define VPP2_SC_MISC 0x1919 +#define VPP2_PREBLEND_VD1_H_START_END 0x191a +#define VPP2_PREBLEND_VD1_V_START_END 0x191b +#define VPP2_POSTBLEND_VD1_H_START_END 0x191c +#define VPP2_POSTBLEND_VD1_V_START_END 0x191d +#define VPP2_PREBLEND_H_SIZE 0x1920 +#define VPP2_POSTBLEND_H_SIZE 0x1921 +#define VPP2_HOLD_LINES 0x1922 +#define VPP2_BLEND_ONECOLOR_CTRL 0x1923 +#define VPP2_PREBLEND_CURRENT_XY 0x1924 +#define VPP2_POSTBLEND_CURRENT_XY 0x1925 +#define VPP2_MISC 0x1926 +#define VPP2_OFIFO_SIZE 0x1927 +#define VPP2_FIFO_STATUS 0x1928 +#define VPP2_SMOKE_CTRL 0x1929 +#define VPP2_SMOKE1_VAL 0x192a +#define VPP2_SMOKE2_VAL 0x192b +#define VPP2_SMOKE1_H_START_END 0x192d +#define VPP2_SMOKE1_V_START_END 0x192e +#define VPP2_SMOKE2_H_START_END 0x192f +#define VPP2_SMOKE2_V_START_END 0x1930 +#define VPP2_SCO_FIFO_CTRL 0x1933 +#define VPP2_HSC_PHASE_CTRL1 0x1934 +#define VPP2_HSC_INI_PAT_CTRL 0x1935 +#define VPP2_VADJ_CTRL 0x1940 +#define VPP2_VADJ1_Y 0x1941 +#define VPP2_VADJ1_MA_MB 0x1942 +#define VPP2_VADJ1_MC_MD 0x1943 +#define VPP2_VADJ2_Y 0x1944 +#define VPP2_VADJ2_MA_MB 0x1945 +#define VPP2_VADJ2_MC_MD 0x1946 +#define VPP2_MATRIX_PROBE_COLOR 0x195c +#define VPP2_MATRIX_HL_COLOR 0x195d +#define VPP2_MATRIX_PROBE_POS 0x195e +#define VPP2_MATRIX_CTRL 0x195f +#define VPP2_MATRIX_COEF00_01 0x1960 +#define VPP2_MATRIX_COEF02_10 0x1961 +#define VPP2_MATRIX_COEF11_12 0x1962 +#define VPP2_MATRIX_COEF20_21 0x1963 +#define VPP2_MATRIX_COEF22 0x1964 +#define VPP2_MATRIX_OFFSET0_1 0x1965 +#define VPP2_MATRIX_OFFSET2 0x1966 +#define VPP2_MATRIX_PRE_OFFSET0_1 0x1967 +#define VPP2_MATRIX_PRE_OFFSET2 0x1968 +#define VPP2_DUMMY_DATA1 0x1969 +#define VPP2_GAINOFF_CTRL0 0x196a +#define VPP2_GAINOFF_CTRL1 0x196b +#define VPP2_GAINOFF_CTRL2 0x196c +#define VPP2_GAINOFF_CTRL3 0x196d +#define VPP2_GAINOFF_CTRL4 0x196e +#define VPP2_CHROMA_ADDR_PORT 0x1970 +#define VPP2_CHROMA_DATA_PORT 0x1971 +#define VPP2_GCLK_CTRL0 0x1972 +#define VPP2_GCLK_CTRL1 0x1973 +#define VPP2_SC_GCLK_CTRL 0x1974 +#define VPP2_MISC1 0x1976 +#define VPP2_DNLP_CTRL_00 0x1981 +#define VPP2_DNLP_CTRL_01 0x1982 +#define VPP2_DNLP_CTRL_02 0x1983 +#define VPP2_DNLP_CTRL_03 0x1984 +#define VPP2_DNLP_CTRL_04 0x1985 +#define VPP2_DNLP_CTRL_05 0x1986 +#define VPP2_DNLP_CTRL_06 0x1987 +#define VPP2_DNLP_CTRL_07 0x1988 +#define VPP2_DNLP_CTRL_08 0x1989 +#define VPP2_DNLP_CTRL_09 0x198a +#define VPP2_DNLP_CTRL_10 0x198b +#define VPP2_DNLP_CTRL_11 0x198c +#define VPP2_DNLP_CTRL_12 0x198d +#define VPP2_DNLP_CTRL_13 0x198e +#define VPP2_DNLP_CTRL_14 0x198f +#define VPP2_DNLP_CTRL_15 0x1990 +#define VPP2_VE_ENABLE_CTRL 0x19a1 +#define VPP2_VE_DEMO_LEFT_TOP_SCREEN_WIDTH 0x19a2 +#define VPP2_VE_DEMO_CENTER_BAR 0x19a3 +#define VPP2_VE_H_V_SIZE 0x19a4 +#define VPP2_VDO_MEAS_CTRL 0x19a8 +#define VPP2_VDO_MEAS_VS_COUNT_HI 0x19a9 +#define VPP2_VDO_MEAS_VS_COUNT_LO 0x19aa +#define VPP2_OSD_VSC_PHASE_STEP 0x19c0 +#define VPP2_OSD_VSC_INI_PHASE 0x19c1 +#define VPP2_OSD_VSC_CTRL0 0x19c2 +#define VPP2_OSD_HSC_PHASE_STEP 0x19c3 +#define VPP2_OSD_HSC_INI_PHASE 0x19c4 +#define VPP2_OSD_HSC_CTRL0 0x19c5 +#define VPP2_OSD_HSC_INI_PAT_CTRL 0x19c6 +#define VPP2_OSD_SC_DUMMY_DATA 0x19c7 +#define VPP2_OSD_SC_CTRL0 0x19c8 +#define VPP2_OSD_SCI_WH_M1 0x19c9 +#define VPP2_OSD_SCO_H_START_END 0x19ca +#define VPP2_OSD_SCO_V_START_END 0x19cb +#define VPP2_OSD_SCALE_COEF_IDX 0x19cc +#define VPP2_OSD_SCALE_COEF 0x19cd +#define VPP2_INT_LINE_NUM 0x19ce + +/* viu */ +#define VIU_ADDR_START 0x1a00 +#define VIU_ADDR_END 0x1aff +#define VIU_SW_RESET 0x1a01 +#define VIU_SW_RESET_OSD1_AFBCD BIT(31) +#define VIU_SW_RESET_G12A_OSD1_AFBCD BIT(21) +#define VIU_SW_RESET_G12A_AFBC_ARB BIT(19) +#define VIU_SW_RESET_OSD1 BIT(0) +#define VIU_MISC_CTRL0 0x1a06 +#define VIU_CTRL0_VD1_AFBC_MASK 0x170000 +#define VIU_CTRL0_AFBC_TO_VD1 BIT(20) +#define VIU_MISC_CTRL1 0x1a07 +#define MALI_AFBC_MISC GENMASK(15, 8) +#define D2D3_INTF_LENGTH 0x1a08 +#define D2D3_INTF_CTRL0 0x1a09 +#define VD1_AFBCD0_MISC_CTRL 0x1a0a +#define VD1_AXI_SEL_AFBC (1 << 12) +#define AFBC_VD1_SEL (1 << 10) +#define VD2_AFBCD1_MISC_CTRL 0x1a0b +#define VIU_OSD1_CTRL_STAT 0x1a10 +#define VIU_OSD1_OSD_BLK_ENABLE BIT(0) +#define VIU_OSD1_OSD_MEM_MODE_LINEAR BIT(2) +#define VIU_OSD1_POSTBLD_SRC_VD1 (1 << 8) +#define VIU_OSD1_POSTBLD_SRC_VD2 (2 << 8) +#define VIU_OSD1_POSTBLD_SRC_OSD1 (3 << 8) +#define VIU_OSD1_POSTBLD_SRC_OSD2 (4 << 8) +#define VIU_OSD1_OSD_ENABLE BIT(21) +#define VIU_OSD1_CFG_SYN_EN BIT(31) +#define VIU_OSD1_CTRL_STAT2 0x1a2d +#define VIU_OSD1_COLOR_ADDR 0x1a11 +#define VIU_OSD1_COLOR 0x1a12 +#define VIU_OSD1_TCOLOR_AG0 0x1a17 +#define VIU_OSD1_TCOLOR_AG1 0x1a18 +#define VIU_OSD1_TCOLOR_AG2 0x1a19 +#define VIU_OSD1_TCOLOR_AG3 0x1a1a +#define VIU_OSD1_BLK0_CFG_W0 0x1a1b +#define VIU_OSD1_BLK1_CFG_W0 0x1a1f +#define VIU_OSD1_BLK2_CFG_W0 0x1a23 +#define VIU_OSD1_BLK3_CFG_W0 0x1a27 +#define VIU_OSD1_BLK0_CFG_W1 0x1a1c +#define VIU_OSD1_BLK1_CFG_W1 0x1a20 +#define VIU_OSD1_BLK2_CFG_W1 0x1a24 +#define VIU_OSD1_BLK3_CFG_W1 0x1a28 +#define VIU_OSD1_BLK0_CFG_W2 0x1a1d +#define VIU_OSD1_BLK1_CFG_W2 0x1a21 +#define VIU_OSD1_BLK2_CFG_W2 0x1a25 +#define VIU_OSD1_BLK3_CFG_W2 0x1a29 +#define VIU_OSD1_BLK0_CFG_W3 0x1a1e +#define VIU_OSD1_BLK1_CFG_W3 0x1a22 +#define VIU_OSD1_BLK2_CFG_W3 0x1a26 +#define VIU_OSD1_BLK3_CFG_W3 0x1a2a +#define VIU_OSD1_BLK0_CFG_W4 0x1a13 +#define VIU_OSD1_BLK1_CFG_W4 0x1a14 +#define VIU_OSD1_BLK2_CFG_W4 0x1a15 +#define VIU_OSD1_BLK3_CFG_W4 0x1a16 +#define VIU_OSD1_FIFO_CTRL_STAT 0x1a2b +#define VIU_OSD1_TEST_RDDATA 0x1a2c +#define VIU_OSD1_PROT_CTRL 0x1a2e +#define VIU_OSD1_MALI_UNPACK_CTRL 0x1a2f +#define VIU_OSD1_MALI_UNPACK_EN BIT(31) +#define VIU_OSD1_MALI_AFBCD_R_REORDER GENMASK(15, 12) +#define VIU_OSD1_MALI_AFBCD_G_REORDER GENMASK(11, 8) +#define VIU_OSD1_MALI_AFBCD_B_REORDER GENMASK(7, 4) +#define VIU_OSD1_MALI_AFBCD_A_REORDER GENMASK(3, 0) +#define VIU_OSD1_MALI_REORDER_R 1 +#define VIU_OSD1_MALI_REORDER_G 2 +#define VIU_OSD1_MALI_REORDER_B 3 +#define VIU_OSD1_MALI_REORDER_A 4 +#define VIU_OSD2_CTRL_STAT 0x1a30 +#define VIU_OSD2_CTRL_STAT2 0x1a4d +#define VIU_OSD2_COLOR_ADDR 0x1a31 +#define VIU_OSD2_COLOR 0x1a32 +#define VIU_OSD2_HL1_H_START_END 0x1a33 +#define VIU_OSD2_HL1_V_START_END 0x1a34 +#define VIU_OSD2_HL2_H_START_END 0x1a35 +#define VIU_OSD2_HL2_V_START_END 0x1a36 +#define VIU_OSD2_TCOLOR_AG0 0x1a37 +#define VIU_OSD2_TCOLOR_AG1 0x1a38 +#define VIU_OSD2_TCOLOR_AG2 0x1a39 +#define VIU_OSD2_TCOLOR_AG3 0x1a3a +#define VIU_OSD2_BLK0_CFG_W0 0x1a3b +#define VIU_OSD2_BLK1_CFG_W0 0x1a3f +#define VIU_OSD2_BLK2_CFG_W0 0x1a43 +#define VIU_OSD2_BLK3_CFG_W0 0x1a47 +#define VIU_OSD2_BLK0_CFG_W1 0x1a3c +#define VIU_OSD2_BLK1_CFG_W1 0x1a40 +#define VIU_OSD2_BLK2_CFG_W1 0x1a44 +#define VIU_OSD2_BLK3_CFG_W1 0x1a48 +#define VIU_OSD2_BLK0_CFG_W2 0x1a3d +#define VIU_OSD2_BLK1_CFG_W2 0x1a41 +#define VIU_OSD2_BLK2_CFG_W2 0x1a45 +#define VIU_OSD2_BLK3_CFG_W2 0x1a49 +#define VIU_OSD2_BLK0_CFG_W3 0x1a3e +#define VIU_OSD2_BLK1_CFG_W3 0x1a42 +#define VIU_OSD2_BLK2_CFG_W3 0x1a46 +#define VIU_OSD2_BLK3_CFG_W3 0x1a4a +#define VIU_OSD2_BLK0_CFG_W4 0x1a64 +#define VIU_OSD2_BLK1_CFG_W4 0x1a65 +#define VIU_OSD2_BLK2_CFG_W4 0x1a66 +#define VIU_OSD2_BLK3_CFG_W4 0x1a67 +#define VIU_OSD2_FIFO_CTRL_STAT 0x1a4b +#define VIU_OSD2_TEST_RDDATA 0x1a4c +#define VIU_OSD2_PROT_CTRL 0x1a4e +#define VIU_OSD2_MALI_UNPACK_CTRL 0x1abd +#define VIU_OSD2_DIMM_CTRL 0x1acf + +#define VIU_OSD3_CTRL_STAT 0x3d80 +#define VIU_OSD3_CTRL_STAT2 0x3d81 +#define VIU_OSD3_COLOR_ADDR 0x3d82 +#define VIU_OSD3_COLOR 0x3d83 +#define VIU_OSD3_TCOLOR_AG0 0x3d84 +#define VIU_OSD3_TCOLOR_AG1 0x3d85 +#define VIU_OSD3_TCOLOR_AG2 0x3d86 +#define VIU_OSD3_TCOLOR_AG3 0x3d87 +#define VIU_OSD3_BLK0_CFG_W0 0x3d88 +#define VIU_OSD3_BLK0_CFG_W1 0x3d8c +#define VIU_OSD3_BLK0_CFG_W2 0x3d90 +#define VIU_OSD3_BLK0_CFG_W3 0x3d94 +#define VIU_OSD3_BLK0_CFG_W4 0x3d98 +#define VIU_OSD3_BLK1_CFG_W4 0x3d99 +#define VIU_OSD3_BLK2_CFG_W4 0x3d9a +#define VIU_OSD3_FIFO_CTRL_STAT 0x3d9c +#define VIU_OSD3_TEST_RDDATA 0x3d9d +#define VIU_OSD3_PROT_CTRL 0x3d9e +#define VIU_OSD3_MALI_UNPACK_CTRL 0x3d9f +#define VIU_OSD3_DIMM_CTRL 0x3da0 + +#define VIU_OSD_DDR_PRIORITY_URGENT BIT(0) +#define VIU_OSD_HOLD_FIFO_LINES(lines) ((lines & 0x1f) << 5) +#define VIU_OSD_FIFO_DEPTH_VAL(val) ((val & 0x7f) << 12) +#define VIU_OSD_WORDS_PER_BURST(words) (((words & 0x4) >> 1) << 22) +#define VIU_OSD_FIFO_LIMITS(size) ((size & 0xf) << 24) +#define VIU_OSD_BURST_LENGTH_24 (0x0 << 31 | 0x0 << 10) +#define VIU_OSD_BURST_LENGTH_32 (0x0 << 31 | 0x1 << 10) +#define VIU_OSD_BURST_LENGTH_48 (0x0 << 31 | 0x2 << 10) +#define VIU_OSD_BURST_LENGTH_64 (0x0 << 31 | 0x3 << 10) +#define VIU_OSD_BURST_LENGTH_96 (0x1 << 31 | 0x0 << 10) +#define VIU_OSD_BURST_LENGTH_128 (0x1 << 31 | 0x1 << 10) + +#define VD1_IF0_GEN_REG 0x1a50 +#define VD1_IF0_CANVAS0 0x1a51 +#define VD1_IF0_CANVAS1 0x1a52 +#define VD1_IF0_LUMA_X0 0x1a53 +#define VD1_IF0_LUMA_Y0 0x1a54 +#define VD1_IF0_CHROMA_X0 0x1a55 +#define VD1_IF0_CHROMA_Y0 0x1a56 +#define VD1_IF0_LUMA_X1 0x1a57 +#define VD1_IF0_LUMA_Y1 0x1a58 +#define VD1_IF0_CHROMA_X1 0x1a59 +#define VD1_IF0_CHROMA_Y1 0x1a5a +#define VD1_IF0_RPT_LOOP 0x1a5b +#define VD1_IF0_LUMA0_RPT_PAT 0x1a5c +#define VD1_IF0_CHROMA0_RPT_PAT 0x1a5d +#define VD1_IF0_LUMA1_RPT_PAT 0x1a5e +#define VD1_IF0_CHROMA1_RPT_PAT 0x1a5f +#define VD1_IF0_LUMA_PSEL 0x1a60 +#define VD1_IF0_CHROMA_PSEL 0x1a61 +#define VD1_IF0_DUMMY_PIXEL 0x1a62 +#define VD1_IF0_LUMA_FIFO_SIZE 0x1a63 +#define VD1_IF0_RANGE_MAP_Y 0x1a6a +#define VD1_IF0_RANGE_MAP_CB 0x1a6b +#define VD1_IF0_RANGE_MAP_CR 0x1a6c +#define VD1_IF0_GEN_REG2 0x1a6d +#define VD1_IF0_PROT_CNTL 0x1a6e +#define VIU_VD1_FMT_CTRL 0x1a68 +#define VIU_VD1_FMT_W 0x1a69 +#define VD2_IF0_GEN_REG 0x1a70 +#define VD2_IF0_CANVAS0 0x1a71 +#define VD2_IF0_CANVAS1 0x1a72 +#define VD2_IF0_LUMA_X0 0x1a73 +#define VD2_IF0_LUMA_Y0 0x1a74 +#define VD2_IF0_CHROMA_X0 0x1a75 +#define VD2_IF0_CHROMA_Y0 0x1a76 +#define VD2_IF0_LUMA_X1 0x1a77 +#define VD2_IF0_LUMA_Y1 0x1a78 +#define VD2_IF0_CHROMA_X1 0x1a79 +#define VD2_IF0_CHROMA_Y1 0x1a7a +#define VD2_IF0_RPT_LOOP 0x1a7b +#define VD2_IF0_LUMA0_RPT_PAT 0x1a7c +#define VD2_IF0_CHROMA0_RPT_PAT 0x1a7d +#define VD2_IF0_LUMA1_RPT_PAT 0x1a7e +#define VD2_IF0_CHROMA1_RPT_PAT 0x1a7f +#define VD2_IF0_LUMA_PSEL 0x1a80 +#define VD2_IF0_CHROMA_PSEL 0x1a81 +#define VD2_IF0_DUMMY_PIXEL 0x1a82 +#define VD2_IF0_LUMA_FIFO_SIZE 0x1a83 +#define VD2_IF0_RANGE_MAP_Y 0x1a8a +#define VD2_IF0_RANGE_MAP_CB 0x1a8b +#define VD2_IF0_RANGE_MAP_CR 0x1a8c +#define VD2_IF0_GEN_REG2 0x1a8d +#define VD2_IF0_PROT_CNTL 0x1a8e +#define VIU_VD2_FMT_CTRL 0x1a88 +#define VIU_VD2_FMT_W 0x1a89 + +/* VIU Matrix Registers */ +#define VIU_OSD1_MATRIX_CTRL 0x1a90 +#define VIU_OSD1_MATRIX_COEF00_01 0x1a91 +#define VIU_OSD1_MATRIX_COEF02_10 0x1a92 +#define VIU_OSD1_MATRIX_COEF11_12 0x1a93 +#define VIU_OSD1_MATRIX_COEF20_21 0x1a94 +#define VIU_OSD1_MATRIX_COLMOD_COEF42 0x1a95 +#define VIU_OSD1_MATRIX_OFFSET0_1 0x1a96 +#define VIU_OSD1_MATRIX_OFFSET2 0x1a97 +#define VIU_OSD1_MATRIX_PRE_OFFSET0_1 0x1a98 +#define VIU_OSD1_MATRIX_PRE_OFFSET2 0x1a99 +#define VIU_OSD1_MATRIX_COEF22_30 0x1a9d +#define VIU_OSD1_MATRIX_COEF31_32 0x1a9e +#define VIU_OSD1_MATRIX_COEF40_41 0x1a9f +#define VD1_IF0_GEN_REG3 0x1aa7 + +#define VIU_OSD_BLENDO_H_START_END 0x1aa9 +#define VIU_OSD_BLENDO_V_START_END 0x1aaa +#define VIU_OSD_BLEND_GEN_CTRL0 0x1aab +#define VIU_OSD_BLEND_GEN_CTRL1 0x1aac +#define VIU_OSD_BLEND_DUMMY_DATA 0x1aad +#define VIU_OSD_BLEND_CURRENT_XY 0x1aae + +#define VIU_OSD2_MATRIX_CTRL 0x1ab0 +#define VIU_OSD2_MATRIX_COEF00_01 0x1ab1 +#define VIU_OSD2_MATRIX_COEF02_10 0x1ab2 +#define VIU_OSD2_MATRIX_COEF11_12 0x1ab3 +#define VIU_OSD2_MATRIX_COEF20_21 0x1ab4 +#define VIU_OSD2_MATRIX_COEF22 0x1ab5 +#define VIU_OSD2_MATRIX_OFFSET0_1 0x1ab6 +#define VIU_OSD2_MATRIX_OFFSET2 0x1ab7 +#define VIU_OSD2_MATRIX_PRE_OFFSET0_1 0x1ab8 +#define VIU_OSD2_MATRIX_PRE_OFFSET2 0x1ab9 +#define VIU_OSD2_MATRIX_PROBE_COLOR 0x1aba +#define VIU_OSD2_MATRIX_HL_COLOR 0x1abb +#define VIU_OSD2_MATRIX_PROBE_POS 0x1abc +#define VIU_OSD1_EOTF_CTL 0x1ad4 +#define VIU_OSD1_EOTF_COEF00_01 0x1ad5 +#define VIU_OSD1_EOTF_COEF02_10 0x1ad6 +#define VIU_OSD1_EOTF_COEF11_12 0x1ad7 +#define VIU_OSD1_EOTF_COEF20_21 0x1ad8 +#define VIU_OSD1_EOTF_COEF22_RS 0x1ad9 +#define VIU_OSD1_EOTF_LUT_ADDR_PORT 0x1ada +#define VIU_OSD1_EOTF_LUT_DATA_PORT 0x1adb +#define VIU_OSD1_OETF_CTL 0x1adc +#define VIU_OSD1_OETF_LUT_ADDR_PORT 0x1add +#define VIU_OSD1_OETF_LUT_DATA_PORT 0x1ade +#define AFBC_ENABLE 0x1ae0 +#define AFBC_MODE 0x1ae1 +#define AFBC_SIZE_IN 0x1ae2 +#define AFBC_DEC_DEF_COLOR 0x1ae3 +#define AFBC_CONV_CTRL 0x1ae4 +#define AFBC_LBUF_DEPTH 0x1ae5 +#define AFBC_HEAD_BADDR 0x1ae6 +#define AFBC_BODY_BADDR 0x1ae7 +#define AFBC_SIZE_OUT 0x1ae8 +#define AFBC_OUT_YSCOPE 0x1ae9 +#define AFBC_STAT 0x1aea +#define AFBC_VD_CFMT_CTRL 0x1aeb +#define AFBC_VD_CFMT_W 0x1aec +#define AFBC_MIF_HOR_SCOPE 0x1aed +#define AFBC_MIF_VER_SCOPE 0x1aee +#define AFBC_PIXEL_HOR_SCOPE 0x1aef +#define AFBC_PIXEL_VER_SCOPE 0x1af0 +#define AFBC_VD_CFMT_H 0x1af1 + +/* vpp */ +#define VPP_DUMMY_DATA 0x1d00 +#define VPP_LINE_IN_LENGTH 0x1d01 +#define VPP_PIC_IN_HEIGHT 0x1d02 +#define VPP_SCALE_COEF_IDX 0x1d03 +#define VPP_SCALE_HORIZONTAL_COEF BIT(8) +#define VPP_SCALE_COEF 0x1d04 +#define VPP_VSC_REGION12_STARTP 0x1d05 +#define VPP_VSC_REGION34_STARTP 0x1d06 +#define VPP_VSC_REGION4_ENDP 0x1d07 +#define VPP_VSC_START_PHASE_STEP 0x1d08 +#define VPP_VSC_REGION0_PHASE_SLOPE 0x1d09 +#define VPP_VSC_REGION1_PHASE_SLOPE 0x1d0a +#define VPP_VSC_REGION3_PHASE_SLOPE 0x1d0b +#define VPP_VSC_REGION4_PHASE_SLOPE 0x1d0c +#define VPP_VSC_PHASE_CTRL 0x1d0d +#define VPP_VSC_INI_PHASE 0x1d0e +#define VPP_HSC_REGION12_STARTP 0x1d10 +#define VPP_HSC_REGION34_STARTP 0x1d11 +#define VPP_HSC_REGION4_ENDP 0x1d12 +#define VPP_HSC_START_PHASE_STEP 0x1d13 +#define VPP_HSC_REGION0_PHASE_SLOPE 0x1d14 +#define VPP_HSC_REGION1_PHASE_SLOPE 0x1d15 +#define VPP_HSC_REGION3_PHASE_SLOPE 0x1d16 +#define VPP_HSC_REGION4_PHASE_SLOPE 0x1d17 +#define VPP_HSC_PHASE_CTRL 0x1d18 +#define VPP_SC_MISC 0x1d19 +#define VPP_SC_VD_EN_ENABLE BIT(15) +#define VPP_SC_TOP_EN_ENABLE BIT(16) +#define VPP_SC_HSC_EN_ENABLE BIT(17) +#define VPP_SC_VSC_EN_ENABLE BIT(18) +#define VPP_VSC_BANK_LENGTH(length) (length & 0x7) +#define VPP_HSC_BANK_LENGTH(length) ((length & 0x7) << 8) +#define VPP_PREBLEND_VD1_H_START_END 0x1d1a +#define VPP_PREBLEND_VD1_V_START_END 0x1d1b +#define VPP_POSTBLEND_VD1_H_START_END 0x1d1c +#define VPP_POSTBLEND_VD1_V_START_END 0x1d1d +#define VPP_BLEND_VD2_H_START_END 0x1d1e +#define VPP_BLEND_VD2_V_START_END 0x1d1f +#define VPP_PREBLEND_H_SIZE 0x1d20 +#define VPP_POSTBLEND_H_SIZE 0x1d21 +#define VPP_HOLD_LINES 0x1d22 +#define VPP_POSTBLEND_HOLD_LINES(lines) (lines & 0xf) +#define VPP_PREBLEND_HOLD_LINES(lines) ((lines & 0xf) << 8) +#define VPP_BLEND_ONECOLOR_CTRL 0x1d23 +#define VPP_PREBLEND_CURRENT_XY 0x1d24 +#define VPP_POSTBLEND_CURRENT_XY 0x1d25 +#define VPP_MISC 0x1d26 +#define VPP_PREBLEND_ENABLE BIT(6) +#define VPP_POSTBLEND_ENABLE BIT(7) +#define VPP_OSD2_ALPHA_PREMULT BIT(8) +#define VPP_OSD1_ALPHA_PREMULT BIT(9) +#define VPP_VD1_POSTBLEND BIT(10) +#define VPP_VD2_POSTBLEND BIT(11) +#define VPP_OSD1_POSTBLEND BIT(12) +#define VPP_OSD2_POSTBLEND BIT(13) +#define VPP_VD1_PREBLEND BIT(14) +#define VPP_VD2_PREBLEND BIT(15) +#define VPP_OSD1_PREBLEND BIT(16) +#define VPP_OSD2_PREBLEND BIT(17) +#define VPP_COLOR_MNG_ENABLE BIT(28) +#define VPP_OFIFO_SIZE 0x1d27 +#define VPP_OFIFO_SIZE_MASK GENMASK(13, 0) +#define VPP_OFIFO_SIZE_DEFAULT (0xfff << 20 | 0x1000) +#define VPP_FIFO_STATUS 0x1d28 +#define VPP_SMOKE_CTRL 0x1d29 +#define VPP_SMOKE1_VAL 0x1d2a +#define VPP_SMOKE2_VAL 0x1d2b +#define VPP_SMOKE3_VAL 0x1d2c +#define VPP_SMOKE1_H_START_END 0x1d2d +#define VPP_SMOKE1_V_START_END 0x1d2e +#define VPP_SMOKE2_H_START_END 0x1d2f +#define VPP_SMOKE2_V_START_END 0x1d30 +#define VPP_SMOKE3_H_START_END 0x1d31 +#define VPP_SMOKE3_V_START_END 0x1d32 +#define VPP_SCO_FIFO_CTRL 0x1d33 +#define VPP_HSC_PHASE_CTRL1 0x1d34 +#define VPP_HSC_INI_PAT_CTRL 0x1d35 +#define VPP_VADJ_CTRL 0x1d40 +#define VPP_MINUS_BLACK_LVL_VADJ1_ENABLE BIT(1) + +#define VPP_VADJ1_Y 0x1d41 +#define VPP_VADJ1_MA_MB 0x1d42 +#define VPP_VADJ1_MC_MD 0x1d43 +#define VPP_VADJ2_Y 0x1d44 +#define VPP_VADJ2_MA_MB 0x1d45 +#define VPP_VADJ2_MC_MD 0x1d46 +#define VPP_HSHARP_CTRL 0x1d50 +#define VPP_HSHARP_LUMA_THRESH01 0x1d51 +#define VPP_HSHARP_LUMA_THRESH23 0x1d52 +#define VPP_HSHARP_CHROMA_THRESH01 0x1d53 +#define VPP_HSHARP_CHROMA_THRESH23 0x1d54 +#define VPP_HSHARP_LUMA_GAIN 0x1d55 +#define VPP_HSHARP_CHROMA_GAIN 0x1d56 +#define VPP_MATRIX_PROBE_COLOR 0x1d5c +#define VPP_MATRIX_HL_COLOR 0x1d5d +#define VPP_MATRIX_PROBE_POS 0x1d5e +#define VPP_MATRIX_CTRL 0x1d5f +#define VPP_MATRIX_COEF00_01 0x1d60 +#define VPP_MATRIX_COEF02_10 0x1d61 +#define VPP_MATRIX_COEF11_12 0x1d62 +#define VPP_MATRIX_COEF20_21 0x1d63 +#define VPP_MATRIX_COEF22 0x1d64 +#define VPP_MATRIX_OFFSET0_1 0x1d65 +#define VPP_MATRIX_OFFSET2 0x1d66 +#define VPP_MATRIX_PRE_OFFSET0_1 0x1d67 +#define VPP_MATRIX_PRE_OFFSET2 0x1d68 +#define VPP_DUMMY_DATA1 0x1d69 +#define VPP_GAINOFF_CTRL0 0x1d6a +#define VPP_GAINOFF_CTRL1 0x1d6b +#define VPP_GAINOFF_CTRL2 0x1d6c +#define VPP_GAINOFF_CTRL3 0x1d6d +#define VPP_GAINOFF_CTRL4 0x1d6e +#define VPP_CHROMA_ADDR_PORT 0x1d70 +#define VPP_CHROMA_DATA_PORT 0x1d71 +#define VPP_GCLK_CTRL0 0x1d72 +#define VPP_GCLK_CTRL1 0x1d73 +#define VPP_SC_GCLK_CTRL 0x1d74 +#define VPP_MISC1 0x1d76 +#define VPP_BLACKEXT_CTRL 0x1d80 +#define VPP_DNLP_CTRL_00 0x1d81 +#define VPP_DNLP_CTRL_01 0x1d82 +#define VPP_DNLP_CTRL_02 0x1d83 +#define VPP_DNLP_CTRL_03 0x1d84 +#define VPP_DNLP_CTRL_04 0x1d85 +#define VPP_DNLP_CTRL_05 0x1d86 +#define VPP_DNLP_CTRL_06 0x1d87 +#define VPP_DNLP_CTRL_07 0x1d88 +#define VPP_DNLP_CTRL_08 0x1d89 +#define VPP_DNLP_CTRL_09 0x1d8a +#define VPP_DNLP_CTRL_10 0x1d8b +#define VPP_DNLP_CTRL_11 0x1d8c +#define VPP_DNLP_CTRL_12 0x1d8d +#define VPP_DNLP_CTRL_13 0x1d8e +#define VPP_DNLP_CTRL_14 0x1d8f +#define VPP_DNLP_CTRL_15 0x1d90 +#define VPP_PEAKING_HGAIN 0x1d91 +#define VPP_PEAKING_VGAIN 0x1d92 +#define VPP_PEAKING_NLP_1 0x1d93 +#define VPP_DOLBY_CTRL 0x1d93 +#define VPP_PPS_DUMMY_DATA_MODE (1 << 17) +#define VPP_PEAKING_NLP_2 0x1d94 +#define VPP_PEAKING_NLP_3 0x1d95 +#define VPP_PEAKING_NLP_4 0x1d96 +#define VPP_PEAKING_NLP_5 0x1d97 +#define VPP_SHARP_LIMIT 0x1d98 +#define VPP_VLTI_CTRL 0x1d99 +#define VPP_HLTI_CTRL 0x1d9a +#define VPP_CTI_CTRL 0x1d9b +#define VPP_BLUE_STRETCH_1 0x1d9c +#define VPP_BLUE_STRETCH_2 0x1d9d +#define VPP_BLUE_STRETCH_3 0x1d9e +#define VPP_CCORING_CTRL 0x1da0 +#define VPP_VE_ENABLE_CTRL 0x1da1 +#define VPP_VE_DEMO_LEFT_TOP_SCREEN_WIDTH 0x1da2 +#define VPP_VE_DEMO_CENTER_BAR 0x1da3 +#define VPP_VE_H_V_SIZE 0x1da4 +#define VPP_VDO_MEAS_CTRL 0x1da8 +#define VPP_VDO_MEAS_VS_COUNT_HI 0x1da9 +#define VPP_VDO_MEAS_VS_COUNT_LO 0x1daa +#define VPP_INPUT_CTRL 0x1dab +#define VPP_CTI_CTRL2 0x1dac +#define VPP_PEAKING_SAT_THD1 0x1dad +#define VPP_PEAKING_SAT_THD2 0x1dae +#define VPP_PEAKING_SAT_THD3 0x1daf +#define VPP_PEAKING_SAT_THD4 0x1db0 +#define VPP_PEAKING_SAT_THD5 0x1db1 +#define VPP_PEAKING_SAT_THD6 0x1db2 +#define VPP_PEAKING_SAT_THD7 0x1db3 +#define VPP_PEAKING_SAT_THD8 0x1db4 +#define VPP_PEAKING_SAT_THD9 0x1db5 +#define VPP_PEAKING_GAIN_ADD1 0x1db6 +#define VPP_PEAKING_GAIN_ADD2 0x1db7 +#define VPP_PEAKING_DNLP 0x1db8 +#define VPP_SHARP_DEMO_WIN_CTRL1 0x1db9 +#define VPP_SHARP_DEMO_WIN_CTRL2 0x1dba +#define VPP_FRONT_HLTI_CTRL 0x1dbb +#define VPP_FRONT_CTI_CTRL 0x1dbc +#define VPP_FRONT_CTI_CTRL2 0x1dbd +#define VPP_OSD_VSC_PHASE_STEP 0x1dc0 +#define VPP_OSD_VSC_INI_PHASE 0x1dc1 +#define VPP_OSD_VSC_CTRL0 0x1dc2 +#define VPP_OSD_HSC_PHASE_STEP 0x1dc3 +#define VPP_OSD_HSC_INI_PHASE 0x1dc4 +#define VPP_OSD_HSC_CTRL0 0x1dc5 +#define VPP_OSD_HSC_INI_PAT_CTRL 0x1dc6 +#define VPP_OSD_SC_DUMMY_DATA 0x1dc7 +#define VPP_OSD_SC_CTRL0 0x1dc8 +#define VPP_OSD_SCI_WH_M1 0x1dc9 +#define VPP_OSD_SCO_H_START_END 0x1dca +#define VPP_OSD_SCO_V_START_END 0x1dcb +#define VPP_OSD_SCALE_COEF_IDX 0x1dcc +#define VPP_OSD_SCALE_COEF 0x1dcd +#define VPP_INT_LINE_NUM 0x1dce + +#define VPP_WRAP_OSD1_MATRIX_COEF00_01 0x3d60 +#define VPP_WRAP_OSD1_MATRIX_COEF02_10 0x3d61 +#define VPP_WRAP_OSD1_MATRIX_COEF11_12 0x3d62 +#define VPP_WRAP_OSD1_MATRIX_COEF20_21 0x3d63 +#define VPP_WRAP_OSD1_MATRIX_COEF22 0x3d64 +#define VPP_WRAP_OSD1_MATRIX_COEF13_14 0x3d65 +#define VPP_WRAP_OSD1_MATRIX_COEF23_24 0x3d66 +#define VPP_WRAP_OSD1_MATRIX_COEF15_25 0x3d67 +#define VPP_WRAP_OSD1_MATRIX_CLIP 0x3d68 +#define VPP_WRAP_OSD1_MATRIX_OFFSET0_1 0x3d69 +#define VPP_WRAP_OSD1_MATRIX_OFFSET2 0x3d6a +#define VPP_WRAP_OSD1_MATRIX_PRE_OFFSET0_1 0x3d6b +#define VPP_WRAP_OSD1_MATRIX_PRE_OFFSET2 0x3d6c +#define VPP_WRAP_OSD1_MATRIX_EN_CTRL 0x3d6d + +#define VPP_WRAP_OSD2_MATRIX_COEF00_01 0x3d70 +#define VPP_WRAP_OSD2_MATRIX_COEF02_10 0x3d71 +#define VPP_WRAP_OSD2_MATRIX_COEF11_12 0x3d72 +#define VPP_WRAP_OSD2_MATRIX_COEF20_21 0x3d73 +#define VPP_WRAP_OSD2_MATRIX_COEF22 0x3d74 +#define VPP_WRAP_OSD2_MATRIX_COEF13_14 0x3d75 +#define VPP_WRAP_OSD2_MATRIX_COEF23_24 0x3d76 +#define VPP_WRAP_OSD2_MATRIX_COEF15_25 0x3d77 +#define VPP_WRAP_OSD2_MATRIX_CLIP 0x3d78 +#define VPP_WRAP_OSD2_MATRIX_OFFSET0_1 0x3d79 +#define VPP_WRAP_OSD2_MATRIX_OFFSET2 0x3d7a +#define VPP_WRAP_OSD2_MATRIX_PRE_OFFSET0_1 0x3d7b +#define VPP_WRAP_OSD2_MATRIX_PRE_OFFSET2 0x3d7c +#define VPP_WRAP_OSD2_MATRIX_EN_CTRL 0x3d7d + +#define VPP_WRAP_OSD3_MATRIX_COEF00_01 0x3db0 +#define VPP_WRAP_OSD3_MATRIX_COEF02_10 0x3db1 +#define VPP_WRAP_OSD3_MATRIX_COEF11_12 0x3db2 +#define VPP_WRAP_OSD3_MATRIX_COEF20_21 0x3db3 +#define VPP_WRAP_OSD3_MATRIX_COEF22 0x3db4 +#define VPP_WRAP_OSD3_MATRIX_COEF13_14 0x3db5 +#define VPP_WRAP_OSD3_MATRIX_COEF23_24 0x3db6 +#define VPP_WRAP_OSD3_MATRIX_COEF15_25 0x3db7 +#define VPP_WRAP_OSD3_MATRIX_CLIP 0x3db8 +#define VPP_WRAP_OSD3_MATRIX_OFFSET0_1 0x3db9 +#define VPP_WRAP_OSD3_MATRIX_OFFSET2 0x3dba +#define VPP_WRAP_OSD3_MATRIX_PRE_OFFSET0_1 0x3dbb +#define VPP_WRAP_OSD3_MATRIX_PRE_OFFSET2 0x3dbc +#define VPP_WRAP_OSD3_MATRIX_EN_CTRL 0x3dbd + +/* osd1 HDR */ +#define OSD1_HDR2_CTRL 0x38a0 +#define OSD1_HDR2_CTRL_VDIN0_HDR2_TOP_EN BIT(13) +#define OSD1_HDR2_CTRL_REG_ONLY_MAT BIT(16) + +/* osd2 scaler */ +#define OSD2_VSC_PHASE_STEP 0x3d00 +#define OSD2_VSC_INI_PHASE 0x3d01 +#define OSD2_VSC_CTRL0 0x3d02 +#define OSD2_HSC_PHASE_STEP 0x3d03 +#define OSD2_HSC_INI_PHASE 0x3d04 +#define OSD2_HSC_CTRL0 0x3d05 +#define OSD2_HSC_INI_PAT_CTRL 0x3d06 +#define OSD2_SC_DUMMY_DATA 0x3d07 +#define OSD2_SC_CTRL0 0x3d08 +#define OSD2_SCI_WH_M1 0x3d09 +#define OSD2_SCO_H_START_END 0x3d0a +#define OSD2_SCO_V_START_END 0x3d0b +#define OSD2_SCALE_COEF_IDX 0x3d18 +#define OSD2_SCALE_COEF 0x3d19 + +/* osd34 scaler */ +#define OSD34_SCALE_COEF_IDX 0x3d1e +#define OSD34_SCALE_COEF 0x3d1f +#define OSD34_VSC_PHASE_STEP 0x3d20 +#define OSD34_VSC_INI_PHASE 0x3d21 +#define OSD34_VSC_CTRL0 0x3d22 +#define OSD34_HSC_PHASE_STEP 0x3d23 +#define OSD34_HSC_INI_PHASE 0x3d24 +#define OSD34_HSC_CTRL0 0x3d25 +#define OSD34_HSC_INI_PAT_CTRL 0x3d26 +#define OSD34_SC_DUMMY_DATA 0x3d27 +#define OSD34_SC_CTRL0 0x3d28 +#define OSD34_SCI_WH_M1 0x3d29 +#define OSD34_SCO_H_START_END 0x3d2a +#define OSD34_SCO_V_START_END 0x3d2b + +/* viu2 */ +#define VIU2_ADDR_START 0x1e00 +#define VIU2_ADDR_END 0x1eff +#define VIU2_SW_RESET 0x1e01 +#define VIU2_OSD1_CTRL_STAT 0x1e10 +#define VIU2_OSD1_CTRL_STAT2 0x1e2d +#define VIU2_OSD1_COLOR_ADDR 0x1e11 +#define VIU2_OSD1_COLOR 0x1e12 +#define VIU2_OSD1_TCOLOR_AG0 0x1e17 +#define VIU2_OSD1_TCOLOR_AG1 0x1e18 +#define VIU2_OSD1_TCOLOR_AG2 0x1e19 +#define VIU2_OSD1_TCOLOR_AG3 0x1e1a +#define VIU2_OSD1_BLK0_CFG_W0 0x1e1b +#define VIU2_OSD1_BLK1_CFG_W0 0x1e1f +#define VIU2_OSD1_BLK2_CFG_W0 0x1e23 +#define VIU2_OSD1_BLK3_CFG_W0 0x1e27 +#define VIU2_OSD1_BLK0_CFG_W1 0x1e1c +#define VIU2_OSD1_BLK1_CFG_W1 0x1e20 +#define VIU2_OSD1_BLK2_CFG_W1 0x1e24 +#define VIU2_OSD1_BLK3_CFG_W1 0x1e28 +#define VIU2_OSD1_BLK0_CFG_W2 0x1e1d +#define VIU2_OSD1_BLK1_CFG_W2 0x1e21 +#define VIU2_OSD1_BLK2_CFG_W2 0x1e25 +#define VIU2_OSD1_BLK3_CFG_W2 0x1e29 +#define VIU2_OSD1_BLK0_CFG_W3 0x1e1e +#define VIU2_OSD1_BLK1_CFG_W3 0x1e22 +#define VIU2_OSD1_BLK2_CFG_W3 0x1e26 +#define VIU2_OSD1_BLK3_CFG_W3 0x1e2a +#define VIU2_OSD1_BLK0_CFG_W4 0x1e13 +#define VIU2_OSD1_BLK1_CFG_W4 0x1e14 +#define VIU2_OSD1_BLK2_CFG_W4 0x1e15 +#define VIU2_OSD1_BLK3_CFG_W4 0x1e16 +#define VIU2_OSD1_FIFO_CTRL_STAT 0x1e2b +#define VIU2_OSD1_TEST_RDDATA 0x1e2c +#define VIU2_OSD1_PROT_CTRL 0x1e2e +#define VIU2_OSD2_CTRL_STAT 0x1e30 +#define VIU2_OSD2_CTRL_STAT2 0x1e4d +#define VIU2_OSD2_COLOR_ADDR 0x1e31 +#define VIU2_OSD2_COLOR 0x1e32 +#define VIU2_OSD2_HL1_H_START_END 0x1e33 +#define VIU2_OSD2_HL1_V_START_END 0x1e34 +#define VIU2_OSD2_HL2_H_START_END 0x1e35 +#define VIU2_OSD2_HL2_V_START_END 0x1e36 +#define VIU2_OSD2_TCOLOR_AG0 0x1e37 +#define VIU2_OSD2_TCOLOR_AG1 0x1e38 +#define VIU2_OSD2_TCOLOR_AG2 0x1e39 +#define VIU2_OSD2_TCOLOR_AG3 0x1e3a +#define VIU2_OSD2_BLK0_CFG_W0 0x1e3b +#define VIU2_OSD2_BLK1_CFG_W0 0x1e3f +#define VIU2_OSD2_BLK2_CFG_W0 0x1e43 +#define VIU2_OSD2_BLK3_CFG_W0 0x1e47 +#define VIU2_OSD2_BLK0_CFG_W1 0x1e3c +#define VIU2_OSD2_BLK1_CFG_W1 0x1e40 +#define VIU2_OSD2_BLK2_CFG_W1 0x1e44 +#define VIU2_OSD2_BLK3_CFG_W1 0x1e48 +#define VIU2_OSD2_BLK0_CFG_W2 0x1e3d +#define VIU2_OSD2_BLK1_CFG_W2 0x1e41 +#define VIU2_OSD2_BLK2_CFG_W2 0x1e45 +#define VIU2_OSD2_BLK3_CFG_W2 0x1e49 +#define VIU2_OSD2_BLK0_CFG_W3 0x1e3e +#define VIU2_OSD2_BLK1_CFG_W3 0x1e42 +#define VIU2_OSD2_BLK2_CFG_W3 0x1e46 +#define VIU2_OSD2_BLK3_CFG_W3 0x1e4a +#define VIU2_OSD2_BLK0_CFG_W4 0x1e64 +#define VIU2_OSD2_BLK1_CFG_W4 0x1e65 +#define VIU2_OSD2_BLK2_CFG_W4 0x1e66 +#define VIU2_OSD2_BLK3_CFG_W4 0x1e67 +#define VIU2_OSD2_FIFO_CTRL_STAT 0x1e4b +#define VIU2_OSD2_TEST_RDDATA 0x1e4c +#define VIU2_OSD2_PROT_CTRL 0x1e4e +#define VIU2_VD1_IF0_GEN_REG 0x1e50 +#define VIU2_VD1_IF0_CANVAS0 0x1e51 +#define VIU2_VD1_IF0_CANVAS1 0x1e52 +#define VIU2_VD1_IF0_LUMA_X0 0x1e53 +#define VIU2_VD1_IF0_LUMA_Y0 0x1e54 +#define VIU2_VD1_IF0_CHROMA_X0 0x1e55 +#define VIU2_VD1_IF0_CHROMA_Y0 0x1e56 +#define VIU2_VD1_IF0_LUMA_X1 0x1e57 +#define VIU2_VD1_IF0_LUMA_Y1 0x1e58 +#define VIU2_VD1_IF0_CHROMA_X1 0x1e59 +#define VIU2_VD1_IF0_CHROMA_Y1 0x1e5a +#define VIU2_VD1_IF0_RPT_LOOP 0x1e5b +#define VIU2_VD1_IF0_LUMA0_RPT_PAT 0x1e5c +#define VIU2_VD1_IF0_CHROMA0_RPT_PAT 0x1e5d +#define VIU2_VD1_IF0_LUMA1_RPT_PAT 0x1e5e +#define VIU2_VD1_IF0_CHROMA1_RPT_PAT 0x1e5f +#define VIU2_VD1_IF0_LUMA_PSEL 0x1e60 +#define VIU2_VD1_IF0_CHROMA_PSEL 0x1e61 +#define VIU2_VD1_IF0_DUMMY_PIXEL 0x1e62 +#define VIU2_VD1_IF0_LUMA_FIFO_SIZE 0x1e63 +#define VIU2_VD1_IF0_RANGE_MAP_Y 0x1e6a +#define VIU2_VD1_IF0_RANGE_MAP_CB 0x1e6b +#define VIU2_VD1_IF0_RANGE_MAP_CR 0x1e6c +#define VIU2_VD1_IF0_GEN_REG2 0x1e6d +#define VIU2_VD1_IF0_PROT_CNTL 0x1e6e +#define VIU2_VD1_FMT_CTRL 0x1e68 +#define VIU2_VD1_FMT_W 0x1e69 + +/* encode */ +#define ENCP_VFIFO2VD_CTL 0x1b58 +#define ENCP_VFIFO2VD_PIXEL_START 0x1b59 +#define ENCP_VFIFO2VD_PIXEL_END 0x1b5a +#define ENCP_VFIFO2VD_LINE_TOP_START 0x1b5b +#define ENCP_VFIFO2VD_LINE_TOP_END 0x1b5c +#define ENCP_VFIFO2VD_LINE_BOT_START 0x1b5d +#define ENCP_VFIFO2VD_LINE_BOT_END 0x1b5e +#define VENC_SYNC_ROUTE 0x1b60 +#define VENC_VIDEO_EXSRC 0x1b61 +#define VENC_DVI_SETTING 0x1b62 +#define VENC_C656_CTRL 0x1b63 +#define VENC_UPSAMPLE_CTRL0 0x1b64 +#define VENC_UPSAMPLE_CTRL1 0x1b65 +#define VENC_UPSAMPLE_CTRL2 0x1b66 +#define VENC_UPSAMPLE_CTRL_F0_2_CLK_RATIO BIT(0) +#define VENC_UPSAMPLE_CTRL_F1_EN BIT(5) +#define VENC_UPSAMPLE_CTRL_F1_UPSAMPLE_EN BIT(6) +#define VENC_UPSAMPLE_CTRL_INTERLACE_HIGH_LUMA (0x0 << 12) +#define VENC_UPSAMPLE_CTRL_CVBS (0x1 << 12) +#define VENC_UPSAMPLE_CTRL_S_VIDEO_LUMA (0x2 << 12) +#define VENC_UPSAMPLE_CTRL_S_VIDEO_CHROMA (0x3 << 12) +#define VENC_UPSAMPLE_CTRL_INTERLACE_PB (0x4 << 12) +#define VENC_UPSAMPLE_CTRL_INTERLACE_PR (0x5 << 12) +#define VENC_UPSAMPLE_CTRL_INTERLACE_R (0x6 << 12) +#define VENC_UPSAMPLE_CTRL_INTERLACE_G (0x7 << 12) +#define VENC_UPSAMPLE_CTRL_INTERLACE_B (0x8 << 12) +#define VENC_UPSAMPLE_CTRL_PROGRESSIVE_Y (0x9 << 12) +#define VENC_UPSAMPLE_CTRL_PROGRESSIVE_PB (0xa << 12) +#define VENC_UPSAMPLE_CTRL_PROGRESSIVE_PR (0xb << 12) +#define VENC_UPSAMPLE_CTRL_PROGRESSIVE_R (0xc << 12) +#define VENC_UPSAMPLE_CTRL_PROGRESSIVE_G (0xd << 12) +#define VENC_UPSAMPLE_CTRL_PROGRESSIVE_B (0xe << 12) +#define VENC_UPSAMPLE_CTRL_VDAC_TEST_VALUE (0xf << 12) +#define TCON_INVERT_CTL 0x1b67 +#define VENC_VIDEO_PROG_MODE 0x1b68 +#define VENC_ENCI_LINE 0x1b69 +#define VENC_ENCI_PIXEL 0x1b6a +#define VENC_ENCP_LINE 0x1b6b +#define VENC_ENCP_PIXEL 0x1b6c +#define VENC_STATA 0x1b6d +#define VENC_INTCTRL 0x1b6e +#define VENC_INTCTRL_ENCI_LNRST_INT_EN BIT(1) +#define VENC_INTCTRL_ENCP_LNRST_INT_EN BIT(9) +#define VENC_INTFLAG 0x1b6f +#define VENC_VIDEO_TST_EN 0x1b70 +#define VENC_VIDEO_TST_MDSEL 0x1b71 +#define VENC_VIDEO_TST_Y 0x1b72 +#define VENC_VIDEO_TST_CB 0x1b73 +#define VENC_VIDEO_TST_CR 0x1b74 +#define VENC_VIDEO_TST_CLRBAR_STRT 0x1b75 +#define VENC_VIDEO_TST_CLRBAR_WIDTH 0x1b76 +#define VENC_VIDEO_TST_VDCNT_STSET 0x1b77 +#define VENC_VDAC_DACSEL0 0x1b78 +#define VENC_VDAC_SEL_ATV_DMD BIT(5) +#define VENC_VDAC_DACSEL1 0x1b79 +#define VENC_VDAC_DACSEL2 0x1b7a +#define VENC_VDAC_DACSEL3 0x1b7b +#define VENC_VDAC_DACSEL4 0x1b7c +#define VENC_VDAC_DACSEL5 0x1b7d +#define VENC_VDAC_SETTING 0x1b7e +#define VENC_VDAC_TST_VAL 0x1b7f +#define VENC_VDAC_DAC0_GAINCTRL 0x1bf0 +#define VENC_VDAC_DAC0_OFFSET 0x1bf1 +#define VENC_VDAC_DAC1_GAINCTRL 0x1bf2 +#define VENC_VDAC_DAC1_OFFSET 0x1bf3 +#define VENC_VDAC_DAC2_GAINCTRL 0x1bf4 +#define VENC_VDAC_DAC2_OFFSET 0x1bf5 +#define VENC_VDAC_DAC3_GAINCTRL 0x1bf6 +#define VENC_VDAC_DAC3_OFFSET 0x1bf7 +#define VENC_VDAC_DAC4_GAINCTRL 0x1bf8 +#define VENC_VDAC_DAC4_OFFSET 0x1bf9 +#define VENC_VDAC_DAC5_GAINCTRL 0x1bfa +#define VENC_VDAC_DAC5_OFFSET 0x1bfb +#define VENC_VDAC_FIFO_CTRL 0x1bfc +#define VENC_VDAC_FIFO_EN_ENCI_ENABLE BIT(13) +#define ENCL_TCON_INVERT_CTL 0x1bfd +#define ENCP_VIDEO_EN 0x1b80 +#define ENCP_VIDEO_SYNC_MODE 0x1b81 +#define ENCP_MACV_EN 0x1b82 +#define ENCP_VIDEO_Y_SCL 0x1b83 +#define ENCP_VIDEO_PB_SCL 0x1b84 +#define ENCP_VIDEO_PR_SCL 0x1b85 +#define ENCP_VIDEO_SYNC_SCL 0x1b86 +#define ENCP_VIDEO_MACV_SCL 0x1b87 +#define ENCP_VIDEO_Y_OFFST 0x1b88 +#define ENCP_VIDEO_PB_OFFST 0x1b89 +#define ENCP_VIDEO_PR_OFFST 0x1b8a +#define ENCP_VIDEO_SYNC_OFFST 0x1b8b +#define ENCP_VIDEO_MACV_OFFST 0x1b8c +#define ENCP_VIDEO_MODE 0x1b8d +#define ENCP_VIDEO_MODE_DE_V_HIGH BIT(14) +#define ENCP_VIDEO_MODE_ADV 0x1b8e +#define ENCP_DBG_PX_RST 0x1b90 +#define ENCP_DBG_LN_RST 0x1b91 +#define ENCP_DBG_PX_INT 0x1b92 +#define ENCP_DBG_LN_INT 0x1b93 +#define ENCP_VIDEO_YFP1_HTIME 0x1b94 +#define ENCP_VIDEO_YFP2_HTIME 0x1b95 +#define ENCP_VIDEO_YC_DLY 0x1b96 +#define ENCP_VIDEO_MAX_PXCNT 0x1b97 +#define ENCP_VIDEO_HSPULS_BEGIN 0x1b98 +#define ENCP_VIDEO_HSPULS_END 0x1b99 +#define ENCP_VIDEO_HSPULS_SWITCH 0x1b9a +#define ENCP_VIDEO_VSPULS_BEGIN 0x1b9b +#define ENCP_VIDEO_VSPULS_END 0x1b9c +#define ENCP_VIDEO_VSPULS_BLINE 0x1b9d +#define ENCP_VIDEO_VSPULS_ELINE 0x1b9e +#define ENCP_VIDEO_EQPULS_BEGIN 0x1b9f +#define ENCP_VIDEO_EQPULS_END 0x1ba0 +#define ENCP_VIDEO_EQPULS_BLINE 0x1ba1 +#define ENCP_VIDEO_EQPULS_ELINE 0x1ba2 +#define ENCP_VIDEO_HAVON_END 0x1ba3 +#define ENCP_VIDEO_HAVON_BEGIN 0x1ba4 +#define ENCP_VIDEO_VAVON_ELINE 0x1baf +#define ENCP_VIDEO_VAVON_BLINE 0x1ba6 +#define ENCP_VIDEO_HSO_BEGIN 0x1ba7 +#define ENCP_VIDEO_HSO_END 0x1ba8 +#define ENCP_VIDEO_VSO_BEGIN 0x1ba9 +#define ENCP_VIDEO_VSO_END 0x1baa +#define ENCP_VIDEO_VSO_BLINE 0x1bab +#define ENCP_VIDEO_VSO_ELINE 0x1bac +#define ENCP_VIDEO_SYNC_WAVE_CURVE 0x1bad +#define ENCP_VIDEO_MAX_LNCNT 0x1bae +#define ENCP_VIDEO_SY_VAL 0x1bb0 +#define ENCP_VIDEO_SY2_VAL 0x1bb1 +#define ENCP_VIDEO_BLANKY_VAL 0x1bb2 +#define ENCP_VIDEO_BLANKPB_VAL 0x1bb3 +#define ENCP_VIDEO_BLANKPR_VAL 0x1bb4 +#define ENCP_VIDEO_HOFFST 0x1bb5 +#define ENCP_VIDEO_VOFFST 0x1bb6 +#define ENCP_VIDEO_RGB_CTRL 0x1bb7 +#define ENCP_VIDEO_FILT_CTRL 0x1bb8 +#define ENCP_VIDEO_OFLD_VPEQ_OFST 0x1bb9 +#define ENCP_VIDEO_OFLD_VOAV_OFST 0x1bba +#define ENCP_VIDEO_MATRIX_CB 0x1bbb +#define ENCP_VIDEO_MATRIX_CR 0x1bbc +#define ENCP_VIDEO_RGBIN_CTRL 0x1bbd +#define ENCP_MACV_BLANKY_VAL 0x1bc0 +#define ENCP_MACV_MAXY_VAL 0x1bc1 +#define ENCP_MACV_1ST_PSSYNC_STRT 0x1bc2 +#define ENCP_MACV_PSSYNC_STRT 0x1bc3 +#define ENCP_MACV_AGC_STRT 0x1bc4 +#define ENCP_MACV_AGC_END 0x1bc5 +#define ENCP_MACV_WAVE_END 0x1bc6 +#define ENCP_MACV_STRTLINE 0x1bc7 +#define ENCP_MACV_ENDLINE 0x1bc8 +#define ENCP_MACV_TS_CNT_MAX_L 0x1bc9 +#define ENCP_MACV_TS_CNT_MAX_H 0x1bca +#define ENCP_MACV_TIME_DOWN 0x1bcb +#define ENCP_MACV_TIME_LO 0x1bcc +#define ENCP_MACV_TIME_UP 0x1bcd +#define ENCP_MACV_TIME_RST 0x1bce +#define ENCP_VBI_CTRL 0x1bd0 +#define ENCP_VBI_SETTING 0x1bd1 +#define ENCP_VBI_BEGIN 0x1bd2 +#define ENCP_VBI_WIDTH 0x1bd3 +#define ENCP_VBI_HVAL 0x1bd4 +#define ENCP_VBI_DATA0 0x1bd5 +#define ENCP_VBI_DATA1 0x1bd6 +#define C656_HS_ST 0x1be0 +#define C656_HS_ED 0x1be1 +#define C656_VS_LNST_E 0x1be2 +#define C656_VS_LNST_O 0x1be3 +#define C656_VS_LNED_E 0x1be4 +#define C656_VS_LNED_O 0x1be5 +#define C656_FS_LNST 0x1be6 +#define C656_FS_LNED 0x1be7 +#define ENCI_VIDEO_MODE 0x1b00 +#define ENCI_VIDEO_MODE_ADV 0x1b01 +#define ENCI_VIDEO_MODE_ADV_DMXMD(val) (val & 0x3) +#define ENCI_VIDEO_MODE_ADV_VBICTL_LINE_17_22 BIT(2) +#define ENCI_VIDEO_MODE_ADV_YBW_MEDIUM (0 << 4) +#define ENCI_VIDEO_MODE_ADV_YBW_LOW (0x1 << 4) +#define ENCI_VIDEO_MODE_ADV_YBW_HIGH (0x2 << 4) +#define ENCI_VIDEO_FSC_ADJ 0x1b02 +#define ENCI_VIDEO_BRIGHT 0x1b03 +#define ENCI_VIDEO_CONT 0x1b04 +#define ENCI_VIDEO_SAT 0x1b05 +#define ENCI_VIDEO_HUE 0x1b06 +#define ENCI_VIDEO_SCH 0x1b07 +#define ENCI_SYNC_MODE 0x1b08 +#define ENCI_SYNC_CTRL 0x1b09 +#define ENCI_SYNC_HSO_BEGIN 0x1b0a +#define ENCI_SYNC_HSO_END 0x1b0b +#define ENCI_SYNC_VSO_EVN 0x1b0c +#define ENCI_SYNC_VSO_ODD 0x1b0d +#define ENCI_SYNC_VSO_EVNLN 0x1b0e +#define ENCI_SYNC_VSO_ODDLN 0x1b0f +#define ENCI_SYNC_HOFFST 0x1b10 +#define ENCI_SYNC_VOFFST 0x1b11 +#define ENCI_SYNC_ADJ 0x1b12 +#define ENCI_RGB_SETTING 0x1b13 +#define ENCI_DE_H_BEGIN 0x1b16 +#define ENCI_DE_H_END 0x1b17 +#define ENCI_DE_V_BEGIN_EVEN 0x1b18 +#define ENCI_DE_V_END_EVEN 0x1b19 +#define ENCI_DE_V_BEGIN_ODD 0x1b1a +#define ENCI_DE_V_END_ODD 0x1b1b +#define ENCI_VBI_SETTING 0x1b20 +#define ENCI_VBI_CCDT_EVN 0x1b21 +#define ENCI_VBI_CCDT_ODD 0x1b22 +#define ENCI_VBI_CC525_LN 0x1b23 +#define ENCI_VBI_CC625_LN 0x1b24 +#define ENCI_VBI_WSSDT 0x1b25 +#define ENCI_VBI_WSS_LN 0x1b26 +#define ENCI_VBI_CGMSDT_L 0x1b27 +#define ENCI_VBI_CGMSDT_H 0x1b28 +#define ENCI_VBI_CGMS_LN 0x1b29 +#define ENCI_VBI_TTX_HTIME 0x1b2a +#define ENCI_VBI_TTX_LN 0x1b2b +#define ENCI_VBI_TTXDT0 0x1b2c +#define ENCI_VBI_TTXDT1 0x1b2d +#define ENCI_VBI_TTXDT2 0x1b2e +#define ENCI_VBI_TTXDT3 0x1b2f +#define ENCI_MACV_N0 0x1b30 +#define ENCI_MACV_N1 0x1b31 +#define ENCI_MACV_N2 0x1b32 +#define ENCI_MACV_N3 0x1b33 +#define ENCI_MACV_N4 0x1b34 +#define ENCI_MACV_N5 0x1b35 +#define ENCI_MACV_N6 0x1b36 +#define ENCI_MACV_N7 0x1b37 +#define ENCI_MACV_N8 0x1b38 +#define ENCI_MACV_N9 0x1b39 +#define ENCI_MACV_N10 0x1b3a +#define ENCI_MACV_N11 0x1b3b +#define ENCI_MACV_N12 0x1b3c +#define ENCI_MACV_N13 0x1b3d +#define ENCI_MACV_N14 0x1b3e +#define ENCI_MACV_N15 0x1b3f +#define ENCI_MACV_N16 0x1b40 +#define ENCI_MACV_N17 0x1b41 +#define ENCI_MACV_N18 0x1b42 +#define ENCI_MACV_N19 0x1b43 +#define ENCI_MACV_N20 0x1b44 +#define ENCI_MACV_N21 0x1b45 +#define ENCI_MACV_N22 0x1b46 +#define ENCI_DBG_PX_RST 0x1b48 +#define ENCI_DBG_FLDLN_RST 0x1b49 +#define ENCI_DBG_PX_INT 0x1b4a +#define ENCI_DBG_FLDLN_INT 0x1b4b +#define ENCI_DBG_MAXPX 0x1b4c +#define ENCI_DBG_MAXLN 0x1b4d +#define ENCI_MACV_MAX_AMP 0x1b50 +#define ENCI_MACV_MAX_AMP_ENABLE_CHANGE BIT(15) +#define ENCI_MACV_MAX_AMP_VAL(val) (val & 0x83ff) +#define ENCI_MACV_PULSE_LO 0x1b51 +#define ENCI_MACV_PULSE_HI 0x1b52 +#define ENCI_MACV_BKP_MAX 0x1b53 +#define ENCI_CFILT_CTRL 0x1b54 +#define ENCI_CFILT_CMPT_SEL_HIGH BIT(1) +#define ENCI_CFILT7 0x1b55 +#define ENCI_YC_DELAY 0x1b56 +#define ENCI_VIDEO_EN 0x1b57 +#define ENCI_VIDEO_EN_ENABLE BIT(0) +#define ENCI_DVI_HSO_BEGIN 0x1c00 +#define ENCI_DVI_HSO_END 0x1c01 +#define ENCI_DVI_VSO_BLINE_EVN 0x1c02 +#define ENCI_DVI_VSO_BLINE_ODD 0x1c03 +#define ENCI_DVI_VSO_ELINE_EVN 0x1c04 +#define ENCI_DVI_VSO_ELINE_ODD 0x1c05 +#define ENCI_DVI_VSO_BEGIN_EVN 0x1c06 +#define ENCI_DVI_VSO_BEGIN_ODD 0x1c07 +#define ENCI_DVI_VSO_END_EVN 0x1c08 +#define ENCI_DVI_VSO_END_ODD 0x1c09 +#define ENCI_CFILT_CTRL2 0x1c0a +#define ENCI_CFILT_CMPT_CR_DLY(delay) (delay & 0xf) +#define ENCI_CFILT_CMPT_CB_DLY(delay) ((delay & 0xf) << 4) +#define ENCI_CFILT_CVBS_CR_DLY(delay) ((delay & 0xf) << 8) +#define ENCI_CFILT_CVBS_CB_DLY(delay) ((delay & 0xf) << 12) +#define ENCI_DACSEL_0 0x1c0b +#define ENCI_DACSEL_1 0x1c0c +#define ENCP_DACSEL_0 0x1c0d +#define ENCP_DACSEL_1 0x1c0e +#define ENCP_MAX_LINE_SWITCH_POINT 0x1c0f +#define ENCI_TST_EN 0x1c10 +#define ENCI_TST_MDSEL 0x1c11 +#define ENCI_TST_Y 0x1c12 +#define ENCI_TST_CB 0x1c13 +#define ENCI_TST_CR 0x1c14 +#define ENCI_TST_CLRBAR_STRT 0x1c15 +#define ENCI_TST_CLRBAR_WIDTH 0x1c16 +#define ENCI_TST_VDCNT_STSET 0x1c17 +#define ENCI_VFIFO2VD_CTL 0x1c18 +#define ENCI_VFIFO2VD_CTL_ENABLE BIT(0) +#define ENCI_VFIFO2VD_CTL_VD_SEL(val) ((val & 0xff) << 8) +#define ENCI_VFIFO2VD_PIXEL_START 0x1c19 +#define ENCI_VFIFO2VD_PIXEL_END 0x1c1a +#define ENCI_VFIFO2VD_LINE_TOP_START 0x1c1b +#define ENCI_VFIFO2VD_LINE_TOP_END 0x1c1c +#define ENCI_VFIFO2VD_LINE_BOT_START 0x1c1d +#define ENCI_VFIFO2VD_LINE_BOT_END 0x1c1e +#define ENCI_VFIFO2VD_CTL2 0x1c1f +#define ENCT_VFIFO2VD_CTL 0x1c20 +#define ENCT_VFIFO2VD_PIXEL_START 0x1c21 +#define ENCT_VFIFO2VD_PIXEL_END 0x1c22 +#define ENCT_VFIFO2VD_LINE_TOP_START 0x1c23 +#define ENCT_VFIFO2VD_LINE_TOP_END 0x1c24 +#define ENCT_VFIFO2VD_LINE_BOT_START 0x1c25 +#define ENCT_VFIFO2VD_LINE_BOT_END 0x1c26 +#define ENCT_VFIFO2VD_CTL2 0x1c27 +#define ENCT_TST_EN 0x1c28 +#define ENCT_TST_MDSEL 0x1c29 +#define ENCT_TST_Y 0x1c2a +#define ENCT_TST_CB 0x1c2b +#define ENCT_TST_CR 0x1c2c +#define ENCT_TST_CLRBAR_STRT 0x1c2d +#define ENCT_TST_CLRBAR_WIDTH 0x1c2e +#define ENCT_TST_VDCNT_STSET 0x1c2f +#define ENCP_DVI_HSO_BEGIN 0x1c30 +#define ENCP_DVI_HSO_END 0x1c31 +#define ENCP_DVI_VSO_BLINE_EVN 0x1c32 +#define ENCP_DVI_VSO_BLINE_ODD 0x1c33 +#define ENCP_DVI_VSO_ELINE_EVN 0x1c34 +#define ENCP_DVI_VSO_ELINE_ODD 0x1c35 +#define ENCP_DVI_VSO_BEGIN_EVN 0x1c36 +#define ENCP_DVI_VSO_BEGIN_ODD 0x1c37 +#define ENCP_DVI_VSO_END_EVN 0x1c38 +#define ENCP_DVI_VSO_END_ODD 0x1c39 +#define ENCP_DE_H_BEGIN 0x1c3a +#define ENCP_DE_H_END 0x1c3b +#define ENCP_DE_V_BEGIN_EVEN 0x1c3c +#define ENCP_DE_V_END_EVEN 0x1c3d +#define ENCP_DE_V_BEGIN_ODD 0x1c3e +#define ENCP_DE_V_END_ODD 0x1c3f +#define ENCI_SYNC_LINE_LENGTH 0x1c40 +#define ENCI_SYNC_PIXEL_EN 0x1c41 +#define ENCI_SYNC_TO_LINE_EN 0x1c42 +#define ENCI_SYNC_TO_PIXEL 0x1c43 +#define ENCP_SYNC_LINE_LENGTH 0x1c44 +#define ENCP_SYNC_PIXEL_EN 0x1c45 +#define ENCP_SYNC_TO_LINE_EN 0x1c46 +#define ENCP_SYNC_TO_PIXEL 0x1c47 +#define ENCT_SYNC_LINE_LENGTH 0x1c48 +#define ENCT_SYNC_PIXEL_EN 0x1c49 +#define ENCT_SYNC_TO_LINE_EN 0x1c4a +#define ENCT_SYNC_TO_PIXEL 0x1c4b +#define ENCL_SYNC_LINE_LENGTH 0x1c4c +#define ENCL_SYNC_PIXEL_EN 0x1c4d +#define ENCL_SYNC_TO_LINE_EN 0x1c4e +#define ENCL_SYNC_TO_PIXEL 0x1c4f +#define ENCP_VFIFO2VD_CTL2 0x1c50 +#define VENC_DVI_SETTING_MORE 0x1c51 +#define VENC_VDAC_DAC4_FILT_CTRL0 0x1c54 +#define VENC_VDAC_DAC4_FILT_CTRL1 0x1c55 +#define VENC_VDAC_DAC5_FILT_CTRL0 0x1c56 +#define VENC_VDAC_DAC5_FILT_CTRL1 0x1c57 +#define VENC_VDAC_DAC0_FILT_CTRL0 0x1c58 +#define VENC_VDAC_DAC0_FILT_CTRL0_EN BIT(0) +#define VENC_VDAC_DAC0_FILT_CTRL1 0x1c59 +#define VENC_VDAC_DAC1_FILT_CTRL0 0x1c5a +#define VENC_VDAC_DAC1_FILT_CTRL1 0x1c5b +#define VENC_VDAC_DAC2_FILT_CTRL0 0x1c5c +#define VENC_VDAC_DAC2_FILT_CTRL1 0x1c5d +#define VENC_VDAC_DAC3_FILT_CTRL0 0x1c5e +#define VENC_VDAC_DAC3_FILT_CTRL1 0x1c5f +#define ENCT_VIDEO_EN 0x1c60 +#define ENCT_VIDEO_Y_SCL 0x1c61 +#define ENCT_VIDEO_PB_SCL 0x1c62 +#define ENCT_VIDEO_PR_SCL 0x1c63 +#define ENCT_VIDEO_Y_OFFST 0x1c64 +#define ENCT_VIDEO_PB_OFFST 0x1c65 +#define ENCT_VIDEO_PR_OFFST 0x1c66 +#define ENCT_VIDEO_MODE 0x1c67 +#define ENCT_VIDEO_MODE_ADV 0x1c68 +#define ENCT_DBG_PX_RST 0x1c69 +#define ENCT_DBG_LN_RST 0x1c6a +#define ENCT_DBG_PX_INT 0x1c6b +#define ENCT_DBG_LN_INT 0x1c6c +#define ENCT_VIDEO_YFP1_HTIME 0x1c6d +#define ENCT_VIDEO_YFP2_HTIME 0x1c6e +#define ENCT_VIDEO_YC_DLY 0x1c6f +#define ENCT_VIDEO_MAX_PXCNT 0x1c70 +#define ENCT_VIDEO_HAVON_END 0x1c71 +#define ENCT_VIDEO_HAVON_BEGIN 0x1c72 +#define ENCT_VIDEO_VAVON_ELINE 0x1c73 +#define ENCT_VIDEO_VAVON_BLINE 0x1c74 +#define ENCT_VIDEO_HSO_BEGIN 0x1c75 +#define ENCT_VIDEO_HSO_END 0x1c76 +#define ENCT_VIDEO_VSO_BEGIN 0x1c77 +#define ENCT_VIDEO_VSO_END 0x1c78 +#define ENCT_VIDEO_VSO_BLINE 0x1c79 +#define ENCT_VIDEO_VSO_ELINE 0x1c7a +#define ENCT_VIDEO_MAX_LNCNT 0x1c7b +#define ENCT_VIDEO_BLANKY_VAL 0x1c7c +#define ENCT_VIDEO_BLANKPB_VAL 0x1c7d +#define ENCT_VIDEO_BLANKPR_VAL 0x1c7e +#define ENCT_VIDEO_HOFFST 0x1c7f +#define ENCT_VIDEO_VOFFST 0x1c80 +#define ENCT_VIDEO_RGB_CTRL 0x1c81 +#define ENCT_VIDEO_FILT_CTRL 0x1c82 +#define ENCT_VIDEO_OFLD_VPEQ_OFST 0x1c83 +#define ENCT_VIDEO_OFLD_VOAV_OFST 0x1c84 +#define ENCT_VIDEO_MATRIX_CB 0x1c85 +#define ENCT_VIDEO_MATRIX_CR 0x1c86 +#define ENCT_VIDEO_RGBIN_CTRL 0x1c87 +#define ENCT_MAX_LINE_SWITCH_POINT 0x1c88 +#define ENCT_DACSEL_0 0x1c89 +#define ENCT_DACSEL_1 0x1c8a +#define ENCL_VFIFO2VD_CTL 0x1c90 +#define ENCL_VFIFO2VD_PIXEL_START 0x1c91 +#define ENCL_VFIFO2VD_PIXEL_END 0x1c92 +#define ENCL_VFIFO2VD_LINE_TOP_START 0x1c93 +#define ENCL_VFIFO2VD_LINE_TOP_END 0x1c94 +#define ENCL_VFIFO2VD_LINE_BOT_START 0x1c95 +#define ENCL_VFIFO2VD_LINE_BOT_END 0x1c96 +#define ENCL_VFIFO2VD_CTL2 0x1c97 +#define ENCL_TST_EN 0x1c98 +#define ENCL_TST_MDSEL 0x1c99 +#define ENCL_TST_Y 0x1c9a +#define ENCL_TST_CB 0x1c9b +#define ENCL_TST_CR 0x1c9c +#define ENCL_TST_CLRBAR_STRT 0x1c9d +#define ENCL_TST_CLRBAR_WIDTH 0x1c9e +#define ENCL_TST_VDCNT_STSET 0x1c9f +#define ENCL_VIDEO_EN 0x1ca0 +#define ENCL_VIDEO_Y_SCL 0x1ca1 +#define ENCL_VIDEO_PB_SCL 0x1ca2 +#define ENCL_VIDEO_PR_SCL 0x1ca3 +#define ENCL_VIDEO_Y_OFFST 0x1ca4 +#define ENCL_VIDEO_PB_OFFST 0x1ca5 +#define ENCL_VIDEO_PR_OFFST 0x1ca6 +#define ENCL_VIDEO_MODE 0x1ca7 +#define ENCL_PX_LN_CNT_SHADOW_EN BIT(15) +#define ENCL_VIDEO_MODE_ADV 0x1ca8 +#define ENCL_VIDEO_MODE_ADV_VFIFO_EN BIT(3) +#define ENCL_VIDEO_MODE_ADV_GAIN_HDTV BIT(4) +#define ENCL_SEL_GAMMA_RGB_IN BIT(10) +#define ENCL_DBG_PX_RST 0x1ca9 +#define ENCL_DBG_LN_RST 0x1caa +#define ENCL_DBG_PX_INT 0x1cab +#define ENCL_DBG_LN_INT 0x1cac +#define ENCL_VIDEO_YFP1_HTIME 0x1cad +#define ENCL_VIDEO_YFP2_HTIME 0x1cae +#define ENCL_VIDEO_YC_DLY 0x1caf +#define ENCL_VIDEO_MAX_PXCNT 0x1cb0 +#define ENCL_VIDEO_HAVON_END 0x1cb1 +#define ENCL_VIDEO_HAVON_BEGIN 0x1cb2 +#define ENCL_VIDEO_VAVON_ELINE 0x1cb3 +#define ENCL_VIDEO_VAVON_BLINE 0x1cb4 +#define ENCL_VIDEO_HSO_BEGIN 0x1cb5 +#define ENCL_VIDEO_HSO_END 0x1cb6 +#define ENCL_VIDEO_VSO_BEGIN 0x1cb7 +#define ENCL_VIDEO_VSO_END 0x1cb8 +#define ENCL_VIDEO_VSO_BLINE 0x1cb9 +#define ENCL_VIDEO_VSO_ELINE 0x1cba +#define ENCL_VIDEO_MAX_LNCNT 0x1cbb +#define ENCL_VIDEO_BLANKY_VAL 0x1cbc +#define ENCL_VIDEO_BLANKPB_VAL 0x1cbd +#define ENCL_VIDEO_BLANKPR_VAL 0x1cbe +#define ENCL_VIDEO_HOFFST 0x1cbf +#define ENCL_VIDEO_VOFFST 0x1cc0 +#define ENCL_VIDEO_RGB_CTRL 0x1cc1 +#define ENCL_VIDEO_FILT_CTRL 0x1cc2 +#define ENCL_VIDEO_FILT_CTRL_BYPASS_FILTER BIT(12) +#define ENCL_VIDEO_OFLD_VPEQ_OFST 0x1cc3 +#define ENCL_VIDEO_OFLD_VOAV_OFST 0x1cc4 +#define ENCL_VIDEO_MATRIX_CB 0x1cc5 +#define ENCL_VIDEO_MATRIX_CR 0x1cc6 +#define ENCL_VIDEO_RGBIN_CTRL 0x1cc7 +#define ENCL_VIDEO_RGBIN_RGB BIT(0) +#define ENCL_VIDEO_RGBIN_ZBLK BIT(1) +#define ENCL_MAX_LINE_SWITCH_POINT 0x1cc8 +#define ENCL_DACSEL_0 0x1cc9 +#define ENCL_DACSEL_1 0x1cca +#define RDMA_AHB_START_ADDR_MAN 0x1100 +#define RDMA_AHB_END_ADDR_MAN 0x1101 +#define RDMA_AHB_START_ADDR_1 0x1102 +#define RDMA_AHB_END_ADDR_1 0x1103 +#define RDMA_AHB_START_ADDR_2 0x1104 +#define RDMA_AHB_END_ADDR_2 0x1105 +#define RDMA_AHB_START_ADDR_3 0x1106 +#define RDMA_AHB_END_ADDR_3 0x1107 +#define RDMA_AHB_START_ADDR_4 0x1108 +#define RDMA_AHB_END_ADDR_4 0x1109 +#define RDMA_AHB_START_ADDR_5 0x110a +#define RDMA_AHB_END_ADDR_5 0x110b +#define RDMA_AHB_START_ADDR_6 0x110c +#define RDMA_AHB_END_ADDR_6 0x110d +#define RDMA_AHB_START_ADDR_7 0x110e +#define RDMA_AHB_END_ADDR_7 0x110f +#define RDMA_ACCESS_AUTO 0x1110 +#define RDMA_ACCESS_TRIGGER_CHAN3 GENMASK(31, 24) +#define RDMA_ACCESS_TRIGGER_CHAN2 GENMASK(23, 16) +#define RDMA_ACCESS_TRIGGER_CHAN1 GENMASK(15, 8) +#define RDMA_ACCESS_TRIGGER_STOP 0 +#define RDMA_ACCESS_TRIGGER_VSYNC 1 +#define RDMA_ACCESS_TRIGGER_LINE 32 +#define RDMA_ACCESS_RW_FLAG_CHAN3 BIT(7) +#define RDMA_ACCESS_RW_FLAG_CHAN2 BIT(6) +#define RDMA_ACCESS_RW_FLAG_CHAN1 BIT(5) +#define RDMA_ACCESS_ADDR_INC_CHAN3 BIT(3) +#define RDMA_ACCESS_ADDR_INC_CHAN2 BIT(2) +#define RDMA_ACCESS_ADDR_INC_CHAN1 BIT(1) +#define RDMA_ACCESS_AUTO2 0x1111 +#define RDMA_ACCESS_RW_FLAG_CHAN7 BIT(7) +#define RDMA_ACCESS_RW_FLAG_CHAN6 BIT(6) +#define RDMA_ACCESS_RW_FLAG_CHAN5 BIT(5) +#define RDMA_ACCESS_RW_FLAG_CHAN4 BIT(4) +#define RDMA_ACCESS_ADDR_INC_CHAN7 BIT(3) +#define RDMA_ACCESS_ADDR_INC_CHAN6 BIT(2) +#define RDMA_ACCESS_ADDR_INC_CHAN5 BIT(1) +#define RDMA_ACCESS_ADDR_INC_CHAN4 BIT(0) +#define RDMA_ACCESS_AUTO3 0x1112 +#define RDMA_ACCESS_TRIGGER_CHAN7 GENMASK(31, 24) +#define RDMA_ACCESS_TRIGGER_CHAN6 GENMASK(23, 16) +#define RDMA_ACCESS_TRIGGER_CHAN5 GENMASK(15, 8) +#define RDMA_ACCESS_TRIGGER_CHAN4 GENMASK(7, 0) +#define RDMA_ACCESS_MAN 0x1113 +#define RDMA_ACCESS_MAN_RW_FLAG BIT(2) +#define RDMA_ACCESS_MAN_ADDR_INC BIT(1) +#define RDMA_ACCESS_MAN_START BIT(0) +#define RDMA_CTRL 0x1114 +#define RDMA_IRQ_CLEAR_CHAN7 BIT(31) +#define RDMA_IRQ_CLEAR_CHAN6 BIT(30) +#define RDMA_IRQ_CLEAR_CHAN5 BIT(29) +#define RDMA_IRQ_CLEAR_CHAN4 BIT(28) +#define RDMA_IRQ_CLEAR_CHAN3 BIT(27) +#define RDMA_IRQ_CLEAR_CHAN2 BIT(26) +#define RDMA_IRQ_CLEAR_CHAN1 BIT(25) +#define RDMA_IRQ_CLEAR_CHAN_MAN BIT(24) +#define RDMA_DEFAULT_CONFIG (BIT(7) | BIT(6)) +#define RDMA_CTRL_AHB_WR_BURST GENMASK(5, 4) +#define RDMA_CTRL_AHB_RD_BURST GENMASK(3, 2) +#define RDMA_CTRL_SW_RESET BIT(1) +#define RDMA_CTRL_FREE_CLK_EN BIT(0) +#define RDMA_STATUS 0x1115 +#define RDMA_IRQ_STAT_CHAN7 BIT(31) +#define RDMA_IRQ_STAT_CHAN6 BIT(30) +#define RDMA_IRQ_STAT_CHAN5 BIT(29) +#define RDMA_IRQ_STAT_CHAN4 BIT(28) +#define RDMA_IRQ_STAT_CHAN3 BIT(27) +#define RDMA_IRQ_STAT_CHAN2 BIT(26) +#define RDMA_IRQ_STAT_CHAN1 BIT(25) +#define RDMA_IRQ_STAT_CHAN_MAN BIT(24) +#define RDMA_STATUS2 0x1116 +#define RDMA_STATUS3 0x1117 +#define L_GAMMA_CNTL_PORT 0x1400 +#define L_GAMMA_CNTL_PORT_VCOM_POL BIT(7) /* RW */ +#define L_GAMMA_CNTL_PORT_RVS_OUT BIT(6) /* RW */ +#define L_GAMMA_CNTL_PORT_ADR_RDY BIT(5) /* Read Only */ +#define L_GAMMA_CNTL_PORT_WR_RDY BIT(4) /* Read Only */ +#define L_GAMMA_CNTL_PORT_RD_RDY BIT(3) /* Read Only */ +#define L_GAMMA_CNTL_PORT_TR BIT(2) /* RW */ +#define L_GAMMA_CNTL_PORT_SET BIT(1) /* RW */ +#define L_GAMMA_CNTL_PORT_EN BIT(0) /* RW */ +#define L_GAMMA_DATA_PORT 0x1401 +#define L_GAMMA_ADDR_PORT 0x1402 +#define L_GAMMA_ADDR_PORT_RD BIT(12) +#define L_GAMMA_ADDR_PORT_AUTO_INC BIT(11) +#define L_GAMMA_ADDR_PORT_SEL_R BIT(10) +#define L_GAMMA_ADDR_PORT_SEL_G BIT(9) +#define L_GAMMA_ADDR_PORT_SEL_B BIT(8) +#define L_GAMMA_ADDR_PORT_ADDR GENMASK(7, 0) +#define L_GAMMA_VCOM_HSWITCH_ADDR 0x1403 +#define L_RGB_BASE_ADDR 0x1405 +#define L_RGB_COEFF_ADDR 0x1406 +#define L_POL_CNTL_ADDR 0x1407 +#define L_DITH_CNTL_ADDR 0x1408 +#define L_DITH_CNTL_DITH10_EN BIT(10) +#define L_GAMMA_PROBE_CTRL 0x1409 +#define L_GAMMA_PROBE_COLOR_L 0x140a +#define L_GAMMA_PROBE_COLOR_H 0x140b +#define L_GAMMA_PROBE_HL_COLOR 0x140c +#define L_GAMMA_PROBE_POS_X 0x140d +#define L_GAMMA_PROBE_POS_Y 0x140e +#define L_STH1_HS_ADDR 0x1410 +#define L_STH1_HE_ADDR 0x1411 +#define L_STH1_VS_ADDR 0x1412 +#define L_STH1_VE_ADDR 0x1413 +#define L_STH2_HS_ADDR 0x1414 +#define L_STH2_HE_ADDR 0x1415 +#define L_STH2_VS_ADDR 0x1416 +#define L_STH2_VE_ADDR 0x1417 +#define L_OEH_HS_ADDR 0x1418 +#define L_OEH_HE_ADDR 0x1419 +#define L_OEH_VS_ADDR 0x141a +#define L_OEH_VE_ADDR 0x141b +#define L_VCOM_HSWITCH_ADDR 0x141c +#define L_VCOM_VS_ADDR 0x141d +#define L_VCOM_VE_ADDR 0x141e +#define L_CPV1_HS_ADDR 0x141f +#define L_CPV1_HE_ADDR 0x1420 +#define L_CPV1_VS_ADDR 0x1421 +#define L_CPV1_VE_ADDR 0x1422 +#define L_CPV2_HS_ADDR 0x1423 +#define L_CPV2_HE_ADDR 0x1424 +#define L_CPV2_VS_ADDR 0x1425 +#define L_CPV2_VE_ADDR 0x1426 +#define L_STV1_HS_ADDR 0x1427 +#define L_STV1_HE_ADDR 0x1428 +#define L_STV1_VS_ADDR 0x1429 +#define L_STV1_VE_ADDR 0x142a +#define L_STV2_HS_ADDR 0x142b +#define L_STV2_HE_ADDR 0x142c +#define L_STV2_VS_ADDR 0x142d +#define L_STV2_VE_ADDR 0x142e +#define L_OEV1_HS_ADDR 0x142f +#define L_OEV1_HE_ADDR 0x1430 +#define L_OEV1_VS_ADDR 0x1431 +#define L_OEV1_VE_ADDR 0x1432 +#define L_OEV2_HS_ADDR 0x1433 +#define L_OEV2_HE_ADDR 0x1434 +#define L_OEV2_VS_ADDR 0x1435 +#define L_OEV2_VE_ADDR 0x1436 +#define L_OEV3_HS_ADDR 0x1437 +#define L_OEV3_HE_ADDR 0x1438 +#define L_OEV3_VS_ADDR 0x1439 +#define L_OEV3_VE_ADDR 0x143a +#define L_LCD_PWR_ADDR 0x143b +#define L_LCD_PWM0_LO_ADDR 0x143c +#define L_LCD_PWM0_HI_ADDR 0x143d +#define L_LCD_PWM1_LO_ADDR 0x143e +#define L_LCD_PWM1_HI_ADDR 0x143f +#define L_INV_CNT_ADDR 0x1440 +#define L_TCON_MISC_SEL_ADDR 0x1441 +#define L_TCON_MISC_SEL_STV1 BIT(4) +#define L_TCON_MISC_SEL_STV2 BIT(5) +#define L_DUAL_PORT_CNTL_ADDR 0x1442 +#define MLVDS_CLK_CTL1_HI 0x1443 +#define MLVDS_CLK_CTL1_LO 0x1444 +#define L_TCON_DOUBLE_CTL 0x1449 +#define L_TCON_PATTERN_HI 0x144a +#define L_TCON_PATTERN_LO 0x144b +#define LDIM_BL_ADDR_PORT 0x144e +#define LDIM_BL_DATA_PORT 0x144f +#define L_DE_HS_ADDR 0x1451 +#define L_DE_HE_ADDR 0x1452 +#define L_DE_VS_ADDR 0x1453 +#define L_DE_VE_ADDR 0x1454 +#define L_HSYNC_HS_ADDR 0x1455 +#define L_HSYNC_HE_ADDR 0x1456 +#define L_HSYNC_VS_ADDR 0x1457 +#define L_HSYNC_VE_ADDR 0x1458 +#define L_VSYNC_HS_ADDR 0x1459 +#define L_VSYNC_HE_ADDR 0x145a +#define L_VSYNC_VS_ADDR 0x145b +#define L_VSYNC_VE_ADDR 0x145c +#define L_LCD_MCU_CTL 0x145d +#define DUAL_MLVDS_CTL 0x1460 +#define DUAL_MLVDS_LINE_START 0x1461 +#define DUAL_MLVDS_LINE_END 0x1462 +#define DUAL_MLVDS_PIXEL_W_START_L 0x1463 +#define DUAL_MLVDS_PIXEL_W_END_L 0x1464 +#define DUAL_MLVDS_PIXEL_W_START_R 0x1465 +#define DUAL_MLVDS_PIXEL_W_END_R 0x1466 +#define DUAL_MLVDS_PIXEL_R_START_L 0x1467 +#define DUAL_MLVDS_PIXEL_R_CNT_L 0x1468 +#define DUAL_MLVDS_PIXEL_R_START_R 0x1469 +#define DUAL_MLVDS_PIXEL_R_CNT_R 0x146a +#define V_INVERSION_PIXEL 0x1470 +#define V_INVERSION_LINE 0x1471 +#define V_INVERSION_CONTROL 0x1472 +#define MLVDS2_CONTROL 0x1474 +#define MLVDS2_CONFIG_HI 0x1475 +#define MLVDS2_CONFIG_LO 0x1476 +#define MLVDS2_DUAL_GATE_WR_START 0x1477 +#define MLVDS2_DUAL_GATE_WR_END 0x1478 +#define MLVDS2_DUAL_GATE_RD_START 0x1479 +#define MLVDS2_DUAL_GATE_RD_END 0x147a +#define MLVDS2_SECOND_RESET_CTL 0x147b +#define MLVDS2_DUAL_GATE_CTL_HI 0x147c +#define MLVDS2_DUAL_GATE_CTL_LO 0x147d +#define MLVDS2_RESET_CONFIG_HI 0x147e +#define MLVDS2_RESET_CONFIG_LO 0x147f +#define GAMMA_CNTL_PORT 0x1480 +#define GAMMA_DATA_PORT 0x1481 +#define GAMMA_ADDR_PORT 0x1482 +#define GAMMA_VCOM_HSWITCH_ADDR 0x1483 +#define RGB_BASE_ADDR 0x1485 +#define RGB_COEFF_ADDR 0x1486 +#define POL_CNTL_ADDR 0x1487 +#define DITH_CNTL_ADDR 0x1488 +#define GAMMA_PROBE_CTRL 0x1489 +#define GAMMA_PROBE_COLOR_L 0x148a +#define GAMMA_PROBE_COLOR_H 0x148b +#define GAMMA_PROBE_HL_COLOR 0x148c +#define GAMMA_PROBE_POS_X 0x148d +#define GAMMA_PROBE_POS_Y 0x148e +#define STH1_HS_ADDR 0x1490 +#define STH1_HE_ADDR 0x1491 +#define STH1_VS_ADDR 0x1492 +#define STH1_VE_ADDR 0x1493 +#define STH2_HS_ADDR 0x1494 +#define STH2_HE_ADDR 0x1495 +#define STH2_VS_ADDR 0x1496 +#define STH2_VE_ADDR 0x1497 +#define OEH_HS_ADDR 0x1498 +#define OEH_HE_ADDR 0x1499 +#define OEH_VS_ADDR 0x149a +#define OEH_VE_ADDR 0x149b +#define VCOM_HSWITCH_ADDR 0x149c +#define VCOM_VS_ADDR 0x149d +#define VCOM_VE_ADDR 0x149e +#define CPV1_HS_ADDR 0x149f +#define CPV1_HE_ADDR 0x14a0 +#define CPV1_VS_ADDR 0x14a1 +#define CPV1_VE_ADDR 0x14a2 +#define CPV2_HS_ADDR 0x14a3 +#define CPV2_HE_ADDR 0x14a4 +#define CPV2_VS_ADDR 0x14a5 +#define CPV2_VE_ADDR 0x14a6 +#define STV1_HS_ADDR 0x14a7 +#define STV1_HE_ADDR 0x14a8 +#define STV1_VS_ADDR 0x14a9 +#define STV1_VE_ADDR 0x14aa +#define STV2_HS_ADDR 0x14ab +#define STV2_HE_ADDR 0x14ac +#define STV2_VS_ADDR 0x14ad +#define STV2_VE_ADDR 0x14ae +#define OEV1_HS_ADDR 0x14af +#define OEV1_HE_ADDR 0x14b0 +#define OEV1_VS_ADDR 0x14b1 +#define OEV1_VE_ADDR 0x14b2 +#define OEV2_HS_ADDR 0x14b3 +#define OEV2_HE_ADDR 0x14b4 +#define OEV2_VS_ADDR 0x14b5 +#define OEV2_VE_ADDR 0x14b6 +#define OEV3_HS_ADDR 0x14b7 +#define OEV3_HE_ADDR 0x14b8 +#define OEV3_VS_ADDR 0x14b9 +#define OEV3_VE_ADDR 0x14ba +#define LCD_PWR_ADDR 0x14bb +#define LCD_PWM0_LO_ADDR 0x14bc +#define LCD_PWM0_HI_ADDR 0x14bd +#define LCD_PWM1_LO_ADDR 0x14be +#define LCD_PWM1_HI_ADDR 0x14bf +#define INV_CNT_ADDR 0x14c0 +#define TCON_MISC_SEL_ADDR 0x14c1 +#define DUAL_PORT_CNTL_ADDR 0x14c2 +#define MLVDS_CONTROL 0x14c3 +#define MLVDS_RESET_PATTERN_HI 0x14c4 +#define MLVDS_RESET_PATTERN_LO 0x14c5 +#define MLVDS_RESET_PATTERN_EXT 0x14c6 +#define MLVDS_CONFIG_HI 0x14c7 +#define MLVDS_CONFIG_LO 0x14c8 +#define TCON_DOUBLE_CTL 0x14c9 +#define TCON_PATTERN_HI 0x14ca +#define TCON_PATTERN_LO 0x14cb +#define TCON_CONTROL_HI 0x14cc +#define TCON_CONTROL_LO 0x14cd +#define LVDS_BLANK_DATA_HI 0x14ce +#define LVDS_BLANK_DATA_LO 0x14cf +#define LVDS_PACK_CNTL_ADDR 0x14d0 +#define DE_HS_ADDR 0x14d1 +#define DE_HE_ADDR 0x14d2 +#define DE_VS_ADDR 0x14d3 +#define DE_VE_ADDR 0x14d4 +#define HSYNC_HS_ADDR 0x14d5 +#define HSYNC_HE_ADDR 0x14d6 +#define HSYNC_VS_ADDR 0x14d7 +#define HSYNC_VE_ADDR 0x14d8 +#define VSYNC_HS_ADDR 0x14d9 +#define VSYNC_HE_ADDR 0x14da +#define VSYNC_VS_ADDR 0x14db +#define VSYNC_VE_ADDR 0x14dc +#define LCD_MCU_CTL 0x14dd +#define LCD_MCU_DATA_0 0x14de +#define LCD_MCU_DATA_1 0x14df +#define LVDS_GEN_CNTL 0x14e0 +#define LVDS_PHY_CNTL0 0x14e1 +#define LVDS_PHY_CNTL1 0x14e2 +#define LVDS_PHY_CNTL2 0x14e3 +#define LVDS_PHY_CNTL3 0x14e4 +#define LVDS_PHY_CNTL4 0x14e5 +#define LVDS_PHY_CNTL5 0x14e6 +#define LVDS_SRG_TEST 0x14e8 +#define LVDS_BIST_MUX0 0x14e9 +#define LVDS_BIST_MUX1 0x14ea +#define LVDS_BIST_FIXED0 0x14eb +#define LVDS_BIST_FIXED1 0x14ec +#define LVDS_BIST_CNTL0 0x14ed +#define LVDS_CLKB_CLKA 0x14ee +#define LVDS_PHY_CLK_CNTL 0x14ef +#define LVDS_SER_EN 0x14f0 +#define LVDS_PHY_CNTL6 0x14f1 +#define LVDS_PHY_CNTL7 0x14f2 +#define LVDS_PHY_CNTL8 0x14f3 +#define MLVDS_CLK_CTL0_HI 0x14f4 +#define MLVDS_CLK_CTL0_LO 0x14f5 +#define MLVDS_DUAL_GATE_WR_START 0x14f6 +#define MLVDS_DUAL_GATE_WR_END 0x14f7 +#define MLVDS_DUAL_GATE_RD_START 0x14f8 +#define MLVDS_DUAL_GATE_RD_END 0x14f9 +#define MLVDS_SECOND_RESET_CTL 0x14fa +#define MLVDS_DUAL_GATE_CTL_HI 0x14fb +#define MLVDS_DUAL_GATE_CTL_LO 0x14fc +#define MLVDS_RESET_CONFIG_HI 0x14fd +#define MLVDS_RESET_CONFIG_LO 0x14fe +#define VPU_OSD1_MMC_CTRL 0x2701 +#define VPU_OSD2_MMC_CTRL 0x2702 +#define VPU_VD1_MMC_CTRL 0x2703 +#define VPU_VD2_MMC_CTRL 0x2704 +#define VPU_DI_IF1_MMC_CTRL 0x2705 +#define VPU_DI_MEM_MMC_CTRL 0x2706 +#define VPU_DI_INP_MMC_CTRL 0x2707 +#define VPU_DI_MTNRD_MMC_CTRL 0x2708 +#define VPU_DI_CHAN2_MMC_CTRL 0x2709 +#define VPU_DI_MTNWR_MMC_CTRL 0x270a +#define VPU_DI_NRWR_MMC_CTRL 0x270b +#define VPU_DI_DIWR_MMC_CTRL 0x270c +#define VPU_VDIN0_MMC_CTRL 0x270d +#define VPU_VDIN1_MMC_CTRL 0x270e +#define VPU_BT656_MMC_CTRL 0x270f +#define VPU_TVD3D_MMC_CTRL 0x2710 +#define VPU_TVDVBI_MMC_CTRL 0x2711 +#define VPU_TVDVBI_VSLATCH_ADDR 0x2712 +#define VPU_TVDVBI_WRRSP_ADDR 0x2713 +#define VPU_VDIN_PRE_ARB_CTRL 0x2714 +#define VPU_VDISP_PRE_ARB_CTRL 0x2715 +#define VPU_VPUARB2_PRE_ARB_CTRL 0x2716 +#define VPU_OSD3_MMC_CTRL 0x2717 +#define VPU_OSD4_MMC_CTRL 0x2718 +#define VPU_VD3_MMC_CTRL 0x2719 +#define VPU_VIU_VENC_MUX_CTRL 0x271a +#define VIU1_SEL_VENC_MASK 0x3 +#define VIU1_SEL_VENC_ENCL 0 +#define VIU1_SEL_VENC_ENCI 1 +#define VIU1_SEL_VENC_ENCP 2 +#define VIU1_SEL_VENC_ENCT 3 +#define VIU2_SEL_VENC_MASK 0xc +#define VIU2_SEL_VENC_ENCL 0 +#define VIU2_SEL_VENC_ENCI (1 << 2) +#define VIU2_SEL_VENC_ENCP (2 << 2) +#define VIU2_SEL_VENC_ENCT (3 << 2) +#define VPU_HDMI_SETTING 0x271b +#define VPU_HDMI_ENCI_DATA_TO_HDMI BIT(0) +#define VPU_HDMI_ENCP_DATA_TO_HDMI BIT(1) +#define VPU_HDMI_INV_HSYNC BIT(2) +#define VPU_HDMI_INV_VSYNC BIT(3) +#define VPU_HDMI_OUTPUT_CRYCB (0 << 5) +#define VPU_HDMI_OUTPUT_YCBCR (1 << 5) +#define VPU_HDMI_OUTPUT_YCRCB (2 << 5) +#define VPU_HDMI_OUTPUT_CBCRY (3 << 5) +#define VPU_HDMI_OUTPUT_CBYCR (4 << 5) +#define VPU_HDMI_OUTPUT_CRCBY (5 << 5) +#define VPU_HDMI_WR_RATE(rate) (((rate & 0x1f) - 1) << 8) +#define VPU_HDMI_RD_RATE(rate) (((rate & 0x1f) - 1) << 12) +#define ENCI_INFO_READ 0x271c +#define ENCP_INFO_READ 0x271d +#define ENCT_INFO_READ 0x271e +#define ENCL_INFO_READ 0x271f +#define VPU_SW_RESET 0x2720 +#define VPU_D2D3_MMC_CTRL 0x2721 +#define VPU_CONT_MMC_CTRL 0x2722 +#define VPU_CLK_GATE 0x2723 +#define VPU_RDMA_MMC_CTRL 0x2724 +#define VPU_MEM_PD_REG0 0x2725 +#define VPU_MEM_PD_REG1 0x2726 +#define VPU_HDMI_DATA_OVR 0x2727 +#define VPU_PROT1_MMC_CTRL 0x2728 +#define VPU_PROT2_MMC_CTRL 0x2729 +#define VPU_PROT3_MMC_CTRL 0x272a +#define VPU_ARB4_V1_MMC_CTRL 0x272b +#define VPU_ARB4_V2_MMC_CTRL 0x272c +#define VPU_VPU_PWM_V0 0x2730 +#define VPU_VPU_PWM_V1 0x2731 +#define VPU_VPU_PWM_V2 0x2732 +#define VPU_VPU_PWM_V3 0x2733 +#define VPU_VPU_PWM_H0 0x2734 +#define VPU_VPU_PWM_H1 0x2735 +#define VPU_VPU_PWM_H2 0x2736 +#define VPU_VPU_PWM_H3 0x2737 +#define VPU_MISC_CTRL 0x2740 +#define VPU_ISP_GCLK_CTRL0 0x2741 +#define VPU_ISP_GCLK_CTRL1 0x2742 +#define VPU_HDMI_FMT_CTRL 0x2743 +#define VPU_VDIN_ASYNC_HOLD_CTRL 0x2743 +#define VPU_VDISP_ASYNC_HOLD_CTRL 0x2744 +#define VPU_VPUARB2_ASYNC_HOLD_CTRL 0x2745 + +#define VPU_PROT1_CLK_GATE 0x2750 +#define VPU_PROT1_GEN_CNTL 0x2751 +#define VPU_PROT1_X_START_END 0x2752 +#define VPU_PROT1_Y_START_END 0x2753 +#define VPU_PROT1_Y_LEN_STEP 0x2754 +#define VPU_PROT1_RPT_LOOP 0x2755 +#define VPU_PROT1_RPT_PAT 0x2756 +#define VPU_PROT1_DDR 0x2757 +#define VPU_PROT1_RBUF_ROOM 0x2758 +#define VPU_PROT1_STAT_0 0x2759 +#define VPU_PROT1_STAT_1 0x275a +#define VPU_PROT1_STAT_2 0x275b +#define VPU_PROT1_REQ_ONOFF 0x275c +#define VPU_PROT2_CLK_GATE 0x2760 +#define VPU_PROT2_GEN_CNTL 0x2761 +#define VPU_PROT2_X_START_END 0x2762 +#define VPU_PROT2_Y_START_END 0x2763 +#define VPU_PROT2_Y_LEN_STEP 0x2764 +#define VPU_PROT2_RPT_LOOP 0x2765 +#define VPU_PROT2_RPT_PAT 0x2766 +#define VPU_PROT2_DDR 0x2767 +#define VPU_PROT2_RBUF_ROOM 0x2768 +#define VPU_PROT2_STAT_0 0x2769 +#define VPU_PROT2_STAT_1 0x276a +#define VPU_PROT2_STAT_2 0x276b +#define VPU_PROT2_REQ_ONOFF 0x276c +#define VPU_PROT3_CLK_GATE 0x2770 +#define VPU_PROT3_GEN_CNTL 0x2771 +#define VPU_PROT3_X_START_END 0x2772 +#define VPU_PROT3_Y_START_END 0x2773 +#define VPU_PROT3_Y_LEN_STEP 0x2774 +#define VPU_PROT3_RPT_LOOP 0x2775 +#define VPU_PROT3_RPT_PAT 0x2776 +#define VPU_PROT3_DDR 0x2777 +#define VPU_PROT3_RBUF_ROOM 0x2778 +#define VPU_PROT3_STAT_0 0x2779 +#define VPU_PROT3_STAT_1 0x277a +#define VPU_PROT3_STAT_2 0x277b +#define VPU_PROT3_REQ_ONOFF 0x277c +#define VPU_RDARB_MODE_L1C1 0x2790 +#define VPU_RDARB_MODE_L1C2 0x2799 +#define VPU_RDARB_MODE_L2C1 0x279d +#define VPU_WRARB_MODE_L2C1 0x27a2 +#define VPU_RDARB_SLAVE_TO_MASTER_PORT(dc, port) (port << (16 + dc)) + +/* osd super scale */ +#define OSDSR_HV_SIZEIN 0x3130 +#define OSDSR_CTRL_MODE 0x3131 +#define OSDSR_ABIC_HCOEF 0x3132 +#define OSDSR_YBIC_HCOEF 0x3133 +#define OSDSR_CBIC_HCOEF 0x3134 +#define OSDSR_ABIC_VCOEF 0x3135 +#define OSDSR_YBIC_VCOEF 0x3136 +#define OSDSR_CBIC_VCOEF 0x3137 +#define OSDSR_VAR_PARA 0x3138 +#define OSDSR_CONST_PARA 0x3139 +#define OSDSR_RKE_EXTWIN 0x313a +#define OSDSR_UK_GRAD2DDIAG_TH_RATE 0x313b +#define OSDSR_UK_GRAD2DDIAG_LIMIT 0x313c +#define OSDSR_UK_GRAD2DADJA_TH_RATE 0x313d +#define OSDSR_UK_GRAD2DADJA_LIMIT 0x313e +#define OSDSR_UK_BST_GAIN 0x313f +#define OSDSR_HVBLEND_TH 0x3140 +#define OSDSR_DEMO_WIND_TB 0x3141 +#define OSDSR_DEMO_WIND_LR 0x3142 +#define OSDSR_INT_BLANK_NUM 0x3143 +#define OSDSR_FRM_END_STAT 0x3144 +#define OSDSR_ABIC_HCOEF0 0x3145 +#define OSDSR_YBIC_HCOEF0 0x3146 +#define OSDSR_CBIC_HCOEF0 0x3147 +#define OSDSR_ABIC_VCOEF0 0x3148 +#define OSDSR_YBIC_VCOEF0 0x3149 +#define OSDSR_CBIC_VCOEF0 0x314a + +/* osd afbcd on gxtvbb */ +#define OSD1_AFBCD_ENABLE 0x31a0 +#define OSD1_AFBCD_ID_FIFO_THRD GENMASK(15, 9) +#define OSD1_AFBCD_DEC_ENABLE BIT(8) +#define OSD1_AFBCD_FRM_START BIT(0) +#define OSD1_AFBCD_MODE 0x31a1 +#define OSD1_AFBCD_SOFT_RESET BIT(31) +#define OSD1_AFBCD_AXI_REORDER_MODE BIT(28) +#define OSD1_AFBCD_MIF_URGENT GENMASK(25, 24) +#define OSD1_AFBCD_HOLD_LINE_NUM GENMASK(22, 16) +#define OSD1_AFBCD_RGBA_EXCHAN_CTRL GENMASK(15, 8) +#define OSD1_AFBCD_HREG_BLOCK_SPLIT BIT(6) +#define OSD1_AFBCD_HREG_HALF_BLOCK BIT(5) +#define OSD1_AFBCD_HREG_PIXEL_PACKING_FMT GENMASK(4, 0) +#define OSD1_AFBCD_SIZE_IN 0x31a2 +#define OSD1_AFBCD_HREG_VSIZE_IN GENMASK(31, 16) +#define OSD1_AFBCD_HREG_HSIZE_IN GENMASK(15, 0) +#define OSD1_AFBCD_HDR_PTR 0x31a3 +#define OSD1_AFBCD_FRAME_PTR 0x31a4 +#define OSD1_AFBCD_CHROMA_PTR 0x31a5 +#define OSD1_AFBCD_CONV_CTRL 0x31a6 +#define OSD1_AFBCD_CONV_LBUF_LEN GENMASK(15, 0) +#define OSD1_AFBCD_STATUS 0x31a8 +#define OSD1_AFBCD_PIXEL_HSCOPE 0x31a9 +#define OSD1_AFBCD_DEC_PIXEL_BGN_H GENMASK(31, 16) +#define OSD1_AFBCD_DEC_PIXEL_END_H GENMASK(15, 0) +#define OSD1_AFBCD_PIXEL_VSCOPE 0x31aa +#define OSD1_AFBCD_DEC_PIXEL_BGN_V GENMASK(31, 16) +#define OSD1_AFBCD_DEC_PIXEL_END_V GENMASK(15, 0) + +/* add for gxm and 962e dv core2 */ +#define DOLBY_CORE2A_SWAP_CTRL1 0x3434 +#define DOLBY_CORE2A_SWAP_CTRL2 0x3435 + +/* osd afbc on g12a */ +#define VPU_MAFBC_BLOCK_ID 0x3a00 +#define VPU_MAFBC_IRQ_RAW_STATUS 0x3a01 +#define VPU_MAFBC_IRQ_CLEAR 0x3a02 +#define VPU_MAFBC_IRQ_MASK 0x3a03 +#define VPU_MAFBC_IRQ_STATUS 0x3a04 +#define VPU_MAFBC_IRQ_SECURE_ID_ERROR BIT(5) +#define VPU_MAFBC_IRQ_AXI_ERROR BIT(4) +#define VPU_MAFBC_IRQ_DETILING_ERROR BIT(3) +#define VPU_MAFBC_IRQ_DECODE_ERROR BIT(2) +#define VPU_MAFBC_IRQ_CONFIGURATION_SWAPPED BIT(1) +#define VPU_MAFBC_IRQ_SURFACES_COMPLETED BIT(0) +#define VPU_MAFBC_COMMAND 0x3a05 +#define VPU_MAFBC_PENDING_SWAP BIT(1) +#define VPU_MAFBC_DIRECT_SWAP BIT(0) +#define VPU_MAFBC_STATUS 0x3a06 +#define VPU_MAFBC_ERROR BIT(2) +#define VPU_MAFBC_SWAPPING BIT(1) +#define VPU_MAFBC_ACTIVE BIT(0) +#define VPU_MAFBC_SURFACE_CFG 0x3a07 +#define VPU_MAFBC_CONTINUOUS_DECODING_ENABLE BIT(16) +#define VPU_MAFBC_S3_ENABLE BIT(3) +#define VPU_MAFBC_S2_ENABLE BIT(2) +#define VPU_MAFBC_S1_ENABLE BIT(1) +#define VPU_MAFBC_S0_ENABLE BIT(0) +#define VPU_MAFBC_HEADER_BUF_ADDR_LOW_S0 0x3a10 +#define VPU_MAFBC_HEADER_BUF_ADDR_HIGH_S0 0x3a11 +#define VPU_MAFBC_FORMAT_SPECIFIER_S0 0x3a12 +#define VPU_MAFBC_PAYLOAD_LIMIT_EN BIT(19) +#define VPU_MAFBC_TILED_HEADER_EN BIT(18) +#define VPU_MAFBC_SUPER_BLOCK_ASPECT GENMASK(17, 16) +#define VPU_MAFBC_BLOCK_SPLIT BIT(9) +#define VPU_MAFBC_YUV_TRANSFORM BIT(8) +#define VPU_MAFBC_PIXEL_FORMAT GENMASK(3, 0) +#define VPU_MAFBC_BUFFER_WIDTH_S0 0x3a13 +#define VPU_MAFBC_BUFFER_HEIGHT_S0 0x3a14 +#define VPU_MAFBC_BOUNDING_BOX_X_START_S0 0x3a15 +#define VPU_MAFBC_BOUNDING_BOX_X_END_S0 0x3a16 +#define VPU_MAFBC_BOUNDING_BOX_Y_START_S0 0x3a17 +#define VPU_MAFBC_BOUNDING_BOX_Y_END_S0 0x3a18 +#define VPU_MAFBC_OUTPUT_BUF_ADDR_LOW_S0 0x3a19 +#define VPU_MAFBC_OUTPUT_BUF_ADDR_HIGH_S0 0x3a1a +#define VPU_MAFBC_OUTPUT_BUF_STRIDE_S0 0x3a1b +#define VPU_MAFBC_PREFETCH_CFG_S0 0x3a1c +#define VPU_MAFBC_PREFETCH_READ_DIRECTION_Y BIT(1) +#define VPU_MAFBC_PREFETCH_READ_DIRECTION_X BIT(0) + +#define VPU_MAFBC_HEADER_BUF_ADDR_LOW_S1 0x3a30 +#define VPU_MAFBC_HEADER_BUF_ADDR_HIGH_S1 0x3a31 +#define VPU_MAFBC_FORMAT_SPECIFIER_S1 0x3a32 +#define VPU_MAFBC_BUFFER_WIDTH_S1 0x3a33 +#define VPU_MAFBC_BUFFER_HEIGHT_S1 0x3a34 +#define VPU_MAFBC_BOUNDING_BOX_X_START_S1 0x3a35 +#define VPU_MAFBC_BOUNDING_BOX_X_END_S1 0x3a36 +#define VPU_MAFBC_BOUNDING_BOX_Y_START_S1 0x3a37 +#define VPU_MAFBC_BOUNDING_BOX_Y_END_S1 0x3a38 +#define VPU_MAFBC_OUTPUT_BUF_ADDR_LOW_S1 0x3a39 +#define VPU_MAFBC_OUTPUT_BUF_ADDR_HIGH_S1 0x3a3a +#define VPU_MAFBC_OUTPUT_BUF_STRIDE_S1 0x3a3b +#define VPU_MAFBC_PREFETCH_CFG_S1 0x3a3c + +#define VPU_MAFBC_HEADER_BUF_ADDR_LOW_S2 0x3a50 +#define VPU_MAFBC_HEADER_BUF_ADDR_HIGH_S2 0x3a51 +#define VPU_MAFBC_FORMAT_SPECIFIER_S2 0x3a52 +#define VPU_MAFBC_BUFFER_WIDTH_S2 0x3a53 +#define VPU_MAFBC_BUFFER_HEIGHT_S2 0x3a54 +#define VPU_MAFBC_BOUNDING_BOX_X_START_S2 0x3a55 +#define VPU_MAFBC_BOUNDING_BOX_X_END_S2 0x3a56 +#define VPU_MAFBC_BOUNDING_BOX_Y_START_S2 0x3a57 +#define VPU_MAFBC_BOUNDING_BOX_Y_END_S2 0x3a58 +#define VPU_MAFBC_OUTPUT_BUF_ADDR_LOW_S2 0x3a59 +#define VPU_MAFBC_OUTPUT_BUF_ADDR_HIGH_S2 0x3a5a +#define VPU_MAFBC_OUTPUT_BUF_STRIDE_S2 0x3a5b +#define VPU_MAFBC_PREFETCH_CFG_S2 0x3a5c + +#define VPU_MAFBC_HEADER_BUF_ADDR_LOW_S3 0x3a70 +#define VPU_MAFBC_HEADER_BUF_ADDR_HIGH_S3 0x3a71 +#define VPU_MAFBC_FORMAT_SPECIFIER_S3 0x3a72 +#define VPU_MAFBC_BUFFER_WIDTH_S3 0x3a73 +#define VPU_MAFBC_BUFFER_HEIGHT_S3 0x3a74 +#define VPU_MAFBC_BOUNDING_BOX_X_START_S3 0x3a75 +#define VPU_MAFBC_BOUNDING_BOX_X_END_S3 0x3a76 +#define VPU_MAFBC_BOUNDING_BOX_Y_START_S3 0x3a77 +#define VPU_MAFBC_BOUNDING_BOX_Y_END_S3 0x3a78 +#define VPU_MAFBC_OUTPUT_BUF_ADDR_LOW_S3 0x3a79 +#define VPU_MAFBC_OUTPUT_BUF_ADDR_HIGH_S3 0x3a7a +#define VPU_MAFBC_OUTPUT_BUF_STRIDE_S3 0x3a7b +#define VPU_MAFBC_PREFETCH_CFG_S3 0x3a7c + +#define DOLBY_PATH_CTRL 0x1a0c +#define DOLBY_BYPASS_EN(val) (val & 0xf) +#define OSD_PATH_MISC_CTRL 0x1a0e +#define OSD_PATH_OSD_AXI_SEL_OSD1_AFBCD BIT(4) +#define OSD_PATH_OSD_AXI_SEL_OSD2_AFBCD BIT(5) +#define OSD_PATH_OSD_AXI_SEL_OSD3_AFBCD BIT(6) +#define MALI_AFBCD_TOP_CTRL 0x1a0f +#define MALI_AFBCD_MANUAL_RESET BIT(23) + +#define VIU_OSD_BLEND_CTRL 0x39b0 +#define VIU_OSD_BLEND_REORDER(dest, src) ((src) << (dest * 4)) +#define VIU_OSD_BLEND_DIN_EN(bits) ((bits & 0xf) << 20) +#define VIU_OSD_BLEND1_DIN3_BYPASS_TO_DOUT1 BIT(24) +#define VIU_OSD_BLEND1_DOUT_BYPASS_TO_BLEND2 BIT(25) +#define VIU_OSD_BLEND_DIN0_BYPASS_TO_DOUT0 BIT(26) +#define VIU_OSD_BLEND_BLEN2_PREMULT_EN(input) ((input & 0x3) << 27) +#define VIU_OSD_BLEND_HOLD_LINES(lines) ((lines & 0x7) << 29) +#define VIU_OSD_BLEND_CTRL1 0x39c0 +#define VIU_OSD_BLEND_DIN0_SCOPE_H 0x39b1 +#define VIU_OSD_BLEND_DIN0_SCOPE_V 0x39b2 +#define VIU_OSD_BLEND_DIN1_SCOPE_H 0x39b3 +#define VIU_OSD_BLEND_DIN1_SCOPE_V 0x39b4 +#define VIU_OSD_BLEND_DIN2_SCOPE_H 0x39b5 +#define VIU_OSD_BLEND_DIN2_SCOPE_V 0x39b6 +#define VIU_OSD_BLEND_DIN3_SCOPE_H 0x39b7 +#define VIU_OSD_BLEND_DIN3_SCOPE_V 0x39b8 +#define VIU_OSD_BLEND_DUMMY_DATA0 0x39b9 +#define VIU_OSD_BLEND_DUMMY_ALPHA 0x39ba +#define VIU_OSD_BLEND_BLEND0_SIZE 0x39bb +#define VIU_OSD_BLEND_BLEND1_SIZE 0x39bc +#define VIU_OSD_BLEND_RO_CURRENT_XY 0x39bf + +#define VPP_OUT_H_V_SIZE 0x1da5 + +#define VPP_VD2_HDR_IN_SIZE 0x1df0 +#define VPP_OSD1_IN_SIZE 0x1df1 +#define VPP_GCLK_CTRL2 0x1df2 +#define VD2_PPS_DUMMY_DATA 0x1df4 +#define VPP_OSD1_BLD_H_SCOPE 0x1df5 +#define VPP_OSD1_BLD_V_SCOPE 0x1df6 +#define VPP_OSD2_BLD_H_SCOPE 0x1df7 +#define VPP_OSD2_BLD_V_SCOPE 0x1df8 +#define VPP_WRBAK_CTRL 0x1df9 +#define VPP_SLEEP_CTRL 0x1dfa +#define VD1_BLEND_SRC_CTRL 0x1dfb +#define VD2_BLEND_SRC_CTRL 0x1dfc +#define VD_BLEND_PREBLD_SRC_VD1 (1 << 0) +#define VD_BLEND_PREBLD_SRC_VD2 (2 << 0) +#define VD_BLEND_PREBLD_SRC_OSD1 (3 << 0) +#define VD_BLEND_PREBLD_SRC_OSD2 (4 << 0) +#define VD_BLEND_PREBLD_PREMULT_EN BIT(4) +#define VD_BLEND_POSTBLD_SRC_VD1 (1 << 8) +#define VD_BLEND_POSTBLD_SRC_VD2 (2 << 8) +#define VD_BLEND_POSTBLD_SRC_OSD1 (3 << 8) +#define VD_BLEND_POSTBLD_SRC_OSD2 (4 << 8) +#define VD_BLEND_POSTBLD_PREMULT_EN BIT(16) +#define OSD1_BLEND_SRC_CTRL 0x1dfd +#define OSD2_BLEND_SRC_CTRL 0x1dfe +#define OSD_BLEND_POSTBLD_SRC_VD1 (1 << 8) +#define OSD_BLEND_POSTBLD_SRC_VD2 (2 << 8) +#define OSD_BLEND_POSTBLD_SRC_OSD1 (3 << 8) +#define OSD_BLEND_POSTBLD_SRC_OSD2 (4 << 8) +#define OSD_BLEND_PATH_SEL_ENABLE BIT(20) + +#define VPP_POST_BLEND_BLEND_DUMMY_DATA 0x3968 +#define VPP_POST_BLEND_DUMMY_ALPHA 0x3969 +#define VPP_RDARB_MODE 0x3978 +#define VPP_RDARB_REQEN_SLV 0x3979 + +#endif /* __MESON_REGISTERS_H */ diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c new file mode 100644 index 0000000000..2a82119eb5 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_vclk.c @@ -0,0 +1,1111 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#include <linux/export.h> + +#include <drm/drm_print.h> + +#include "meson_drv.h" +#include "meson_vclk.h" + +/** + * DOC: Video Clocks + * + * VCLK is the "Pixel Clock" frequency generator from a dedicated PLL. + * We handle the following encodings : + * + * - CVBS 27MHz generator via the VCLK2 to the VENCI and VDAC blocks + * - HDMI Pixel Clocks generation + * + * What is missing : + * + * - Genenate Pixel clocks for 2K/4K 10bit formats + * + * Clock generator scheme : + * + * .. code:: + * + * __________ _________ _____ + * | | | | | |--ENCI + * | HDMI PLL |-| PLL_DIV |--- VCLK--| |--ENCL + * |__________| |_________| \ | MUX |--ENCP + * --VCLK2-| |--VDAC + * |_____|--HDMI-TX + * + * Final clocks can take input for either VCLK or VCLK2, but + * VCLK is the preferred path for HDMI clocking and VCLK2 is the + * preferred path for CVBS VDAC clocking. + * + * VCLK and VCLK2 have fixed divided clocks paths for /1, /2, /4, /6 or /12. + * + * The PLL_DIV can achieve an additional fractional dividing like + * 1.5, 3.5, 3.75... to generate special 2K and 4K 10bit clocks. + */ + +/* HHI Registers */ +#define HHI_VID_PLL_CLK_DIV 0x1a0 /* 0x68 offset in data sheet */ +#define VID_PLL_EN BIT(19) +#define VID_PLL_BYPASS BIT(18) +#define VID_PLL_PRESET BIT(15) +#define HHI_VIID_CLK_DIV 0x128 /* 0x4a offset in data sheet */ +#define VCLK2_DIV_MASK 0xff +#define VCLK2_DIV_EN BIT(16) +#define VCLK2_DIV_RESET BIT(17) +#define CTS_VDAC_SEL_MASK (0xf << 28) +#define CTS_VDAC_SEL_SHIFT 28 +#define HHI_VIID_CLK_CNTL 0x12c /* 0x4b offset in data sheet */ +#define VCLK2_EN BIT(19) +#define VCLK2_SEL_MASK (0x7 << 16) +#define VCLK2_SEL_SHIFT 16 +#define VCLK2_SOFT_RESET BIT(15) +#define VCLK2_DIV1_EN BIT(0) +#define HHI_VID_CLK_DIV 0x164 /* 0x59 offset in data sheet */ +#define VCLK_DIV_MASK 0xff +#define VCLK_DIV_EN BIT(16) +#define VCLK_DIV_RESET BIT(17) +#define CTS_ENCP_SEL_MASK (0xf << 24) +#define CTS_ENCP_SEL_SHIFT 24 +#define CTS_ENCI_SEL_MASK (0xf << 28) +#define CTS_ENCI_SEL_SHIFT 28 +#define HHI_VID_CLK_CNTL 0x17c /* 0x5f offset in data sheet */ +#define VCLK_EN BIT(19) +#define VCLK_SEL_MASK (0x7 << 16) +#define VCLK_SEL_SHIFT 16 +#define VCLK_SOFT_RESET BIT(15) +#define VCLK_DIV1_EN BIT(0) +#define VCLK_DIV2_EN BIT(1) +#define VCLK_DIV4_EN BIT(2) +#define VCLK_DIV6_EN BIT(3) +#define VCLK_DIV12_EN BIT(4) +#define HHI_VID_CLK_CNTL2 0x194 /* 0x65 offset in data sheet */ +#define CTS_ENCI_EN BIT(0) +#define CTS_ENCP_EN BIT(2) +#define CTS_VDAC_EN BIT(4) +#define HDMI_TX_PIXEL_EN BIT(5) +#define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 offset in data sheet */ +#define HDMI_TX_PIXEL_SEL_MASK (0xf << 16) +#define HDMI_TX_PIXEL_SEL_SHIFT 16 +#define CTS_HDMI_SYS_SEL_MASK (0x7 << 9) +#define CTS_HDMI_SYS_DIV_MASK (0x7f) +#define CTS_HDMI_SYS_EN BIT(8) + +#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ +#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ + +#define HHI_HDMI_PLL_CNTL 0x320 /* 0xc8 offset in data sheet */ +#define HHI_HDMI_PLL_CNTL_EN BIT(30) +#define HHI_HDMI_PLL_CNTL2 0x324 /* 0xc9 offset in data sheet */ +#define HHI_HDMI_PLL_CNTL3 0x328 /* 0xca offset in data sheet */ +#define HHI_HDMI_PLL_CNTL4 0x32C /* 0xcb offset in data sheet */ +#define HHI_HDMI_PLL_CNTL5 0x330 /* 0xcc offset in data sheet */ +#define HHI_HDMI_PLL_CNTL6 0x334 /* 0xcd offset in data sheet */ +#define HHI_HDMI_PLL_CNTL7 0x338 /* 0xce offset in data sheet */ + +#define HDMI_PLL_RESET BIT(28) +#define HDMI_PLL_RESET_G12A BIT(29) +#define HDMI_PLL_LOCK BIT(31) +#define HDMI_PLL_LOCK_G12A (3 << 30) + +#define FREQ_1000_1001(_freq) DIV_ROUND_CLOSEST(_freq * 1000, 1001) + +/* VID PLL Dividers */ +enum { + VID_PLL_DIV_1 = 0, + VID_PLL_DIV_2, + VID_PLL_DIV_2p5, + VID_PLL_DIV_3, + VID_PLL_DIV_3p5, + VID_PLL_DIV_3p75, + VID_PLL_DIV_4, + VID_PLL_DIV_5, + VID_PLL_DIV_6, + VID_PLL_DIV_6p25, + VID_PLL_DIV_7, + VID_PLL_DIV_7p5, + VID_PLL_DIV_12, + VID_PLL_DIV_14, + VID_PLL_DIV_15, +}; + +static void meson_vid_pll_set(struct meson_drm *priv, unsigned int div) +{ + unsigned int shift_val = 0; + unsigned int shift_sel = 0; + + /* Disable vid_pll output clock */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_EN, 0); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_PRESET, 0); + + switch (div) { + case VID_PLL_DIV_2: + shift_val = 0x0aaa; + shift_sel = 0; + break; + case VID_PLL_DIV_2p5: + shift_val = 0x5294; + shift_sel = 2; + break; + case VID_PLL_DIV_3: + shift_val = 0x0db6; + shift_sel = 0; + break; + case VID_PLL_DIV_3p5: + shift_val = 0x36cc; + shift_sel = 1; + break; + case VID_PLL_DIV_3p75: + shift_val = 0x6666; + shift_sel = 2; + break; + case VID_PLL_DIV_4: + shift_val = 0x0ccc; + shift_sel = 0; + break; + case VID_PLL_DIV_5: + shift_val = 0x739c; + shift_sel = 2; + break; + case VID_PLL_DIV_6: + shift_val = 0x0e38; + shift_sel = 0; + break; + case VID_PLL_DIV_6p25: + shift_val = 0x0000; + shift_sel = 3; + break; + case VID_PLL_DIV_7: + shift_val = 0x3c78; + shift_sel = 1; + break; + case VID_PLL_DIV_7p5: + shift_val = 0x78f0; + shift_sel = 2; + break; + case VID_PLL_DIV_12: + shift_val = 0x0fc0; + shift_sel = 0; + break; + case VID_PLL_DIV_14: + shift_val = 0x3f80; + shift_sel = 1; + break; + case VID_PLL_DIV_15: + shift_val = 0x7f80; + shift_sel = 2; + break; + } + + if (div == VID_PLL_DIV_1) + /* Enable vid_pll bypass to HDMI pll */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_BYPASS, VID_PLL_BYPASS); + else { + /* Disable Bypass */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_BYPASS, 0); + /* Clear sel */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + 3 << 16, 0); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_PRESET, 0); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + 0x7fff, 0); + + /* Setup sel and val */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + 3 << 16, shift_sel << 16); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_PRESET, VID_PLL_PRESET); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + 0x7fff, shift_val); + + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_PRESET, 0); + } + + /* Enable the vid_pll output clock */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_EN, VID_PLL_EN); +} + +/* + * Setup VCLK2 for 27MHz, and enable clocks for ENCI and VDAC + * + * TOFIX: Refactor into table to also handle HDMI frequency and paths + */ +static void meson_venci_cvbs_clock_config(struct meson_drm *priv) +{ + unsigned int val; + + /* Setup PLL to output 1.485GHz */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) { + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800023d); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00404e00); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4800023d); + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val, + (val & HDMI_PLL_LOCK), 10, 0); + } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) { + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4000027b); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb300); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0xa6212844); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c4d000c); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); + + /* Reset PLL */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + HDMI_PLL_RESET, HDMI_PLL_RESET); + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + HDMI_PLL_RESET, 0); + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val, + (val & HDMI_PLL_LOCK), 10, 0); + } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x1a0504f7); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00010000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x00000000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x6a28dc00); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x65771290); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x39272000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL7, 0x56540000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x3a0504f7); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x1a0504f7); + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val, + ((val & HDMI_PLL_LOCK_G12A) == HDMI_PLL_LOCK_G12A), + 10, 0); + } + + /* Disable VCLK2 */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, VCLK2_EN, 0); + + /* Setup vid_pll to /1 */ + meson_vid_pll_set(priv, VID_PLL_DIV_1); + + /* Setup the VCLK2 divider value to achieve 27MHz */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV, + VCLK2_DIV_MASK, (55 - 1)); + + /* select vid_pll for vclk2 */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, + VCLK2_SEL_MASK, (0 << VCLK2_SEL_SHIFT)); + else + regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, + VCLK2_SEL_MASK, (4 << VCLK2_SEL_SHIFT)); + + /* enable vclk2 gate */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, VCLK2_EN, VCLK2_EN); + + /* select vclk_div1 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, (8 << CTS_ENCI_SEL_SHIFT)); + /* select vclk_div1 for vdac */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV, + CTS_VDAC_SEL_MASK, (8 << CTS_VDAC_SEL_SHIFT)); + + /* release vclk2_div_reset and enable vclk2_div */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV, + VCLK2_DIV_EN | VCLK2_DIV_RESET, VCLK2_DIV_EN); + + /* enable vclk2_div1 gate */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, + VCLK2_DIV1_EN, VCLK2_DIV1_EN); + + /* reset vclk2 */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, + VCLK2_SOFT_RESET, VCLK2_SOFT_RESET); + regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, + VCLK2_SOFT_RESET, 0); + + /* enable enci_clk */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2, + CTS_ENCI_EN, CTS_ENCI_EN); + /* enable vdac_clk */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2, + CTS_VDAC_EN, CTS_VDAC_EN); +} + +enum { +/* PLL O1 O2 O3 VP DV EN TX */ +/* 4320 /4 /4 /1 /5 /1 => /2 /2 */ + MESON_VCLK_HDMI_ENCI_54000 = 0, +/* 4320 /4 /4 /1 /5 /1 => /1 /2 */ + MESON_VCLK_HDMI_DDR_54000, +/* 2970 /4 /1 /1 /5 /1 => /1 /2 */ + MESON_VCLK_HDMI_DDR_148500, +/* 2970 /2 /2 /2 /5 /1 => /1 /1 */ + MESON_VCLK_HDMI_74250, +/* 2970 /1 /2 /2 /5 /1 => /1 /1 */ + MESON_VCLK_HDMI_148500, +/* 2970 /1 /1 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_297000, +/* 5940 /1 /1 /2 /5 /1 => /1 /1 */ + MESON_VCLK_HDMI_594000, +/* 2970 /1 /1 /1 /5 /1 => /1 /2 */ + MESON_VCLK_HDMI_594000_YUV420, +}; + +struct meson_vclk_params { + unsigned int pll_freq; + unsigned int phy_freq; + unsigned int vclk_freq; + unsigned int venc_freq; + unsigned int pixel_freq; + unsigned int pll_od1; + unsigned int pll_od2; + unsigned int pll_od3; + unsigned int vid_pll_div; + unsigned int vclk_div; +} params[] = { + [MESON_VCLK_HDMI_ENCI_54000] = { + .pll_freq = 4320000, + .phy_freq = 270000, + .vclk_freq = 54000, + .venc_freq = 54000, + .pixel_freq = 54000, + .pll_od1 = 4, + .pll_od2 = 4, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_DDR_54000] = { + .pll_freq = 4320000, + .phy_freq = 270000, + .vclk_freq = 54000, + .venc_freq = 54000, + .pixel_freq = 27000, + .pll_od1 = 4, + .pll_od2 = 4, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_DDR_148500] = { + .pll_freq = 2970000, + .phy_freq = 742500, + .vclk_freq = 148500, + .venc_freq = 148500, + .pixel_freq = 74250, + .pll_od1 = 4, + .pll_od2 = 1, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_74250] = { + .pll_freq = 2970000, + .phy_freq = 742500, + .vclk_freq = 74250, + .venc_freq = 74250, + .pixel_freq = 74250, + .pll_od1 = 2, + .pll_od2 = 2, + .pll_od3 = 2, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_148500] = { + .pll_freq = 2970000, + .phy_freq = 1485000, + .vclk_freq = 148500, + .venc_freq = 148500, + .pixel_freq = 148500, + .pll_od1 = 1, + .pll_od2 = 2, + .pll_od3 = 2, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_297000] = { + .pll_freq = 5940000, + .phy_freq = 2970000, + .venc_freq = 297000, + .vclk_freq = 297000, + .pixel_freq = 297000, + .pll_od1 = 2, + .pll_od2 = 1, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 2, + }, + [MESON_VCLK_HDMI_594000] = { + .pll_freq = 5940000, + .phy_freq = 5940000, + .venc_freq = 594000, + .vclk_freq = 594000, + .pixel_freq = 594000, + .pll_od1 = 1, + .pll_od2 = 1, + .pll_od3 = 2, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_594000_YUV420] = { + .pll_freq = 5940000, + .phy_freq = 2970000, + .venc_freq = 594000, + .vclk_freq = 594000, + .pixel_freq = 297000, + .pll_od1 = 2, + .pll_od2 = 1, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + { /* sentinel */ }, +}; + +static inline unsigned int pll_od_to_reg(unsigned int od) +{ + switch (od) { + case 1: + return 0; + case 2: + return 1; + case 4: + return 2; + case 8: + return 3; + } + + /* Invalid */ + return 0; +} + +static void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m, + unsigned int frac, unsigned int od1, + unsigned int od2, unsigned int od3) +{ + unsigned int val; + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) { + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000200 | m); + if (frac) + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, + 0x00004000 | frac); + else + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, + 0x00000000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); + + /* Enable and unreset */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + 0x7 << 28, HHI_HDMI_PLL_CNTL_EN); + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, + val, (val & HDMI_PLL_LOCK), 10, 0); + } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) { + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x40000200 | m); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb000 | frac); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); + + /* Reset PLL */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + HDMI_PLL_RESET, HDMI_PLL_RESET); + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + HDMI_PLL_RESET, 0); + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val, + (val & HDMI_PLL_LOCK), 10, 0); + } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x0b3a0400 | m); + + /* Enable and reset */ + /* TODO: add specific macro for g12a here */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + 0x3 << 28, 0x3 << 28); + + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, frac); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x00000000); + + /* G12A HDMI PLL Needs specific parameters for 5.4GHz */ + if (m >= 0xf7) { + if (frac < 0x10000) { + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, + 0x6a685c00); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, + 0x11551293); + } else { + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, + 0xea68dc00); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, + 0x65771290); + } + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x39272000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL7, 0x55540000); + } else { + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0a691c00); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x33771290); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x39270000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL7, 0x50540000); + } + + do { + /* Reset PLL */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + HDMI_PLL_RESET_G12A, HDMI_PLL_RESET_G12A); + + /* UN-Reset PLL */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + HDMI_PLL_RESET_G12A, 0); + + /* Poll for lock bits */ + if (!regmap_read_poll_timeout(priv->hhi, + HHI_HDMI_PLL_CNTL, val, + ((val & HDMI_PLL_LOCK_G12A) + == HDMI_PLL_LOCK_G12A), + 10, 100)) + break; + } while(1); + } + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 3 << 16, pll_od_to_reg(od1) << 16); + else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, + 3 << 21, pll_od_to_reg(od1) << 21); + else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + 3 << 16, pll_od_to_reg(od1) << 16); + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 3 << 22, pll_od_to_reg(od2) << 22); + else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, + 3 << 23, pll_od_to_reg(od2) << 23); + else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + 3 << 18, pll_od_to_reg(od2) << 18); + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 3 << 18, pll_od_to_reg(od3) << 18); + else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, + 3 << 19, pll_od_to_reg(od3) << 19); + else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + 3 << 20, pll_od_to_reg(od3) << 20); +} + +#define XTAL_FREQ 24000 + +static unsigned int meson_hdmi_pll_get_m(struct meson_drm *priv, + unsigned int pll_freq) +{ + /* The GXBB PLL has a /2 pre-multiplier */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) + pll_freq /= 2; + + return pll_freq / XTAL_FREQ; +} + +#define HDMI_FRAC_MAX_GXBB 4096 +#define HDMI_FRAC_MAX_GXL 1024 +#define HDMI_FRAC_MAX_G12A 131072 + +static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv, + unsigned int m, + unsigned int pll_freq) +{ + unsigned int parent_freq = XTAL_FREQ; + unsigned int frac_max = HDMI_FRAC_MAX_GXL; + unsigned int frac_m; + unsigned int frac; + + /* The GXBB PLL has a /2 pre-multiplier and a larger FRAC width */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) { + frac_max = HDMI_FRAC_MAX_GXBB; + parent_freq *= 2; + } + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + frac_max = HDMI_FRAC_MAX_G12A; + + /* We can have a perfect match !*/ + if (pll_freq / m == parent_freq && + pll_freq % m == 0) + return 0; + + frac = div_u64((u64)pll_freq * (u64)frac_max, parent_freq); + frac_m = m * frac_max; + if (frac_m > frac) + return frac_max; + frac -= frac_m; + + return min((u16)frac, (u16)(frac_max - 1)); +} + +static bool meson_hdmi_pll_validate_params(struct meson_drm *priv, + unsigned int m, + unsigned int frac) +{ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) { + /* Empiric supported min/max dividers */ + if (m < 53 || m > 123) + return false; + if (frac >= HDMI_FRAC_MAX_GXBB) + return false; + } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) { + /* Empiric supported min/max dividers */ + if (m < 106 || m > 247) + return false; + if (frac >= HDMI_FRAC_MAX_GXL) + return false; + } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + /* Empiric supported min/max dividers */ + if (m < 106 || m > 247) + return false; + if (frac >= HDMI_FRAC_MAX_G12A) + return false; + } + + return true; +} + +static bool meson_hdmi_pll_find_params(struct meson_drm *priv, + unsigned int freq, + unsigned int *m, + unsigned int *frac, + unsigned int *od) +{ + /* Cycle from /16 to /2 */ + for (*od = 16 ; *od > 1 ; *od >>= 1) { + *m = meson_hdmi_pll_get_m(priv, freq * *od); + if (!*m) + continue; + *frac = meson_hdmi_pll_get_frac(priv, *m, freq * *od); + + DRM_DEBUG_DRIVER("PLL params for %dkHz: m=%x frac=%x od=%d\n", + freq, *m, *frac, *od); + + if (meson_hdmi_pll_validate_params(priv, *m, *frac)) + return true; + } + + return false; +} + +/* pll_freq is the frequency after the OD dividers */ +enum drm_mode_status +meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq) +{ + unsigned int od, m, frac; + + /* In DMT mode, path after PLL is always /10 */ + freq *= 10; + + /* Check against soc revision/package limits */ + if (priv->limits) { + if (priv->limits->max_hdmi_phy_freq && + freq > priv->limits->max_hdmi_phy_freq) + return MODE_CLOCK_HIGH; + } + + if (meson_hdmi_pll_find_params(priv, freq, &m, &frac, &od)) + return MODE_OK; + + return MODE_CLOCK_RANGE; +} +EXPORT_SYMBOL_GPL(meson_vclk_dmt_supported_freq); + +/* pll_freq is the frequency after the OD dividers */ +static void meson_hdmi_pll_generic_set(struct meson_drm *priv, + unsigned int pll_freq) +{ + unsigned int od, m, frac, od1, od2, od3; + + if (meson_hdmi_pll_find_params(priv, pll_freq, &m, &frac, &od)) { + /* OD2 goes to the PHY, and needs to be *10, so keep OD3=1 */ + od3 = 1; + if (od < 4) { + od1 = 2; + od2 = 1; + } else { + od2 = od / 4; + od1 = od / od2; + } + + DRM_DEBUG_DRIVER("PLL params for %dkHz: m=%x frac=%x od=%d/%d/%d\n", + pll_freq, m, frac, od1, od2, od3); + + meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3); + + return; + } + + DRM_ERROR("Fatal, unable to find parameters for PLL freq %d\n", + pll_freq); +} + +enum drm_mode_status +meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq, + unsigned int vclk_freq) +{ + int i; + + DRM_DEBUG_DRIVER("phy_freq = %d vclk_freq = %d\n", + phy_freq, vclk_freq); + + /* Check against soc revision/package limits */ + if (priv->limits) { + if (priv->limits->max_hdmi_phy_freq && + phy_freq > priv->limits->max_hdmi_phy_freq) + return MODE_CLOCK_HIGH; + } + + for (i = 0 ; params[i].pixel_freq ; ++i) { + DRM_DEBUG_DRIVER("i = %d pixel_freq = %d alt = %d\n", + i, params[i].pixel_freq, + FREQ_1000_1001(params[i].pixel_freq)); + DRM_DEBUG_DRIVER("i = %d phy_freq = %d alt = %d\n", + i, params[i].phy_freq, + FREQ_1000_1001(params[i].phy_freq/10)*10); + /* Match strict frequency */ + if (phy_freq == params[i].phy_freq && + vclk_freq == params[i].vclk_freq) + return MODE_OK; + /* Match 1000/1001 variant */ + if (phy_freq == (FREQ_1000_1001(params[i].phy_freq/10)*10) && + vclk_freq == FREQ_1000_1001(params[i].vclk_freq)) + return MODE_OK; + } + + return MODE_CLOCK_RANGE; +} +EXPORT_SYMBOL_GPL(meson_vclk_vic_supported_freq); + +static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq, + unsigned int od1, unsigned int od2, unsigned int od3, + unsigned int vid_pll_div, unsigned int vclk_div, + unsigned int hdmi_tx_div, unsigned int venc_div, + bool hdmi_use_enci, bool vic_alternate_clock) +{ + unsigned int m = 0, frac = 0; + + /* Set HDMI-TX sys clock */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + CTS_HDMI_SYS_SEL_MASK, 0); + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + CTS_HDMI_SYS_DIV_MASK, 0); + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + CTS_HDMI_SYS_EN, CTS_HDMI_SYS_EN); + + /* Set HDMI PLL rate */ + if (!od1 && !od2 && !od3) { + meson_hdmi_pll_generic_set(priv, pll_base_freq); + } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) { + switch (pll_base_freq) { + case 2970000: + m = 0x3d; + frac = vic_alternate_clock ? 0xd02 : 0xe00; + break; + case 4320000: + m = vic_alternate_clock ? 0x59 : 0x5a; + frac = vic_alternate_clock ? 0xe8f : 0; + break; + case 5940000: + m = 0x7b; + frac = vic_alternate_clock ? 0xa05 : 0xc00; + break; + } + + meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3); + } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) { + switch (pll_base_freq) { + case 2970000: + m = 0x7b; + frac = vic_alternate_clock ? 0x281 : 0x300; + break; + case 4320000: + m = vic_alternate_clock ? 0xb3 : 0xb4; + frac = vic_alternate_clock ? 0x347 : 0; + break; + case 5940000: + m = 0xf7; + frac = vic_alternate_clock ? 0x102 : 0x200; + break; + } + + meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3); + } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + switch (pll_base_freq) { + case 2970000: + m = 0x7b; + frac = vic_alternate_clock ? 0x140b4 : 0x18000; + break; + case 4320000: + m = vic_alternate_clock ? 0xb3 : 0xb4; + frac = vic_alternate_clock ? 0x1a3ee : 0; + break; + case 5940000: + m = 0xf7; + frac = vic_alternate_clock ? 0x8148 : 0x10000; + break; + } + + meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3); + } + + /* Setup vid_pll divider */ + meson_vid_pll_set(priv, vid_pll_div); + + /* Set VCLK div */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_SEL_MASK, 0); + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + VCLK_DIV_MASK, vclk_div - 1); + + /* Set HDMI-TX source */ + switch (hdmi_tx_div) { + case 1: + /* enable vclk_div1 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV1_EN, VCLK_DIV1_EN); + + /* select vclk_div1 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 0); + break; + case 2: + /* enable vclk_div2 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV2_EN, VCLK_DIV2_EN); + + /* select vclk_div2 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 1 << HDMI_TX_PIXEL_SEL_SHIFT); + break; + case 4: + /* enable vclk_div4 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV4_EN, VCLK_DIV4_EN); + + /* select vclk_div4 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 2 << HDMI_TX_PIXEL_SEL_SHIFT); + break; + case 6: + /* enable vclk_div6 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV6_EN, VCLK_DIV6_EN); + + /* select vclk_div6 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 3 << HDMI_TX_PIXEL_SEL_SHIFT); + break; + case 12: + /* enable vclk_div12 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV12_EN, VCLK_DIV12_EN); + + /* select vclk_div12 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 4 << HDMI_TX_PIXEL_SEL_SHIFT); + break; + } + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2, + HDMI_TX_PIXEL_EN, HDMI_TX_PIXEL_EN); + + /* Set ENCI/ENCP Source */ + switch (venc_div) { + case 1: + /* enable vclk_div1 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV1_EN, VCLK_DIV1_EN); + + if (hdmi_use_enci) + /* select vclk_div1 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 0); + else + /* select vclk_div1 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 0); + break; + case 2: + /* enable vclk_div2 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV2_EN, VCLK_DIV2_EN); + + if (hdmi_use_enci) + /* select vclk_div2 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 1 << CTS_ENCI_SEL_SHIFT); + else + /* select vclk_div2 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 1 << CTS_ENCP_SEL_SHIFT); + break; + case 4: + /* enable vclk_div4 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV4_EN, VCLK_DIV4_EN); + + if (hdmi_use_enci) + /* select vclk_div4 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 2 << CTS_ENCI_SEL_SHIFT); + else + /* select vclk_div4 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 2 << CTS_ENCP_SEL_SHIFT); + break; + case 6: + /* enable vclk_div6 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV6_EN, VCLK_DIV6_EN); + + if (hdmi_use_enci) + /* select vclk_div6 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 3 << CTS_ENCI_SEL_SHIFT); + else + /* select vclk_div6 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 3 << CTS_ENCP_SEL_SHIFT); + break; + case 12: + /* enable vclk_div12 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV12_EN, VCLK_DIV12_EN); + + if (hdmi_use_enci) + /* select vclk_div12 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 4 << CTS_ENCI_SEL_SHIFT); + else + /* select vclk_div12 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 4 << CTS_ENCP_SEL_SHIFT); + break; + } + + if (hdmi_use_enci) + /* Enable ENCI clock gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2, + CTS_ENCI_EN, CTS_ENCI_EN); + else + /* Enable ENCP clock gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2, + CTS_ENCP_EN, CTS_ENCP_EN); + + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, VCLK_EN, VCLK_EN); +} + +void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + unsigned int phy_freq, unsigned int vclk_freq, + unsigned int venc_freq, unsigned int dac_freq, + bool hdmi_use_enci) +{ + bool vic_alternate_clock = false; + unsigned int freq; + unsigned int hdmi_tx_div; + unsigned int venc_div; + + if (target == MESON_VCLK_TARGET_CVBS) { + meson_venci_cvbs_clock_config(priv); + return; + } else if (target == MESON_VCLK_TARGET_DMT) { + /* + * The DMT clock path is fixed after the PLL: + * - automatic PLL freq + OD management + * - vid_pll_div = VID_PLL_DIV_5 + * - vclk_div = 2 + * - hdmi_tx_div = 1 + * - venc_div = 1 + * - encp encoder + */ + meson_vclk_set(priv, phy_freq, 0, 0, 0, + VID_PLL_DIV_5, 2, 1, 1, false, false); + return; + } + + hdmi_tx_div = vclk_freq / dac_freq; + + if (hdmi_tx_div == 0) { + pr_err("Fatal Error, invalid HDMI-TX freq %d\n", + dac_freq); + return; + } + + venc_div = vclk_freq / venc_freq; + + if (venc_div == 0) { + pr_err("Fatal Error, invalid HDMI venc freq %d\n", + venc_freq); + return; + } + + for (freq = 0 ; params[freq].pixel_freq ; ++freq) { + if ((phy_freq == params[freq].phy_freq || + phy_freq == FREQ_1000_1001(params[freq].phy_freq/10)*10) && + (vclk_freq == params[freq].vclk_freq || + vclk_freq == FREQ_1000_1001(params[freq].vclk_freq))) { + if (vclk_freq != params[freq].vclk_freq) + vic_alternate_clock = true; + else + vic_alternate_clock = false; + + if (freq == MESON_VCLK_HDMI_ENCI_54000 && + !hdmi_use_enci) + continue; + + if (freq == MESON_VCLK_HDMI_DDR_54000 && + hdmi_use_enci) + continue; + + if (freq == MESON_VCLK_HDMI_DDR_148500 && + dac_freq == vclk_freq) + continue; + + if (freq == MESON_VCLK_HDMI_148500 && + dac_freq != vclk_freq) + continue; + break; + } + } + + if (!params[freq].pixel_freq) { + pr_err("Fatal Error, invalid HDMI vclk freq %d\n", vclk_freq); + return; + } + + meson_vclk_set(priv, params[freq].pll_freq, + params[freq].pll_od1, params[freq].pll_od2, + params[freq].pll_od3, params[freq].vid_pll_div, + params[freq].vclk_div, hdmi_tx_div, venc_div, + hdmi_use_enci, vic_alternate_clock); +} +EXPORT_SYMBOL_GPL(meson_vclk_setup); diff --git a/drivers/gpu/drm/meson/meson_vclk.h b/drivers/gpu/drm/meson/meson_vclk.h new file mode 100644 index 0000000000..60617aaf18 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_vclk.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +/* Video Clock */ + +#ifndef __MESON_VCLK_H +#define __MESON_VCLK_H + +#include <drm/drm_modes.h> + +struct meson_drm; + +enum { + MESON_VCLK_TARGET_CVBS = 0, + MESON_VCLK_TARGET_HDMI = 1, + MESON_VCLK_TARGET_DMT = 2, +}; + +/* 27MHz is the CVBS Pixel Clock */ +#define MESON_VCLK_CVBS 27000 + +enum drm_mode_status +meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq); +enum drm_mode_status +meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq, + unsigned int vclk_freq); + +void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + unsigned int phy_freq, unsigned int vclk_freq, + unsigned int venc_freq, unsigned int dac_freq, + bool hdmi_use_enci); + +#endif /* __MESON_VCLK_H */ diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c new file mode 100644 index 0000000000..3bf0d6e4fc --- /dev/null +++ b/drivers/gpu/drm/meson/meson_venc.c @@ -0,0 +1,2000 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#include <linux/bitfield.h> +#include <linux/export.h> +#include <linux/iopoll.h> + +#include <drm/drm_modes.h> + +#include "meson_drv.h" +#include "meson_registers.h" +#include "meson_venc.h" +#include "meson_vpp.h" + +/** + * DOC: Video Encoder + * + * VENC Handle the pixels encoding to the output formats. + * We handle the following encodings : + * + * - CVBS Encoding via the ENCI encoder and VDAC digital to analog converter + * - TMDS/HDMI Encoding via ENCI_DIV and ENCP + * - Setup of more clock rates for HDMI modes + * + * What is missing : + * + * - LCD Panel encoding via ENCL + * - TV Panel encoding via ENCT + * + * VENC paths : + * + * .. code:: + * + * _____ _____ ____________________ + * vd1---| |-| | | VENC /---------|----VDAC + * vd2---| VIU |-| VPP |-|-----ENCI/-ENCI_DVI-|-| + * osd1--| |-| | | \ | X--HDMI-TX + * osd2--|_____|-|_____| | |\-ENCP--ENCP_DVI-|-| + * | | | + * | \--ENCL-----------|----LVDS + * |____________________| + * + * The ENCI is designed for PAl or NTSC encoding and can go through the VDAC + * directly for CVBS encoding or through the ENCI_DVI encoder for HDMI. + * The ENCP is designed for Progressive encoding but can also generate + * 1080i interlaced pixels, and was initially designed to encode pixels for + * VDAC to output RGB ou YUV analog outputs. + * It's output is only used through the ENCP_DVI encoder for HDMI. + * The ENCL LVDS encoder is not implemented. + * + * The ENCI and ENCP encoders needs specially defined parameters for each + * supported mode and thus cannot be determined from standard video timings. + * + * The ENCI end ENCP DVI encoders are more generic and can generate any timings + * from the pixel data generated by ENCI or ENCP, so can use the standard video + * timings are source for HW parameters. + */ + +/* HHI Registers */ +#define HHI_GCLK_MPEG2 0x148 /* 0x52 offset in data sheet */ +#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ +#define HHI_VDAC_CNTL0_G12A 0x2EC /* 0xbb offset in data sheet */ +#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ +#define HHI_VDAC_CNTL1_G12A 0x2F0 /* 0xbc offset in data sheet */ +#define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 offset in data sheet */ + +struct meson_cvbs_enci_mode meson_cvbs_enci_pal = { + .mode_tag = MESON_VENC_MODE_CVBS_PAL, + .hso_begin = 3, + .hso_end = 129, + .vso_even = 3, + .vso_odd = 260, + .macv_max_amp = 7, + .video_prog_mode = 0xff, + .video_mode = 0x13, + .sch_adjust = 0x28, + .yc_delay = 0x343, + .pixel_start = 251, + .pixel_end = 1691, + .top_field_line_start = 22, + .top_field_line_end = 310, + .bottom_field_line_start = 23, + .bottom_field_line_end = 311, + .video_saturation = 9, + .video_contrast = 0, + .video_brightness = 0, + .video_hue = 0, + .analog_sync_adj = 0x8080, +}; + +struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc = { + .mode_tag = MESON_VENC_MODE_CVBS_NTSC, + .hso_begin = 5, + .hso_end = 129, + .vso_even = 3, + .vso_odd = 260, + .macv_max_amp = 0xb, + .video_prog_mode = 0xf0, + .video_mode = 0x8, + .sch_adjust = 0x20, + .yc_delay = 0x333, + .pixel_start = 227, + .pixel_end = 1667, + .top_field_line_start = 18, + .top_field_line_end = 258, + .bottom_field_line_start = 19, + .bottom_field_line_end = 259, + .video_saturation = 18, + .video_contrast = 3, + .video_brightness = 0, + .video_hue = 0, + .analog_sync_adj = 0x9c00, +}; + +union meson_hdmi_venc_mode { + struct { + unsigned int mode_tag; + unsigned int hso_begin; + unsigned int hso_end; + unsigned int vso_even; + unsigned int vso_odd; + unsigned int macv_max_amp; + unsigned int video_prog_mode; + unsigned int video_mode; + unsigned int sch_adjust; + unsigned int yc_delay; + unsigned int pixel_start; + unsigned int pixel_end; + unsigned int top_field_line_start; + unsigned int top_field_line_end; + unsigned int bottom_field_line_start; + unsigned int bottom_field_line_end; + } enci; + struct { + unsigned int dvi_settings; + unsigned int video_mode; + unsigned int video_mode_adv; + unsigned int video_prog_mode; + bool video_prog_mode_present; + unsigned int video_sync_mode; + bool video_sync_mode_present; + unsigned int video_yc_dly; + bool video_yc_dly_present; + unsigned int video_rgb_ctrl; + bool video_rgb_ctrl_present; + unsigned int video_filt_ctrl; + bool video_filt_ctrl_present; + unsigned int video_ofld_voav_ofst; + bool video_ofld_voav_ofst_present; + unsigned int yfp1_htime; + unsigned int yfp2_htime; + unsigned int max_pxcnt; + unsigned int hspuls_begin; + unsigned int hspuls_end; + unsigned int hspuls_switch; + unsigned int vspuls_begin; + unsigned int vspuls_end; + unsigned int vspuls_bline; + unsigned int vspuls_eline; + unsigned int eqpuls_begin; + bool eqpuls_begin_present; + unsigned int eqpuls_end; + bool eqpuls_end_present; + unsigned int eqpuls_bline; + bool eqpuls_bline_present; + unsigned int eqpuls_eline; + bool eqpuls_eline_present; + unsigned int havon_begin; + unsigned int havon_end; + unsigned int vavon_bline; + unsigned int vavon_eline; + unsigned int hso_begin; + unsigned int hso_end; + unsigned int vso_begin; + unsigned int vso_end; + unsigned int vso_bline; + unsigned int vso_eline; + bool vso_eline_present; + unsigned int sy_val; + bool sy_val_present; + unsigned int sy2_val; + bool sy2_val_present; + unsigned int max_lncnt; + } encp; +}; + +static union meson_hdmi_venc_mode meson_hdmi_enci_mode_480i = { + .enci = { + .hso_begin = 5, + .hso_end = 129, + .vso_even = 3, + .vso_odd = 260, + .macv_max_amp = 0xb, + .video_prog_mode = 0xf0, + .video_mode = 0x8, + .sch_adjust = 0x20, + .yc_delay = 0, + .pixel_start = 227, + .pixel_end = 1667, + .top_field_line_start = 18, + .top_field_line_end = 258, + .bottom_field_line_start = 19, + .bottom_field_line_end = 259, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_enci_mode_576i = { + .enci = { + .hso_begin = 3, + .hso_end = 129, + .vso_even = 3, + .vso_odd = 260, + .macv_max_amp = 0x7, + .video_prog_mode = 0xff, + .video_mode = 0x13, + .sch_adjust = 0x28, + .yc_delay = 0x333, + .pixel_start = 251, + .pixel_end = 1691, + .top_field_line_start = 22, + .top_field_line_end = 310, + .bottom_field_line_start = 23, + .bottom_field_line_end = 311, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_encp_mode_480p = { + .encp = { + .dvi_settings = 0x21, + .video_mode = 0x4000, + .video_mode_adv = 0x9, + .video_prog_mode = 0, + .video_prog_mode_present = true, + .video_sync_mode = 7, + .video_sync_mode_present = true, + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x2052, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 244, + .yfp2_htime = 1630, + .max_pxcnt = 1715, + .hspuls_begin = 0x22, + .hspuls_end = 0xa0, + .hspuls_switch = 88, + .vspuls_begin = 0, + .vspuls_end = 1589, + .vspuls_bline = 0, + .vspuls_eline = 5, + .havon_begin = 249, + .havon_end = 1689, + .vavon_bline = 42, + .vavon_eline = 521, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 3, + .hso_end = 5, + .vso_begin = 3, + .vso_end = 5, + .vso_bline = 0, + /* vso_eline */ + .sy_val = 8, + .sy_val_present = true, + .sy2_val = 0x1d8, + .sy2_val_present = true, + .max_lncnt = 524, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_encp_mode_576p = { + .encp = { + .dvi_settings = 0x21, + .video_mode = 0x4000, + .video_mode_adv = 0x9, + .video_prog_mode = 0, + .video_prog_mode_present = true, + .video_sync_mode = 7, + .video_sync_mode_present = true, + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x52, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 235, + .yfp2_htime = 1674, + .max_pxcnt = 1727, + .hspuls_begin = 0, + .hspuls_end = 0x80, + .hspuls_switch = 88, + .vspuls_begin = 0, + .vspuls_end = 1599, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 235, + .havon_end = 1674, + .vavon_bline = 44, + .vavon_eline = 619, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 0x80, + .hso_end = 0, + .vso_begin = 0, + .vso_end = 5, + .vso_bline = 0, + /* vso_eline */ + .sy_val = 8, + .sy_val_present = true, + .sy2_val = 0x1d8, + .sy2_val_present = true, + .max_lncnt = 624, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_encp_mode_720p60 = { + .encp = { + .dvi_settings = 0x2029, + .video_mode = 0x4040, + .video_mode_adv = 0x19, + /* video_prog_mode */ + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + /* video_filt_ctrl */ + /* video_ofld_voav_ofst */ + .yfp1_htime = 648, + .yfp2_htime = 3207, + .max_pxcnt = 3299, + .hspuls_begin = 80, + .hspuls_end = 240, + .hspuls_switch = 80, + .vspuls_begin = 688, + .vspuls_end = 3248, + .vspuls_bline = 4, + .vspuls_eline = 8, + .havon_begin = 648, + .havon_end = 3207, + .vavon_bline = 29, + .vavon_eline = 748, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 256, + .hso_end = 168, + .vso_begin = 168, + .vso_end = 256, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 749, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_encp_mode_720p50 = { + .encp = { + .dvi_settings = 0x202d, + .video_mode = 0x4040, + .video_mode_adv = 0x19, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x407, + .video_sync_mode_present = true, + .video_yc_dly = 0, + .video_yc_dly_present = true, + /* video_rgb_ctrl */ + /* video_filt_ctrl */ + /* video_ofld_voav_ofst */ + .yfp1_htime = 648, + .yfp2_htime = 3207, + .max_pxcnt = 3959, + .hspuls_begin = 80, + .hspuls_end = 240, + .hspuls_switch = 80, + .vspuls_begin = 688, + .vspuls_end = 3248, + .vspuls_bline = 4, + .vspuls_eline = 8, + .havon_begin = 648, + .havon_end = 3207, + .vavon_bline = 29, + .vavon_eline = 748, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 128, + .hso_end = 208, + .vso_begin = 128, + .vso_end = 128, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 749, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080i60 = { + .encp = { + .dvi_settings = 0x2029, + .video_mode = 0x5ffc, + .video_mode_adv = 0x19, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x207, + .video_sync_mode_present = true, + /* video_yc_dly */ + /* video_rgb_ctrl */ + /* video_filt_ctrl */ + .video_ofld_voav_ofst = 0x11, + .video_ofld_voav_ofst_present = true, + .yfp1_htime = 516, + .yfp2_htime = 4355, + .max_pxcnt = 4399, + .hspuls_begin = 88, + .hspuls_end = 264, + .hspuls_switch = 88, + .vspuls_begin = 440, + .vspuls_end = 2200, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 516, + .havon_end = 4355, + .vavon_bline = 20, + .vavon_eline = 559, + .eqpuls_begin = 2288, + .eqpuls_begin_present = true, + .eqpuls_end = 2464, + .eqpuls_end_present = true, + .eqpuls_bline = 0, + .eqpuls_bline_present = true, + .eqpuls_eline = 4, + .eqpuls_eline_present = true, + .hso_begin = 264, + .hso_end = 176, + .vso_begin = 88, + .vso_end = 88, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080i50 = { + .encp = { + .dvi_settings = 0x202d, + .video_mode = 0x5ffc, + .video_mode_adv = 0x19, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x7, + .video_sync_mode_present = true, + /* video_yc_dly */ + /* video_rgb_ctrl */ + /* video_filt_ctrl */ + .video_ofld_voav_ofst = 0x11, + .video_ofld_voav_ofst_present = true, + .yfp1_htime = 526, + .yfp2_htime = 4365, + .max_pxcnt = 5279, + .hspuls_begin = 88, + .hspuls_end = 264, + .hspuls_switch = 88, + .vspuls_begin = 440, + .vspuls_end = 2200, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 526, + .havon_end = 4365, + .vavon_bline = 20, + .vavon_eline = 559, + .eqpuls_begin = 2288, + .eqpuls_begin_present = true, + .eqpuls_end = 2464, + .eqpuls_end_present = true, + .eqpuls_bline = 0, + .eqpuls_bline_present = true, + .eqpuls_eline = 4, + .eqpuls_eline_present = true, + .hso_begin = 142, + .hso_end = 230, + .vso_begin = 142, + .vso_end = 142, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p24 = { + .encp = { + .dvi_settings = 0xd, + .video_mode = 0x4040, + .video_mode_adv = 0x18, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x7, + .video_sync_mode_present = true, + .video_yc_dly = 0, + .video_yc_dly_present = true, + .video_rgb_ctrl = 2, + .video_rgb_ctrl_present = true, + .video_filt_ctrl = 0x1052, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 271, + .yfp2_htime = 2190, + .max_pxcnt = 2749, + .hspuls_begin = 44, + .hspuls_end = 132, + .hspuls_switch = 44, + .vspuls_begin = 220, + .vspuls_end = 2140, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 271, + .havon_end = 2190, + .vavon_bline = 41, + .vavon_eline = 1120, + /* eqpuls_begin */ + /* eqpuls_end */ + .eqpuls_bline = 0, + .eqpuls_bline_present = true, + .eqpuls_eline = 4, + .eqpuls_eline_present = true, + .hso_begin = 79, + .hso_end = 123, + .vso_begin = 79, + .vso_end = 79, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p30 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x18, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1052, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 2060, + .max_pxcnt = 2199, + .hspuls_begin = 2156, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 2067, + .vavon_bline = 41, + .vavon_eline = 1120, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156, + .vso_begin = 2100, + .vso_end = 2164, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p50 = { + .encp = { + .dvi_settings = 0xd, + .video_mode = 0x4040, + .video_mode_adv = 0x18, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x7, + .video_sync_mode_present = true, + .video_yc_dly = 0, + .video_yc_dly_present = true, + .video_rgb_ctrl = 2, + .video_rgb_ctrl_present = true, + /* video_filt_ctrl */ + /* video_ofld_voav_ofst */ + .yfp1_htime = 271, + .yfp2_htime = 2190, + .max_pxcnt = 2639, + .hspuls_begin = 44, + .hspuls_end = 132, + .hspuls_switch = 44, + .vspuls_begin = 220, + .vspuls_end = 2140, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 271, + .havon_end = 2190, + .vavon_bline = 41, + .vavon_eline = 1120, + /* eqpuls_begin */ + /* eqpuls_end */ + .eqpuls_bline = 0, + .eqpuls_bline_present = true, + .eqpuls_eline = 4, + .eqpuls_eline_present = true, + .hso_begin = 79, + .hso_end = 123, + .vso_begin = 79, + .vso_end = 79, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p60 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x18, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1052, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 2060, + .max_pxcnt = 2199, + .hspuls_begin = 2156, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 2067, + .vavon_bline = 41, + .vavon_eline = 1120, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156, + .vso_begin = 2100, + .vso_end = 2164, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p24 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x8, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1000, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 140+3840, + .max_pxcnt = 3840+1660-1, + .hspuls_begin = 2156+1920, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059+1920, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 3987, + .vavon_bline = 89, + .vavon_eline = 2248, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156+1920, + .vso_begin = 2100+1920, + .vso_end = 2164+1920, + .vso_bline = 51, + .vso_eline = 53, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 2249, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p25 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x8, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1000, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 140+3840, + .max_pxcnt = 3840+1440-1, + .hspuls_begin = 2156+1920, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059+1920, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 3987, + .vavon_bline = 89, + .vavon_eline = 2248, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156+1920, + .vso_begin = 2100+1920, + .vso_end = 2164+1920, + .vso_bline = 51, + .vso_eline = 53, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 2249, + }, +}; + +static union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p30 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x8, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1000, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 140+3840, + .max_pxcnt = 3840+560-1, + .hspuls_begin = 2156+1920, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059+1920, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 3987, + .vavon_bline = 89, + .vavon_eline = 2248, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156+1920, + .vso_begin = 2100+1920, + .vso_end = 2164+1920, + .vso_bline = 51, + .vso_eline = 53, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 2249, + }, +}; + +static struct meson_hdmi_venc_vic_mode { + unsigned int vic; + union meson_hdmi_venc_mode *mode; +} meson_hdmi_venc_vic_modes[] = { + { 6, &meson_hdmi_enci_mode_480i }, + { 7, &meson_hdmi_enci_mode_480i }, + { 21, &meson_hdmi_enci_mode_576i }, + { 22, &meson_hdmi_enci_mode_576i }, + { 2, &meson_hdmi_encp_mode_480p }, + { 3, &meson_hdmi_encp_mode_480p }, + { 17, &meson_hdmi_encp_mode_576p }, + { 18, &meson_hdmi_encp_mode_576p }, + { 4, &meson_hdmi_encp_mode_720p60 }, + { 19, &meson_hdmi_encp_mode_720p50 }, + { 5, &meson_hdmi_encp_mode_1080i60 }, + { 20, &meson_hdmi_encp_mode_1080i50 }, + { 32, &meson_hdmi_encp_mode_1080p24 }, + { 33, &meson_hdmi_encp_mode_1080p50 }, + { 34, &meson_hdmi_encp_mode_1080p30 }, + { 31, &meson_hdmi_encp_mode_1080p50 }, + { 16, &meson_hdmi_encp_mode_1080p60 }, + { 93, &meson_hdmi_encp_mode_2160p24 }, + { 94, &meson_hdmi_encp_mode_2160p25 }, + { 95, &meson_hdmi_encp_mode_2160p30 }, + { 96, &meson_hdmi_encp_mode_2160p25 }, + { 97, &meson_hdmi_encp_mode_2160p30 }, + { 0, NULL}, /* sentinel */ +}; + +static signed int to_signed(unsigned int a) +{ + if (a <= 7) + return a; + else + return a - 16; +} + +static unsigned long modulo(unsigned long a, unsigned long b) +{ + if (a >= b) + return a - b; + else + return a; +} + +enum drm_mode_status +meson_venc_hdmi_supported_mode(const struct drm_display_mode *mode) +{ + if (mode->flags & ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC | + DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC)) + return MODE_BAD; + + if (mode->hdisplay < 400 || mode->hdisplay > 1920) + return MODE_BAD_HVALUE; + + if (mode->vdisplay < 480 || mode->vdisplay > 1920) + return MODE_BAD_VVALUE; + + return MODE_OK; +} +EXPORT_SYMBOL_GPL(meson_venc_hdmi_supported_mode); + +bool meson_venc_hdmi_supported_vic(int vic) +{ + struct meson_hdmi_venc_vic_mode *vmode = meson_hdmi_venc_vic_modes; + + while (vmode->vic && vmode->mode) { + if (vmode->vic == vic) + return true; + vmode++; + } + + return false; +} +EXPORT_SYMBOL_GPL(meson_venc_hdmi_supported_vic); + +static void meson_venc_hdmi_get_dmt_vmode(const struct drm_display_mode *mode, + union meson_hdmi_venc_mode *dmt_mode) +{ + memset(dmt_mode, 0, sizeof(*dmt_mode)); + + dmt_mode->encp.dvi_settings = 0x21; + dmt_mode->encp.video_mode = 0x4040; + dmt_mode->encp.video_mode_adv = 0x18; + dmt_mode->encp.max_pxcnt = mode->htotal - 1; + dmt_mode->encp.havon_begin = mode->htotal - mode->hsync_start; + dmt_mode->encp.havon_end = dmt_mode->encp.havon_begin + + mode->hdisplay - 1; + dmt_mode->encp.vavon_bline = mode->vtotal - mode->vsync_start; + dmt_mode->encp.vavon_eline = dmt_mode->encp.vavon_bline + + mode->vdisplay - 1; + dmt_mode->encp.hso_begin = 0; + dmt_mode->encp.hso_end = mode->hsync_end - mode->hsync_start; + dmt_mode->encp.vso_begin = 30; + dmt_mode->encp.vso_end = 50; + dmt_mode->encp.vso_bline = 0; + dmt_mode->encp.vso_eline = mode->vsync_end - mode->vsync_start; + dmt_mode->encp.vso_eline_present = true; + dmt_mode->encp.max_lncnt = mode->vtotal - 1; +} + +static union meson_hdmi_venc_mode *meson_venc_hdmi_get_vic_vmode(int vic) +{ + struct meson_hdmi_venc_vic_mode *vmode = meson_hdmi_venc_vic_modes; + + while (vmode->vic && vmode->mode) { + if (vmode->vic == vic) + return vmode->mode; + vmode++; + } + + return NULL; +} + +bool meson_venc_hdmi_venc_repeat(int vic) +{ + /* Repeat VENC pixels for 480/576i/p, 720p50/60 and 1080p50/60 */ + if (vic == 6 || vic == 7 || /* 480i */ + vic == 21 || vic == 22 || /* 576i */ + vic == 17 || vic == 18 || /* 576p */ + vic == 2 || vic == 3 || /* 480p */ + vic == 4 || /* 720p60 */ + vic == 19 || /* 720p50 */ + vic == 5 || /* 1080i60 */ + vic == 20) /* 1080i50 */ + return true; + + return false; +} +EXPORT_SYMBOL_GPL(meson_venc_hdmi_venc_repeat); + +void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic, + unsigned int ycrcb_map, + bool yuv420_mode, + const struct drm_display_mode *mode) +{ + union meson_hdmi_venc_mode *vmode = NULL; + union meson_hdmi_venc_mode vmode_dmt; + bool use_enci = false; + bool venc_repeat = false; + bool hdmi_repeat = false; + unsigned int venc_hdmi_latency = 2; + unsigned long total_pixels_venc = 0; + unsigned long active_pixels_venc = 0; + unsigned long front_porch_venc = 0; + unsigned long hsync_pixels_venc = 0; + unsigned long de_h_begin = 0; + unsigned long de_h_end = 0; + unsigned long de_v_begin_even = 0; + unsigned long de_v_end_even = 0; + unsigned long de_v_begin_odd = 0; + unsigned long de_v_end_odd = 0; + unsigned long hs_begin = 0; + unsigned long hs_end = 0; + unsigned long vs_adjust = 0; + unsigned long vs_bline_evn = 0; + unsigned long vs_eline_evn = 0; + unsigned long vs_bline_odd = 0; + unsigned long vs_eline_odd = 0; + unsigned long vso_begin_evn = 0; + unsigned long vso_begin_odd = 0; + unsigned int eof_lines; + unsigned int sof_lines; + unsigned int vsync_lines; + u32 reg; + + /* Use VENCI for 480i and 576i and double HDMI pixels */ + if (mode->flags & DRM_MODE_FLAG_DBLCLK) { + hdmi_repeat = true; + use_enci = true; + venc_hdmi_latency = 1; + } + + if (meson_venc_hdmi_supported_vic(vic)) { + vmode = meson_venc_hdmi_get_vic_vmode(vic); + if (!vmode) { + dev_err(priv->dev, "%s: Fatal Error, unsupported mode " + DRM_MODE_FMT "\n", __func__, + DRM_MODE_ARG(mode)); + return; + } + } else { + meson_venc_hdmi_get_dmt_vmode(mode, &vmode_dmt); + vmode = &vmode_dmt; + use_enci = false; + } + + /* Repeat VENC pixels for 480/576i/p, 720p50/60 and 1080p50/60 */ + if (meson_venc_hdmi_venc_repeat(vic)) + venc_repeat = true; + + eof_lines = mode->vsync_start - mode->vdisplay; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + eof_lines /= 2; + sof_lines = mode->vtotal - mode->vsync_end; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + sof_lines /= 2; + vsync_lines = mode->vsync_end - mode->vsync_start; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + vsync_lines /= 2; + + total_pixels_venc = mode->htotal; + if (hdmi_repeat) + total_pixels_venc /= 2; + if (venc_repeat) + total_pixels_venc *= 2; + + active_pixels_venc = mode->hdisplay; + if (hdmi_repeat) + active_pixels_venc /= 2; + if (venc_repeat) + active_pixels_venc *= 2; + + front_porch_venc = (mode->hsync_start - mode->hdisplay); + if (hdmi_repeat) + front_porch_venc /= 2; + if (venc_repeat) + front_porch_venc *= 2; + + hsync_pixels_venc = (mode->hsync_end - mode->hsync_start); + if (hdmi_repeat) + hsync_pixels_venc /= 2; + if (venc_repeat) + hsync_pixels_venc *= 2; + + /* Disable VDACs */ + writel_bits_relaxed(0xff, 0xff, + priv->io_base + _REG(VENC_VDAC_SETTING)); + + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN)); + writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN)); + + if (use_enci) { + unsigned int lines_f0; + unsigned int lines_f1; + + /* CVBS Filter settings */ + writel_relaxed(ENCI_CFILT_CMPT_SEL_HIGH | 0x10, + priv->io_base + _REG(ENCI_CFILT_CTRL)); + writel_relaxed(ENCI_CFILT_CMPT_CR_DLY(2) | + ENCI_CFILT_CMPT_CB_DLY(1), + priv->io_base + _REG(ENCI_CFILT_CTRL2)); + + /* Digital Video Select : Interlace, clk27 clk, external */ + writel_relaxed(0, priv->io_base + _REG(VENC_DVI_SETTING)); + + /* Reset Video Mode */ + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE)); + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE_ADV)); + + /* Horizontal sync signal output */ + writel_relaxed(vmode->enci.hso_begin, + priv->io_base + _REG(ENCI_SYNC_HSO_BEGIN)); + writel_relaxed(vmode->enci.hso_end, + priv->io_base + _REG(ENCI_SYNC_HSO_END)); + + /* Vertical Sync lines */ + writel_relaxed(vmode->enci.vso_even, + priv->io_base + _REG(ENCI_SYNC_VSO_EVNLN)); + writel_relaxed(vmode->enci.vso_odd, + priv->io_base + _REG(ENCI_SYNC_VSO_ODDLN)); + + /* Macrovision max amplitude change */ + writel_relaxed(ENCI_MACV_MAX_AMP_ENABLE_CHANGE | + ENCI_MACV_MAX_AMP_VAL(vmode->enci.macv_max_amp), + priv->io_base + _REG(ENCI_MACV_MAX_AMP)); + + /* Video mode */ + writel_relaxed(vmode->enci.video_prog_mode, + priv->io_base + _REG(VENC_VIDEO_PROG_MODE)); + writel_relaxed(vmode->enci.video_mode, + priv->io_base + _REG(ENCI_VIDEO_MODE)); + + /* + * Advanced Video Mode : + * Demux shifting 0x2 + * Blank line end at line17/22 + * High bandwidth Luma Filter + * Low bandwidth Chroma Filter + * Bypass luma low pass filter + * No macrovision on CSYNC + */ + writel_relaxed(ENCI_VIDEO_MODE_ADV_DMXMD(2) | + ENCI_VIDEO_MODE_ADV_VBICTL_LINE_17_22 | + ENCI_VIDEO_MODE_ADV_YBW_HIGH, + priv->io_base + _REG(ENCI_VIDEO_MODE_ADV)); + + writel(vmode->enci.sch_adjust, + priv->io_base + _REG(ENCI_VIDEO_SCH)); + + /* Sync mode : MASTER Master mode, free run, send HSO/VSO out */ + writel_relaxed(0x07, priv->io_base + _REG(ENCI_SYNC_MODE)); + + if (vmode->enci.yc_delay) + writel_relaxed(vmode->enci.yc_delay, + priv->io_base + _REG(ENCI_YC_DELAY)); + + + /* UNreset Interlaced TV Encoder */ + writel_relaxed(0, priv->io_base + _REG(ENCI_DBG_PX_RST)); + + /* + * Enable Vfifo2vd and set Y_Cb_Y_Cr: + * Corresponding value: + * Y => 00 or 10 + * Cb => 01 + * Cr => 11 + * Ex: 0x4e => 01001110 would mean Cb/Y/Cr/Y + */ + writel_relaxed(ENCI_VFIFO2VD_CTL_ENABLE | + ENCI_VFIFO2VD_CTL_VD_SEL(0x4e), + priv->io_base + _REG(ENCI_VFIFO2VD_CTL)); + + /* Timings */ + writel_relaxed(vmode->enci.pixel_start, + priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_START)); + writel_relaxed(vmode->enci.pixel_end, + priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_END)); + + writel_relaxed(vmode->enci.top_field_line_start, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_START)); + writel_relaxed(vmode->enci.top_field_line_end, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_END)); + + writel_relaxed(vmode->enci.bottom_field_line_start, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_START)); + writel_relaxed(vmode->enci.bottom_field_line_end, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_END)); + + /* Select ENCI for VIU */ + meson_vpp_setup_mux(priv, MESON_VIU_VPP_MUX_ENCI); + + /* Interlace video enable */ + writel_relaxed(ENCI_VIDEO_EN_ENABLE, + priv->io_base + _REG(ENCI_VIDEO_EN)); + + lines_f0 = mode->vtotal >> 1; + lines_f1 = lines_f0 + 1; + + de_h_begin = modulo(readl_relaxed(priv->io_base + + _REG(ENCI_VFIFO2VD_PIXEL_START)) + + venc_hdmi_latency, + total_pixels_venc); + de_h_end = modulo(de_h_begin + active_pixels_venc, + total_pixels_venc); + + writel_relaxed(de_h_begin, + priv->io_base + _REG(ENCI_DE_H_BEGIN)); + writel_relaxed(de_h_end, + priv->io_base + _REG(ENCI_DE_H_END)); + + de_v_begin_even = readl_relaxed(priv->io_base + + _REG(ENCI_VFIFO2VD_LINE_TOP_START)); + de_v_end_even = de_v_begin_even + mode->vdisplay; + de_v_begin_odd = readl_relaxed(priv->io_base + + _REG(ENCI_VFIFO2VD_LINE_BOT_START)); + de_v_end_odd = de_v_begin_odd + mode->vdisplay; + + writel_relaxed(de_v_begin_even, + priv->io_base + _REG(ENCI_DE_V_BEGIN_EVEN)); + writel_relaxed(de_v_end_even, + priv->io_base + _REG(ENCI_DE_V_END_EVEN)); + writel_relaxed(de_v_begin_odd, + priv->io_base + _REG(ENCI_DE_V_BEGIN_ODD)); + writel_relaxed(de_v_end_odd, + priv->io_base + _REG(ENCI_DE_V_END_ODD)); + + /* Program Hsync timing */ + hs_begin = de_h_end + front_porch_venc; + if (de_h_end + front_porch_venc >= total_pixels_venc) { + hs_begin -= total_pixels_venc; + vs_adjust = 1; + } else { + hs_begin = de_h_end + front_porch_venc; + vs_adjust = 0; + } + + hs_end = modulo(hs_begin + hsync_pixels_venc, + total_pixels_venc); + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCI_DVI_HSO_BEGIN)); + writel_relaxed(hs_end, + priv->io_base + _REG(ENCI_DVI_HSO_END)); + + /* Program Vsync timing for even field */ + if (((de_v_end_odd - 1) + eof_lines + vs_adjust) >= lines_f1) { + vs_bline_evn = (de_v_end_odd - 1) + + eof_lines + + vs_adjust + - lines_f1; + vs_eline_evn = vs_bline_evn + vsync_lines; + + writel_relaxed(vs_bline_evn, + priv->io_base + _REG(ENCI_DVI_VSO_BLINE_EVN)); + + writel_relaxed(vs_eline_evn, + priv->io_base + _REG(ENCI_DVI_VSO_ELINE_EVN)); + + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCI_DVI_VSO_BEGIN_EVN)); + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCI_DVI_VSO_END_EVN)); + } else { + vs_bline_odd = (de_v_end_odd - 1) + + eof_lines + + vs_adjust; + + writel_relaxed(vs_bline_odd, + priv->io_base + _REG(ENCI_DVI_VSO_BLINE_ODD)); + + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCI_DVI_VSO_BEGIN_ODD)); + + if ((vs_bline_odd + vsync_lines) >= lines_f1) { + vs_eline_evn = vs_bline_odd + + vsync_lines + - lines_f1; + + writel_relaxed(vs_eline_evn, priv->io_base + + _REG(ENCI_DVI_VSO_ELINE_EVN)); + + writel_relaxed(hs_begin, priv->io_base + + _REG(ENCI_DVI_VSO_END_EVN)); + } else { + vs_eline_odd = vs_bline_odd + + vsync_lines; + + writel_relaxed(vs_eline_odd, priv->io_base + + _REG(ENCI_DVI_VSO_ELINE_ODD)); + + writel_relaxed(hs_begin, priv->io_base + + _REG(ENCI_DVI_VSO_END_ODD)); + } + } + + /* Program Vsync timing for odd field */ + if (((de_v_end_even - 1) + (eof_lines + 1)) >= lines_f0) { + vs_bline_odd = (de_v_end_even - 1) + + (eof_lines + 1) + - lines_f0; + vs_eline_odd = vs_bline_odd + vsync_lines; + + writel_relaxed(vs_bline_odd, + priv->io_base + _REG(ENCI_DVI_VSO_BLINE_ODD)); + + writel_relaxed(vs_eline_odd, + priv->io_base + _REG(ENCI_DVI_VSO_ELINE_ODD)); + + vso_begin_odd = modulo(hs_begin + + (total_pixels_venc >> 1), + total_pixels_venc); + + writel_relaxed(vso_begin_odd, + priv->io_base + _REG(ENCI_DVI_VSO_BEGIN_ODD)); + writel_relaxed(vso_begin_odd, + priv->io_base + _REG(ENCI_DVI_VSO_END_ODD)); + } else { + vs_bline_evn = (de_v_end_even - 1) + + (eof_lines + 1); + + writel_relaxed(vs_bline_evn, + priv->io_base + _REG(ENCI_DVI_VSO_BLINE_EVN)); + + vso_begin_evn = modulo(hs_begin + + (total_pixels_venc >> 1), + total_pixels_venc); + + writel_relaxed(vso_begin_evn, priv->io_base + + _REG(ENCI_DVI_VSO_BEGIN_EVN)); + + if (vs_bline_evn + vsync_lines >= lines_f0) { + vs_eline_odd = vs_bline_evn + + vsync_lines + - lines_f0; + + writel_relaxed(vs_eline_odd, priv->io_base + + _REG(ENCI_DVI_VSO_ELINE_ODD)); + + writel_relaxed(vso_begin_evn, priv->io_base + + _REG(ENCI_DVI_VSO_END_ODD)); + } else { + vs_eline_evn = vs_bline_evn + vsync_lines; + + writel_relaxed(vs_eline_evn, priv->io_base + + _REG(ENCI_DVI_VSO_ELINE_EVN)); + + writel_relaxed(vso_begin_evn, priv->io_base + + _REG(ENCI_DVI_VSO_END_EVN)); + } + } + } else { + writel_relaxed(vmode->encp.dvi_settings, + priv->io_base + _REG(VENC_DVI_SETTING)); + writel_relaxed(vmode->encp.video_mode, + priv->io_base + _REG(ENCP_VIDEO_MODE)); + writel_relaxed(vmode->encp.video_mode_adv, + priv->io_base + _REG(ENCP_VIDEO_MODE_ADV)); + if (vmode->encp.video_prog_mode_present) + writel_relaxed(vmode->encp.video_prog_mode, + priv->io_base + _REG(VENC_VIDEO_PROG_MODE)); + if (vmode->encp.video_sync_mode_present) + writel_relaxed(vmode->encp.video_sync_mode, + priv->io_base + _REG(ENCP_VIDEO_SYNC_MODE)); + if (vmode->encp.video_yc_dly_present) + writel_relaxed(vmode->encp.video_yc_dly, + priv->io_base + _REG(ENCP_VIDEO_YC_DLY)); + if (vmode->encp.video_rgb_ctrl_present) + writel_relaxed(vmode->encp.video_rgb_ctrl, + priv->io_base + _REG(ENCP_VIDEO_RGB_CTRL)); + if (vmode->encp.video_filt_ctrl_present) + writel_relaxed(vmode->encp.video_filt_ctrl, + priv->io_base + _REG(ENCP_VIDEO_FILT_CTRL)); + if (vmode->encp.video_ofld_voav_ofst_present) + writel_relaxed(vmode->encp.video_ofld_voav_ofst, + priv->io_base + + _REG(ENCP_VIDEO_OFLD_VOAV_OFST)); + writel_relaxed(vmode->encp.yfp1_htime, + priv->io_base + _REG(ENCP_VIDEO_YFP1_HTIME)); + writel_relaxed(vmode->encp.yfp2_htime, + priv->io_base + _REG(ENCP_VIDEO_YFP2_HTIME)); + writel_relaxed(vmode->encp.max_pxcnt, + priv->io_base + _REG(ENCP_VIDEO_MAX_PXCNT)); + writel_relaxed(vmode->encp.hspuls_begin, + priv->io_base + _REG(ENCP_VIDEO_HSPULS_BEGIN)); + writel_relaxed(vmode->encp.hspuls_end, + priv->io_base + _REG(ENCP_VIDEO_HSPULS_END)); + writel_relaxed(vmode->encp.hspuls_switch, + priv->io_base + _REG(ENCP_VIDEO_HSPULS_SWITCH)); + writel_relaxed(vmode->encp.vspuls_begin, + priv->io_base + _REG(ENCP_VIDEO_VSPULS_BEGIN)); + writel_relaxed(vmode->encp.vspuls_end, + priv->io_base + _REG(ENCP_VIDEO_VSPULS_END)); + writel_relaxed(vmode->encp.vspuls_bline, + priv->io_base + _REG(ENCP_VIDEO_VSPULS_BLINE)); + writel_relaxed(vmode->encp.vspuls_eline, + priv->io_base + _REG(ENCP_VIDEO_VSPULS_ELINE)); + if (vmode->encp.eqpuls_begin_present) + writel_relaxed(vmode->encp.eqpuls_begin, + priv->io_base + _REG(ENCP_VIDEO_EQPULS_BEGIN)); + if (vmode->encp.eqpuls_end_present) + writel_relaxed(vmode->encp.eqpuls_end, + priv->io_base + _REG(ENCP_VIDEO_EQPULS_END)); + if (vmode->encp.eqpuls_bline_present) + writel_relaxed(vmode->encp.eqpuls_bline, + priv->io_base + _REG(ENCP_VIDEO_EQPULS_BLINE)); + if (vmode->encp.eqpuls_eline_present) + writel_relaxed(vmode->encp.eqpuls_eline, + priv->io_base + _REG(ENCP_VIDEO_EQPULS_ELINE)); + writel_relaxed(vmode->encp.havon_begin, + priv->io_base + _REG(ENCP_VIDEO_HAVON_BEGIN)); + writel_relaxed(vmode->encp.havon_end, + priv->io_base + _REG(ENCP_VIDEO_HAVON_END)); + writel_relaxed(vmode->encp.vavon_bline, + priv->io_base + _REG(ENCP_VIDEO_VAVON_BLINE)); + writel_relaxed(vmode->encp.vavon_eline, + priv->io_base + _REG(ENCP_VIDEO_VAVON_ELINE)); + writel_relaxed(vmode->encp.hso_begin, + priv->io_base + _REG(ENCP_VIDEO_HSO_BEGIN)); + writel_relaxed(vmode->encp.hso_end, + priv->io_base + _REG(ENCP_VIDEO_HSO_END)); + writel_relaxed(vmode->encp.vso_begin, + priv->io_base + _REG(ENCP_VIDEO_VSO_BEGIN)); + writel_relaxed(vmode->encp.vso_end, + priv->io_base + _REG(ENCP_VIDEO_VSO_END)); + writel_relaxed(vmode->encp.vso_bline, + priv->io_base + _REG(ENCP_VIDEO_VSO_BLINE)); + if (vmode->encp.vso_eline_present) + writel_relaxed(vmode->encp.vso_eline, + priv->io_base + _REG(ENCP_VIDEO_VSO_ELINE)); + if (vmode->encp.sy_val_present) + writel_relaxed(vmode->encp.sy_val, + priv->io_base + _REG(ENCP_VIDEO_SY_VAL)); + if (vmode->encp.sy2_val_present) + writel_relaxed(vmode->encp.sy2_val, + priv->io_base + _REG(ENCP_VIDEO_SY2_VAL)); + writel_relaxed(vmode->encp.max_lncnt, + priv->io_base + _REG(ENCP_VIDEO_MAX_LNCNT)); + + writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN)); + + /* Set DE signal’s polarity is active high */ + writel_bits_relaxed(ENCP_VIDEO_MODE_DE_V_HIGH, + ENCP_VIDEO_MODE_DE_V_HIGH, + priv->io_base + _REG(ENCP_VIDEO_MODE)); + + /* Program DE timing */ + de_h_begin = modulo(readl_relaxed(priv->io_base + + _REG(ENCP_VIDEO_HAVON_BEGIN)) + + venc_hdmi_latency, + total_pixels_venc); + de_h_end = modulo(de_h_begin + active_pixels_venc, + total_pixels_venc); + + writel_relaxed(de_h_begin, + priv->io_base + _REG(ENCP_DE_H_BEGIN)); + writel_relaxed(de_h_end, + priv->io_base + _REG(ENCP_DE_H_END)); + + /* Program DE timing for even field */ + de_v_begin_even = readl_relaxed(priv->io_base + + _REG(ENCP_VIDEO_VAVON_BLINE)); + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + de_v_end_even = de_v_begin_even + + (mode->vdisplay / 2); + else + de_v_end_even = de_v_begin_even + mode->vdisplay; + + writel_relaxed(de_v_begin_even, + priv->io_base + _REG(ENCP_DE_V_BEGIN_EVEN)); + writel_relaxed(de_v_end_even, + priv->io_base + _REG(ENCP_DE_V_END_EVEN)); + + /* Program DE timing for odd field if needed */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + unsigned int ofld_voav_ofst = + readl_relaxed(priv->io_base + + _REG(ENCP_VIDEO_OFLD_VOAV_OFST)); + de_v_begin_odd = to_signed((ofld_voav_ofst & 0xf0) >> 4) + + de_v_begin_even + + ((mode->vtotal - 1) / 2); + de_v_end_odd = de_v_begin_odd + (mode->vdisplay / 2); + + writel_relaxed(de_v_begin_odd, + priv->io_base + _REG(ENCP_DE_V_BEGIN_ODD)); + writel_relaxed(de_v_end_odd, + priv->io_base + _REG(ENCP_DE_V_END_ODD)); + } + + /* Program Hsync timing */ + if ((de_h_end + front_porch_venc) >= total_pixels_venc) { + hs_begin = de_h_end + + front_porch_venc + - total_pixels_venc; + vs_adjust = 1; + } else { + hs_begin = de_h_end + + front_porch_venc; + vs_adjust = 0; + } + + hs_end = modulo(hs_begin + hsync_pixels_venc, + total_pixels_venc); + + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCP_DVI_HSO_BEGIN)); + writel_relaxed(hs_end, + priv->io_base + _REG(ENCP_DVI_HSO_END)); + + /* Program Vsync timing for even field */ + if (de_v_begin_even >= + (sof_lines + vsync_lines + (1 - vs_adjust))) + vs_bline_evn = de_v_begin_even + - sof_lines + - vsync_lines + - (1 - vs_adjust); + else + vs_bline_evn = mode->vtotal + + de_v_begin_even + - sof_lines + - vsync_lines + - (1 - vs_adjust); + + vs_eline_evn = modulo(vs_bline_evn + vsync_lines, + mode->vtotal); + + writel_relaxed(vs_bline_evn, + priv->io_base + _REG(ENCP_DVI_VSO_BLINE_EVN)); + writel_relaxed(vs_eline_evn, + priv->io_base + _REG(ENCP_DVI_VSO_ELINE_EVN)); + + vso_begin_evn = hs_begin; + writel_relaxed(vso_begin_evn, + priv->io_base + _REG(ENCP_DVI_VSO_BEGIN_EVN)); + writel_relaxed(vso_begin_evn, + priv->io_base + _REG(ENCP_DVI_VSO_END_EVN)); + + /* Program Vsync timing for odd field if needed */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + vs_bline_odd = (de_v_begin_odd - 1) + - sof_lines + - vsync_lines; + vs_eline_odd = (de_v_begin_odd - 1) + - vsync_lines; + vso_begin_odd = modulo(hs_begin + + (total_pixels_venc >> 1), + total_pixels_venc); + + writel_relaxed(vs_bline_odd, + priv->io_base + _REG(ENCP_DVI_VSO_BLINE_ODD)); + writel_relaxed(vs_eline_odd, + priv->io_base + _REG(ENCP_DVI_VSO_ELINE_ODD)); + writel_relaxed(vso_begin_odd, + priv->io_base + _REG(ENCP_DVI_VSO_BEGIN_ODD)); + writel_relaxed(vso_begin_odd, + priv->io_base + _REG(ENCP_DVI_VSO_END_ODD)); + } + + /* Select ENCP for VIU */ + meson_vpp_setup_mux(priv, MESON_VIU_VPP_MUX_ENCP); + } + + /* Set VPU HDMI setting */ + /* Select ENCP or ENCI data to HDMI */ + if (use_enci) + reg = VPU_HDMI_ENCI_DATA_TO_HDMI; + else + reg = VPU_HDMI_ENCP_DATA_TO_HDMI; + + /* Invert polarity of HSYNC from VENC */ + if (mode->flags & DRM_MODE_FLAG_PHSYNC) + reg |= VPU_HDMI_INV_HSYNC; + + /* Invert polarity of VSYNC from VENC */ + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + reg |= VPU_HDMI_INV_VSYNC; + + /* Output data format */ + reg |= ycrcb_map; + + /* + * Write rate to the async FIFO between VENC and HDMI. + * One write every 2 wr_clk. + */ + if (venc_repeat || yuv420_mode) + reg |= VPU_HDMI_WR_RATE(2); + + /* + * Read rate to the async FIFO between VENC and HDMI. + * One read every 2 wr_clk. + */ + if (hdmi_repeat) + reg |= VPU_HDMI_RD_RATE(2); + + writel_relaxed(reg, priv->io_base + _REG(VPU_HDMI_SETTING)); + + priv->venc.hdmi_repeat = hdmi_repeat; + priv->venc.venc_repeat = venc_repeat; + priv->venc.hdmi_use_enci = use_enci; + + priv->venc.current_mode = MESON_VENC_MODE_HDMI; +} +EXPORT_SYMBOL_GPL(meson_venc_hdmi_mode_set); + +static unsigned short meson_encl_gamma_table[256] = { + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, + 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, + 128, 132, 136, 140, 144, 148, 152, 156, 160, 164, 168, 172, 176, 180, 184, 188, + 192, 196, 200, 204, 208, 212, 216, 220, 224, 228, 232, 236, 240, 244, 248, 252, + 256, 260, 264, 268, 272, 276, 280, 284, 288, 292, 296, 300, 304, 308, 312, 316, + 320, 324, 328, 332, 336, 340, 344, 348, 352, 356, 360, 364, 368, 372, 376, 380, + 384, 388, 392, 396, 400, 404, 408, 412, 416, 420, 424, 428, 432, 436, 440, 444, + 448, 452, 456, 460, 464, 468, 472, 476, 480, 484, 488, 492, 496, 500, 504, 508, + 512, 516, 520, 524, 528, 532, 536, 540, 544, 548, 552, 556, 560, 564, 568, 572, + 576, 580, 584, 588, 592, 596, 600, 604, 608, 612, 616, 620, 624, 628, 632, 636, + 640, 644, 648, 652, 656, 660, 664, 668, 672, 676, 680, 684, 688, 692, 696, 700, + 704, 708, 712, 716, 720, 724, 728, 732, 736, 740, 744, 748, 752, 756, 760, 764, + 768, 772, 776, 780, 784, 788, 792, 796, 800, 804, 808, 812, 816, 820, 824, 828, + 832, 836, 840, 844, 848, 852, 856, 860, 864, 868, 872, 876, 880, 884, 888, 892, + 896, 900, 904, 908, 912, 916, 920, 924, 928, 932, 936, 940, 944, 948, 952, 956, + 960, 964, 968, 972, 976, 980, 984, 988, 992, 996, 1000, 1004, 1008, 1012, 1016, 1020, +}; + +static void meson_encl_set_gamma_table(struct meson_drm *priv, u16 *data, + u32 rgb_mask) +{ + int i, ret; + u32 reg; + + writel_bits_relaxed(L_GAMMA_CNTL_PORT_EN, 0, + priv->io_base + _REG(L_GAMMA_CNTL_PORT)); + + ret = readl_relaxed_poll_timeout(priv->io_base + _REG(L_GAMMA_CNTL_PORT), + reg, reg & L_GAMMA_CNTL_PORT_ADR_RDY, 10, 10000); + if (ret) + pr_warn("%s: GAMMA ADR_RDY timeout\n", __func__); + + writel_relaxed(L_GAMMA_ADDR_PORT_AUTO_INC | rgb_mask | + FIELD_PREP(L_GAMMA_ADDR_PORT_ADDR, 0), + priv->io_base + _REG(L_GAMMA_ADDR_PORT)); + + for (i = 0; i < 256; i++) { + ret = readl_relaxed_poll_timeout(priv->io_base + _REG(L_GAMMA_CNTL_PORT), + reg, reg & L_GAMMA_CNTL_PORT_WR_RDY, + 10, 10000); + if (ret) + pr_warn_once("%s: GAMMA WR_RDY timeout\n", __func__); + + writel_relaxed(data[i], priv->io_base + _REG(L_GAMMA_DATA_PORT)); + } + + ret = readl_relaxed_poll_timeout(priv->io_base + _REG(L_GAMMA_CNTL_PORT), + reg, reg & L_GAMMA_CNTL_PORT_ADR_RDY, 10, 10000); + if (ret) + pr_warn("%s: GAMMA ADR_RDY timeout\n", __func__); + + writel_relaxed(L_GAMMA_ADDR_PORT_AUTO_INC | rgb_mask | + FIELD_PREP(L_GAMMA_ADDR_PORT_ADDR, 0x23), + priv->io_base + _REG(L_GAMMA_ADDR_PORT)); +} + +void meson_encl_load_gamma(struct meson_drm *priv) +{ + meson_encl_set_gamma_table(priv, meson_encl_gamma_table, L_GAMMA_ADDR_PORT_SEL_R); + meson_encl_set_gamma_table(priv, meson_encl_gamma_table, L_GAMMA_ADDR_PORT_SEL_G); + meson_encl_set_gamma_table(priv, meson_encl_gamma_table, L_GAMMA_ADDR_PORT_SEL_B); + + writel_bits_relaxed(L_GAMMA_CNTL_PORT_EN, L_GAMMA_CNTL_PORT_EN, + priv->io_base + _REG(L_GAMMA_CNTL_PORT)); +} + +void meson_venc_mipi_dsi_mode_set(struct meson_drm *priv, + const struct drm_display_mode *mode) +{ + unsigned int max_pxcnt; + unsigned int max_lncnt; + unsigned int havon_begin; + unsigned int havon_end; + unsigned int vavon_bline; + unsigned int vavon_eline; + unsigned int hso_begin; + unsigned int hso_end; + unsigned int vso_begin; + unsigned int vso_end; + unsigned int vso_bline; + unsigned int vso_eline; + + max_pxcnt = mode->htotal - 1; + max_lncnt = mode->vtotal - 1; + havon_begin = mode->htotal - mode->hsync_start; + havon_end = havon_begin + mode->hdisplay - 1; + vavon_bline = mode->vtotal - mode->vsync_start; + vavon_eline = vavon_bline + mode->vdisplay - 1; + hso_begin = 0; + hso_end = mode->hsync_end - mode->hsync_start; + vso_begin = 0; + vso_end = 0; + vso_bline = 0; + vso_eline = mode->vsync_end - mode->vsync_start; + + meson_vpp_setup_mux(priv, MESON_VIU_VPP_MUX_ENCL); + + writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN)); + + writel_relaxed(ENCL_PX_LN_CNT_SHADOW_EN, priv->io_base + _REG(ENCL_VIDEO_MODE)); + writel_relaxed(ENCL_VIDEO_MODE_ADV_VFIFO_EN | + ENCL_VIDEO_MODE_ADV_GAIN_HDTV | + ENCL_SEL_GAMMA_RGB_IN, priv->io_base + _REG(ENCL_VIDEO_MODE_ADV)); + + writel_relaxed(ENCL_VIDEO_FILT_CTRL_BYPASS_FILTER, + priv->io_base + _REG(ENCL_VIDEO_FILT_CTRL)); + writel_relaxed(max_pxcnt, priv->io_base + _REG(ENCL_VIDEO_MAX_PXCNT)); + writel_relaxed(max_lncnt, priv->io_base + _REG(ENCL_VIDEO_MAX_LNCNT)); + writel_relaxed(havon_begin, priv->io_base + _REG(ENCL_VIDEO_HAVON_BEGIN)); + writel_relaxed(havon_end, priv->io_base + _REG(ENCL_VIDEO_HAVON_END)); + writel_relaxed(vavon_bline, priv->io_base + _REG(ENCL_VIDEO_VAVON_BLINE)); + writel_relaxed(vavon_eline, priv->io_base + _REG(ENCL_VIDEO_VAVON_ELINE)); + + writel_relaxed(hso_begin, priv->io_base + _REG(ENCL_VIDEO_HSO_BEGIN)); + writel_relaxed(hso_end, priv->io_base + _REG(ENCL_VIDEO_HSO_END)); + writel_relaxed(vso_begin, priv->io_base + _REG(ENCL_VIDEO_VSO_BEGIN)); + writel_relaxed(vso_end, priv->io_base + _REG(ENCL_VIDEO_VSO_END)); + writel_relaxed(vso_bline, priv->io_base + _REG(ENCL_VIDEO_VSO_BLINE)); + writel_relaxed(vso_eline, priv->io_base + _REG(ENCL_VIDEO_VSO_ELINE)); + writel_relaxed(ENCL_VIDEO_RGBIN_RGB | ENCL_VIDEO_RGBIN_ZBLK, + priv->io_base + _REG(ENCL_VIDEO_RGBIN_CTRL)); + + /* default black pattern */ + writel_relaxed(0, priv->io_base + _REG(ENCL_TST_MDSEL)); + writel_relaxed(0, priv->io_base + _REG(ENCL_TST_Y)); + writel_relaxed(0, priv->io_base + _REG(ENCL_TST_CB)); + writel_relaxed(0, priv->io_base + _REG(ENCL_TST_CR)); + writel_relaxed(1, priv->io_base + _REG(ENCL_TST_EN)); + writel_bits_relaxed(ENCL_VIDEO_MODE_ADV_VFIFO_EN, 0, + priv->io_base + _REG(ENCL_VIDEO_MODE_ADV)); + + writel_relaxed(1, priv->io_base + _REG(ENCL_VIDEO_EN)); + + writel_relaxed(0, priv->io_base + _REG(L_RGB_BASE_ADDR)); + writel_relaxed(0x400, priv->io_base + _REG(L_RGB_COEFF_ADDR)); /* Magic value */ + + writel_relaxed(L_DITH_CNTL_DITH10_EN, priv->io_base + _REG(L_DITH_CNTL_ADDR)); + + /* DE signal for TTL */ + writel_relaxed(havon_begin, priv->io_base + _REG(L_OEH_HS_ADDR)); + writel_relaxed(havon_end + 1, priv->io_base + _REG(L_OEH_HE_ADDR)); + writel_relaxed(vavon_bline, priv->io_base + _REG(L_OEH_VS_ADDR)); + writel_relaxed(vavon_eline, priv->io_base + _REG(L_OEH_VE_ADDR)); + + /* DE signal for TTL */ + writel_relaxed(havon_begin, priv->io_base + _REG(L_OEV1_HS_ADDR)); + writel_relaxed(havon_end + 1, priv->io_base + _REG(L_OEV1_HE_ADDR)); + writel_relaxed(vavon_bline, priv->io_base + _REG(L_OEV1_VS_ADDR)); + writel_relaxed(vavon_eline, priv->io_base + _REG(L_OEV1_VE_ADDR)); + + /* Hsync signal for TTL */ + if (mode->flags & DRM_MODE_FLAG_PHSYNC) { + writel_relaxed(hso_begin, priv->io_base + _REG(L_STH1_HS_ADDR)); + writel_relaxed(hso_end, priv->io_base + _REG(L_STH1_HE_ADDR)); + } else { + writel_relaxed(hso_end, priv->io_base + _REG(L_STH1_HS_ADDR)); + writel_relaxed(hso_begin, priv->io_base + _REG(L_STH1_HE_ADDR)); + } + writel_relaxed(0, priv->io_base + _REG(L_STH1_VS_ADDR)); + writel_relaxed(max_lncnt, priv->io_base + _REG(L_STH1_VE_ADDR)); + + /* Vsync signal for TTL */ + writel_relaxed(vso_begin, priv->io_base + _REG(L_STV1_HS_ADDR)); + writel_relaxed(vso_end, priv->io_base + _REG(L_STV1_HE_ADDR)); + if (mode->flags & DRM_MODE_FLAG_PVSYNC) { + writel_relaxed(vso_bline, priv->io_base + _REG(L_STV1_VS_ADDR)); + writel_relaxed(vso_eline, priv->io_base + _REG(L_STV1_VE_ADDR)); + } else { + writel_relaxed(vso_eline, priv->io_base + _REG(L_STV1_VS_ADDR)); + writel_relaxed(vso_bline, priv->io_base + _REG(L_STV1_VE_ADDR)); + } + + /* DE signal */ + writel_relaxed(havon_begin, priv->io_base + _REG(L_DE_HS_ADDR)); + writel_relaxed(havon_end + 1, priv->io_base + _REG(L_DE_HE_ADDR)); + writel_relaxed(vavon_bline, priv->io_base + _REG(L_DE_VS_ADDR)); + writel_relaxed(vavon_eline, priv->io_base + _REG(L_DE_VE_ADDR)); + + /* Hsync signal */ + writel_relaxed(hso_begin, priv->io_base + _REG(L_HSYNC_HS_ADDR)); + writel_relaxed(hso_end, priv->io_base + _REG(L_HSYNC_HE_ADDR)); + writel_relaxed(0, priv->io_base + _REG(L_HSYNC_VS_ADDR)); + writel_relaxed(max_lncnt, priv->io_base + _REG(L_HSYNC_VE_ADDR)); + + /* Vsync signal */ + writel_relaxed(vso_begin, priv->io_base + _REG(L_VSYNC_HS_ADDR)); + writel_relaxed(vso_end, priv->io_base + _REG(L_VSYNC_HE_ADDR)); + writel_relaxed(vso_bline, priv->io_base + _REG(L_VSYNC_VS_ADDR)); + writel_relaxed(vso_eline, priv->io_base + _REG(L_VSYNC_VE_ADDR)); + + writel_relaxed(0, priv->io_base + _REG(L_INV_CNT_ADDR)); + writel_relaxed(L_TCON_MISC_SEL_STV1 | L_TCON_MISC_SEL_STV2, + priv->io_base + _REG(L_TCON_MISC_SEL_ADDR)); + + priv->venc.current_mode = MESON_VENC_MODE_MIPI_DSI; +} +EXPORT_SYMBOL_GPL(meson_venc_mipi_dsi_mode_set); + +void meson_venci_cvbs_mode_set(struct meson_drm *priv, + struct meson_cvbs_enci_mode *mode) +{ + u32 reg; + + if (mode->mode_tag == priv->venc.current_mode) + return; + + /* CVBS Filter settings */ + writel_relaxed(ENCI_CFILT_CMPT_SEL_HIGH | 0x10, + priv->io_base + _REG(ENCI_CFILT_CTRL)); + writel_relaxed(ENCI_CFILT_CMPT_CR_DLY(2) | + ENCI_CFILT_CMPT_CB_DLY(1), + priv->io_base + _REG(ENCI_CFILT_CTRL2)); + + /* Digital Video Select : Interlace, clk27 clk, external */ + writel_relaxed(0, priv->io_base + _REG(VENC_DVI_SETTING)); + + /* Reset Video Mode */ + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE)); + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE_ADV)); + + /* Horizontal sync signal output */ + writel_relaxed(mode->hso_begin, + priv->io_base + _REG(ENCI_SYNC_HSO_BEGIN)); + writel_relaxed(mode->hso_end, + priv->io_base + _REG(ENCI_SYNC_HSO_END)); + + /* Vertical Sync lines */ + writel_relaxed(mode->vso_even, + priv->io_base + _REG(ENCI_SYNC_VSO_EVNLN)); + writel_relaxed(mode->vso_odd, + priv->io_base + _REG(ENCI_SYNC_VSO_ODDLN)); + + /* Macrovision max amplitude change */ + writel_relaxed(ENCI_MACV_MAX_AMP_ENABLE_CHANGE | + ENCI_MACV_MAX_AMP_VAL(mode->macv_max_amp), + priv->io_base + _REG(ENCI_MACV_MAX_AMP)); + + /* Video mode */ + writel_relaxed(mode->video_prog_mode, + priv->io_base + _REG(VENC_VIDEO_PROG_MODE)); + writel_relaxed(mode->video_mode, + priv->io_base + _REG(ENCI_VIDEO_MODE)); + + /* + * Advanced Video Mode : + * Demux shifting 0x2 + * Blank line end at line17/22 + * High bandwidth Luma Filter + * Low bandwidth Chroma Filter + * Bypass luma low pass filter + * No macrovision on CSYNC + */ + writel_relaxed(ENCI_VIDEO_MODE_ADV_DMXMD(2) | + ENCI_VIDEO_MODE_ADV_VBICTL_LINE_17_22 | + ENCI_VIDEO_MODE_ADV_YBW_HIGH, + priv->io_base + _REG(ENCI_VIDEO_MODE_ADV)); + + writel(mode->sch_adjust, priv->io_base + _REG(ENCI_VIDEO_SCH)); + + /* Sync mode : MASTER Master mode, free run, send HSO/VSO out */ + writel_relaxed(0x07, priv->io_base + _REG(ENCI_SYNC_MODE)); + + /* 0x3 Y, C, and Component Y delay */ + writel_relaxed(mode->yc_delay, priv->io_base + _REG(ENCI_YC_DELAY)); + + /* Timings */ + writel_relaxed(mode->pixel_start, + priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_START)); + writel_relaxed(mode->pixel_end, + priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_END)); + + writel_relaxed(mode->top_field_line_start, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_START)); + writel_relaxed(mode->top_field_line_end, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_END)); + + writel_relaxed(mode->bottom_field_line_start, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_START)); + writel_relaxed(mode->bottom_field_line_end, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_END)); + + /* Internal Venc, Internal VIU Sync, Internal Vencoder */ + writel_relaxed(0, priv->io_base + _REG(VENC_SYNC_ROUTE)); + + /* UNreset Interlaced TV Encoder */ + writel_relaxed(0, priv->io_base + _REG(ENCI_DBG_PX_RST)); + + /* + * Enable Vfifo2vd and set Y_Cb_Y_Cr: + * Corresponding value: + * Y => 00 or 10 + * Cb => 01 + * Cr => 11 + * Ex: 0x4e => 01001110 would mean Cb/Y/Cr/Y + */ + writel_relaxed(ENCI_VFIFO2VD_CTL_ENABLE | + ENCI_VFIFO2VD_CTL_VD_SEL(0x4e), + priv->io_base + _REG(ENCI_VFIFO2VD_CTL)); + + /* Power UP Dacs */ + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_SETTING)); + + /* Video Upsampling */ + /* + * CTRL0, CTRL1 and CTRL2: + * Filter0: input data sample every 2 cloks + * Filter1: filtering and upsample enable + */ + reg = VENC_UPSAMPLE_CTRL_F0_2_CLK_RATIO | VENC_UPSAMPLE_CTRL_F1_EN | + VENC_UPSAMPLE_CTRL_F1_UPSAMPLE_EN; + + /* + * Upsample CTRL0: + * Interlace High Bandwidth Luma + */ + writel_relaxed(VENC_UPSAMPLE_CTRL_INTERLACE_HIGH_LUMA | reg, + priv->io_base + _REG(VENC_UPSAMPLE_CTRL0)); + + /* + * Upsample CTRL1: + * Interlace Pb + */ + writel_relaxed(VENC_UPSAMPLE_CTRL_INTERLACE_PB | reg, + priv->io_base + _REG(VENC_UPSAMPLE_CTRL1)); + + /* + * Upsample CTRL2: + * Interlace R + */ + writel_relaxed(VENC_UPSAMPLE_CTRL_INTERLACE_PR | reg, + priv->io_base + _REG(VENC_UPSAMPLE_CTRL2)); + + /* Select Interlace Y DACs */ + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL0)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL1)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL2)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL3)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL4)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL5)); + + /* Select ENCI for VIU */ + meson_vpp_setup_mux(priv, MESON_VIU_VPP_MUX_ENCI); + + /* Enable ENCI FIFO */ + writel_relaxed(VENC_VDAC_FIFO_EN_ENCI_ENABLE, + priv->io_base + _REG(VENC_VDAC_FIFO_CTRL)); + + /* Select ENCI DACs 0, 1, 4, and 5 */ + writel_relaxed(0x11, priv->io_base + _REG(ENCI_DACSEL_0)); + writel_relaxed(0x11, priv->io_base + _REG(ENCI_DACSEL_1)); + + /* Interlace video enable */ + writel_relaxed(ENCI_VIDEO_EN_ENABLE, + priv->io_base + _REG(ENCI_VIDEO_EN)); + + /* Configure Video Saturation / Contrast / Brightness / Hue */ + writel_relaxed(mode->video_saturation, + priv->io_base + _REG(ENCI_VIDEO_SAT)); + writel_relaxed(mode->video_contrast, + priv->io_base + _REG(ENCI_VIDEO_CONT)); + writel_relaxed(mode->video_brightness, + priv->io_base + _REG(ENCI_VIDEO_BRIGHT)); + writel_relaxed(mode->video_hue, + priv->io_base + _REG(ENCI_VIDEO_HUE)); + + /* Enable DAC0 Filter */ + writel_relaxed(VENC_VDAC_DAC0_FILT_CTRL0_EN, + priv->io_base + _REG(VENC_VDAC_DAC0_FILT_CTRL0)); + writel_relaxed(0xfc48, priv->io_base + _REG(VENC_VDAC_DAC0_FILT_CTRL1)); + + /* 0 in Macrovision register 0 */ + writel_relaxed(0, priv->io_base + _REG(ENCI_MACV_N0)); + + /* Analog Synchronization and color burst value adjust */ + writel_relaxed(mode->analog_sync_adj, + priv->io_base + _REG(ENCI_SYNC_ADJ)); + + priv->venc.current_mode = mode->mode_tag; +} + +/* Returns the current ENCI field polarity */ +unsigned int meson_venci_get_field(struct meson_drm *priv) +{ + return readl_relaxed(priv->io_base + _REG(ENCI_INFO_READ)) & BIT(29); +} + +void meson_venc_enable_vsync(struct meson_drm *priv) +{ + switch (priv->venc.current_mode) { + case MESON_VENC_MODE_MIPI_DSI: + writel_relaxed(VENC_INTCTRL_ENCP_LNRST_INT_EN, + priv->io_base + _REG(VENC_INTCTRL)); + break; + default: + writel_relaxed(VENC_INTCTRL_ENCI_LNRST_INT_EN, + priv->io_base + _REG(VENC_INTCTRL)); + } + regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), BIT(25)); +} + +void meson_venc_disable_vsync(struct meson_drm *priv) +{ + regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), 0); + writel_relaxed(0, priv->io_base + _REG(VENC_INTCTRL)); +} + +void meson_venc_init(struct meson_drm *priv) +{ + /* Disable CVBS VDAC */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0); + regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 8); + } else { + regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0); + regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8); + } + + /* Power Down Dacs */ + writel_relaxed(0xff, priv->io_base + _REG(VENC_VDAC_SETTING)); + + /* Disable HDMI PHY */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0); + + /* Disable HDMI */ + writel_bits_relaxed(VPU_HDMI_ENCI_DATA_TO_HDMI | + VPU_HDMI_ENCP_DATA_TO_HDMI, 0, + priv->io_base + _REG(VPU_HDMI_SETTING)); + + /* Disable all encoders */ + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN)); + writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN)); + writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN)); + + /* Disable VSync IRQ */ + meson_venc_disable_vsync(priv); + + priv->venc.current_mode = MESON_VENC_MODE_NONE; +} diff --git a/drivers/gpu/drm/meson/meson_venc.h b/drivers/gpu/drm/meson/meson_venc.h new file mode 100644 index 0000000000..0f59adb1c6 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_venc.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +/* + * Video Encoders + * - ENCI : Interlace Video Encoder + * - ENCI_DVI : Interlace Video Encoder for DVI/HDMI + * - ENCP : Progressive Video Encoder + */ + +#ifndef __MESON_VENC_H +#define __MESON_VENC_H + +struct drm_display_mode; + +enum { + MESON_VENC_MODE_NONE = 0, + MESON_VENC_MODE_CVBS_PAL, + MESON_VENC_MODE_CVBS_NTSC, + MESON_VENC_MODE_HDMI, + MESON_VENC_MODE_MIPI_DSI, +}; + +struct meson_cvbs_enci_mode { + unsigned int mode_tag; + unsigned int hso_begin; /* HSO begin position */ + unsigned int hso_end; /* HSO end position */ + unsigned int vso_even; /* VSO even line */ + unsigned int vso_odd; /* VSO odd line */ + unsigned int macv_max_amp; /* Macrovision max amplitude */ + unsigned int video_prog_mode; + unsigned int video_mode; + unsigned int sch_adjust; + unsigned int yc_delay; + unsigned int pixel_start; + unsigned int pixel_end; + unsigned int top_field_line_start; + unsigned int top_field_line_end; + unsigned int bottom_field_line_start; + unsigned int bottom_field_line_end; + unsigned int video_saturation; + unsigned int video_contrast; + unsigned int video_brightness; + unsigned int video_hue; + unsigned int analog_sync_adj; +}; + +/* LCD Encoder gamma setup */ +void meson_encl_load_gamma(struct meson_drm *priv); + +/* HDMI Clock parameters */ +enum drm_mode_status +meson_venc_hdmi_supported_mode(const struct drm_display_mode *mode); +bool meson_venc_hdmi_supported_vic(int vic); +bool meson_venc_hdmi_venc_repeat(int vic); + +/* CVBS Timings and Parameters */ +extern struct meson_cvbs_enci_mode meson_cvbs_enci_pal; +extern struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc; + +void meson_venci_cvbs_mode_set(struct meson_drm *priv, + struct meson_cvbs_enci_mode *mode); +void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic, + unsigned int ycrcb_map, + bool yuv420_mode, + const struct drm_display_mode *mode); +void meson_venc_mipi_dsi_mode_set(struct meson_drm *priv, + const struct drm_display_mode *mode); +unsigned int meson_venci_get_field(struct meson_drm *priv); + +void meson_venc_enable_vsync(struct meson_drm *priv); +void meson_venc_disable_vsync(struct meson_drm *priv); + +void meson_venc_init(struct meson_drm *priv); + +#endif /* __MESON_VENC_H */ diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c new file mode 100644 index 0000000000..cd399b0b71 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_viu.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + */ + +#include <linux/export.h> +#include <linux/bitfield.h> + +#include <drm/drm_fourcc.h> + +#include "meson_drv.h" +#include "meson_viu.h" +#include "meson_registers.h" + +/** + * DOC: Video Input Unit + * + * VIU Handles the Pixel scanout and the basic Colorspace conversions + * We handle the following features : + * + * - OSD1 RGB565/RGB888/xRGB8888 scanout + * - RGB conversion to x/cb/cr + * - Progressive or Interlace buffer scanout + * - OSD1 Commit on Vsync + * - HDR OSD matrix for GXL/GXM + * + * What is missing : + * + * - BGR888/xBGR8888/BGRx8888/BGRx8888 modes + * - YUV4:2:2 Y0CbY1Cr scanout + * - Conversion to YUV 4:4:4 from 4:2:2 input + * - Colorkey Alpha matching + * - Big endian scanout + * - X/Y reverse scanout + * - Global alpha setup + * - OSD2 support, would need interlace switching on vsync + * - OSD1 full scaling to support TV overscan + */ + +/* OSD csc defines */ + +enum viu_matrix_sel_e { + VIU_MATRIX_OSD_EOTF = 0, + VIU_MATRIX_OSD, +}; + +enum viu_lut_sel_e { + VIU_LUT_OSD_EOTF = 0, + VIU_LUT_OSD_OETF, +}; + +#define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2)) +#define MATRIX_5X3_COEF_SIZE 24 + +#define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2)) +#define EOTF_COEFF_SIZE 10 +#define EOTF_COEFF_RIGHTSHIFT 1 + +static int RGB709_to_YUV709l_coeff[MATRIX_5X3_COEF_SIZE] = { + 0, 0, 0, /* pre offset */ + COEFF_NORM(0.181873), COEFF_NORM(0.611831), COEFF_NORM(0.061765), + COEFF_NORM(-0.100251), COEFF_NORM(-0.337249), COEFF_NORM(0.437500), + COEFF_NORM(0.437500), COEFF_NORM(-0.397384), COEFF_NORM(-0.040116), + 0, 0, 0, /* 10'/11'/12' */ + 0, 0, 0, /* 20'/21'/22' */ + 64, 512, 512, /* offset */ + 0, 0, 0 /* mode, right_shift, clip_en */ +}; + +/* eotf matrix: bypass */ +static int eotf_bypass_coeff[EOTF_COEFF_SIZE] = { + EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), + EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), + EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), + EOTF_COEFF_RIGHTSHIFT /* right shift */ +}; + +static void meson_viu_set_g12a_osd1_matrix(struct meson_drm *priv, + int *m, bool csc_on) +{ + /* VPP WRAP OSD1 matrix */ + writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff), + priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET0_1)); + writel(m[2] & 0xfff, + priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET2)); + writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff), + priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF00_01)); + writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff), + priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF02_10)); + writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff), + priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF11_12)); + writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff), + priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF20_21)); + writel((m[11] & 0x1fff), + priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF22)); + + writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff), + priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET0_1)); + writel(m[20] & 0xfff, + priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET2)); + + writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0, + priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL)); +} + +static void meson_viu_set_osd_matrix(struct meson_drm *priv, + enum viu_matrix_sel_e m_select, + int *m, bool csc_on) +{ + if (m_select == VIU_MATRIX_OSD) { + /* osd matrix, VIU_MATRIX_0 */ + writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff), + priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET0_1)); + writel(m[2] & 0xfff, + priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET2)); + writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff), + priv->io_base + _REG(VIU_OSD1_MATRIX_COEF00_01)); + writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff), + priv->io_base + _REG(VIU_OSD1_MATRIX_COEF02_10)); + writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff), + priv->io_base + _REG(VIU_OSD1_MATRIX_COEF11_12)); + writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff), + priv->io_base + _REG(VIU_OSD1_MATRIX_COEF20_21)); + + if (m[21]) { + writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff), + priv->io_base + + _REG(VIU_OSD1_MATRIX_COEF22_30)); + writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff), + priv->io_base + + _REG(VIU_OSD1_MATRIX_COEF31_32)); + writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff), + priv->io_base + + _REG(VIU_OSD1_MATRIX_COEF40_41)); + writel(m[17] & 0x1fff, priv->io_base + + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); + } else + writel((m[11] & 0x1fff) << 16, priv->io_base + + _REG(VIU_OSD1_MATRIX_COEF22_30)); + + writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff), + priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET0_1)); + writel(m[20] & 0xfff, + priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET2)); + + writel_bits_relaxed(3 << 30, m[21] << 30, + priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); + writel_bits_relaxed(7 << 16, m[22] << 16, + priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); + + /* 23 reserved for clipping control */ + writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0, + priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL)); + writel_bits_relaxed(BIT(1), 0, + priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL)); + } else if (m_select == VIU_MATRIX_OSD_EOTF) { + int i; + + /* osd eotf matrix, VIU_MATRIX_OSD_EOTF */ + for (i = 0; i < 5; i++) + writel(((m[i * 2] & 0x1fff) << 16) | + (m[i * 2 + 1] & 0x1fff), priv->io_base + + _REG(VIU_OSD1_EOTF_CTL + i + 1)); + + writel_bits_relaxed(BIT(30), csc_on ? BIT(30) : 0, + priv->io_base + _REG(VIU_OSD1_EOTF_CTL)); + writel_bits_relaxed(BIT(31), csc_on ? BIT(31) : 0, + priv->io_base + _REG(VIU_OSD1_EOTF_CTL)); + } +} + +#define OSD_EOTF_LUT_SIZE 33 +#define OSD_OETF_LUT_SIZE 41 + +static void +meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel, + unsigned int *r_map, unsigned int *g_map, + unsigned int *b_map, bool csc_on) +{ + unsigned int addr_port; + unsigned int data_port; + unsigned int ctrl_port; + int i; + + if (lut_sel == VIU_LUT_OSD_EOTF) { + addr_port = VIU_OSD1_EOTF_LUT_ADDR_PORT; + data_port = VIU_OSD1_EOTF_LUT_DATA_PORT; + ctrl_port = VIU_OSD1_EOTF_CTL; + } else if (lut_sel == VIU_LUT_OSD_OETF) { + addr_port = VIU_OSD1_OETF_LUT_ADDR_PORT; + data_port = VIU_OSD1_OETF_LUT_DATA_PORT; + ctrl_port = VIU_OSD1_OETF_CTL; + } else + return; + + if (lut_sel == VIU_LUT_OSD_OETF) { + writel(0, priv->io_base + _REG(addr_port)); + + for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++) + writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16), + priv->io_base + _REG(data_port)); + + writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16), + priv->io_base + _REG(data_port)); + + for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++) + writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16), + priv->io_base + _REG(data_port)); + + for (i = 0; i < (OSD_OETF_LUT_SIZE / 2); i++) + writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16), + priv->io_base + _REG(data_port)); + + writel(b_map[OSD_OETF_LUT_SIZE - 1], + priv->io_base + _REG(data_port)); + + if (csc_on) + writel_bits_relaxed(0x7 << 29, 7 << 29, + priv->io_base + _REG(ctrl_port)); + else + writel_bits_relaxed(0x7 << 29, 0, + priv->io_base + _REG(ctrl_port)); + } else if (lut_sel == VIU_LUT_OSD_EOTF) { + writel(0, priv->io_base + _REG(addr_port)); + + for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++) + writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16), + priv->io_base + _REG(data_port)); + + writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16), + priv->io_base + _REG(data_port)); + + for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++) + writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16), + priv->io_base + _REG(data_port)); + + for (i = 0; i < (OSD_EOTF_LUT_SIZE / 2); i++) + writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16), + priv->io_base + _REG(data_port)); + + writel(b_map[OSD_EOTF_LUT_SIZE - 1], + priv->io_base + _REG(data_port)); + + if (csc_on) + writel_bits_relaxed(7 << 27, 7 << 27, + priv->io_base + _REG(ctrl_port)); + else + writel_bits_relaxed(7 << 27, 0, + priv->io_base + _REG(ctrl_port)); + + writel_bits_relaxed(BIT(31), BIT(31), + priv->io_base + _REG(ctrl_port)); + } +} + +/* eotf lut: linear */ +static unsigned int eotf_33_linear_mapping[OSD_EOTF_LUT_SIZE] = { + 0x0000, 0x0200, 0x0400, 0x0600, + 0x0800, 0x0a00, 0x0c00, 0x0e00, + 0x1000, 0x1200, 0x1400, 0x1600, + 0x1800, 0x1a00, 0x1c00, 0x1e00, + 0x2000, 0x2200, 0x2400, 0x2600, + 0x2800, 0x2a00, 0x2c00, 0x2e00, + 0x3000, 0x3200, 0x3400, 0x3600, + 0x3800, 0x3a00, 0x3c00, 0x3e00, + 0x4000 +}; + +/* osd oetf lut: linear */ +static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = { + 0, 0, 0, 0, + 0, 32, 64, 96, + 128, 160, 196, 224, + 256, 288, 320, 352, + 384, 416, 448, 480, + 512, 544, 576, 608, + 640, 672, 704, 736, + 768, 800, 832, 864, + 896, 928, 960, 992, + 1023, 1023, 1023, 1023, + 1023 +}; + +static void meson_viu_load_matrix(struct meson_drm *priv) +{ + /* eotf lut bypass */ + meson_viu_set_osd_lut(priv, VIU_LUT_OSD_EOTF, + eotf_33_linear_mapping, /* R */ + eotf_33_linear_mapping, /* G */ + eotf_33_linear_mapping, /* B */ + false); + + /* eotf matrix bypass */ + meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF, + eotf_bypass_coeff, + false); + + /* oetf lut bypass */ + meson_viu_set_osd_lut(priv, VIU_LUT_OSD_OETF, + oetf_41_linear_mapping, /* R */ + oetf_41_linear_mapping, /* G */ + oetf_41_linear_mapping, /* B */ + false); + + /* osd matrix RGB709 to YUV709 limit */ + meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD, + RGB709_to_YUV709l_coeff, + true); +} + +/* VIU OSD1 Reset as workaround for GXL+ Alpha OSD Bug */ +void meson_viu_osd1_reset(struct meson_drm *priv) +{ + uint32_t osd1_fifo_ctrl_stat, osd1_ctrl_stat2; + + /* Save these 2 registers state */ + osd1_fifo_ctrl_stat = readl_relaxed( + priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); + osd1_ctrl_stat2 = readl_relaxed( + priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); + + /* Reset OSD1 */ + writel_bits_relaxed(VIU_SW_RESET_OSD1, VIU_SW_RESET_OSD1, + priv->io_base + _REG(VIU_SW_RESET)); + writel_bits_relaxed(VIU_SW_RESET_OSD1, 0, + priv->io_base + _REG(VIU_SW_RESET)); + + /* Rewrite these registers state lost in the reset */ + writel_relaxed(osd1_fifo_ctrl_stat, + priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); + writel_relaxed(osd1_ctrl_stat2, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); + + /* Reload the conversion matrix */ + meson_viu_load_matrix(priv); +} + +#define OSD1_MALI_ORDER_ABGR \ + (FIELD_PREP(VIU_OSD1_MALI_AFBCD_A_REORDER, \ + VIU_OSD1_MALI_REORDER_A) | \ + FIELD_PREP(VIU_OSD1_MALI_AFBCD_B_REORDER, \ + VIU_OSD1_MALI_REORDER_B) | \ + FIELD_PREP(VIU_OSD1_MALI_AFBCD_G_REORDER, \ + VIU_OSD1_MALI_REORDER_G) | \ + FIELD_PREP(VIU_OSD1_MALI_AFBCD_R_REORDER, \ + VIU_OSD1_MALI_REORDER_R)) + +#define OSD1_MALI_ORDER_ARGB \ + (FIELD_PREP(VIU_OSD1_MALI_AFBCD_A_REORDER, \ + VIU_OSD1_MALI_REORDER_A) | \ + FIELD_PREP(VIU_OSD1_MALI_AFBCD_B_REORDER, \ + VIU_OSD1_MALI_REORDER_R) | \ + FIELD_PREP(VIU_OSD1_MALI_AFBCD_G_REORDER, \ + VIU_OSD1_MALI_REORDER_G) | \ + FIELD_PREP(VIU_OSD1_MALI_AFBCD_R_REORDER, \ + VIU_OSD1_MALI_REORDER_B)) + +void meson_viu_g12a_enable_osd1_afbc(struct meson_drm *priv) +{ + u32 afbc_order = OSD1_MALI_ORDER_ARGB; + + /* Enable Mali AFBC Unpack */ + writel_bits_relaxed(VIU_OSD1_MALI_UNPACK_EN, + VIU_OSD1_MALI_UNPACK_EN, + priv->io_base + _REG(VIU_OSD1_MALI_UNPACK_CTRL)); + + switch (priv->afbcd.format) { + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + afbc_order = OSD1_MALI_ORDER_ABGR; + break; + } + + /* Setup RGBA Reordering */ + writel_bits_relaxed(VIU_OSD1_MALI_AFBCD_A_REORDER | + VIU_OSD1_MALI_AFBCD_B_REORDER | + VIU_OSD1_MALI_AFBCD_G_REORDER | + VIU_OSD1_MALI_AFBCD_R_REORDER, + afbc_order, + priv->io_base + _REG(VIU_OSD1_MALI_UNPACK_CTRL)); + + /* Select AFBCD path for OSD1 */ + writel_bits_relaxed(OSD_PATH_OSD_AXI_SEL_OSD1_AFBCD, + OSD_PATH_OSD_AXI_SEL_OSD1_AFBCD, + priv->io_base + _REG(OSD_PATH_MISC_CTRL)); +} + +void meson_viu_g12a_disable_osd1_afbc(struct meson_drm *priv) +{ + /* Disable AFBCD path for OSD1 */ + writel_bits_relaxed(OSD_PATH_OSD_AXI_SEL_OSD1_AFBCD, 0, + priv->io_base + _REG(OSD_PATH_MISC_CTRL)); + + /* Disable AFBCD unpack */ + writel_bits_relaxed(VIU_OSD1_MALI_UNPACK_EN, 0, + priv->io_base + _REG(VIU_OSD1_MALI_UNPACK_CTRL)); +} + +void meson_viu_gxm_enable_osd1_afbc(struct meson_drm *priv) +{ + writel_bits_relaxed(MALI_AFBC_MISC, FIELD_PREP(MALI_AFBC_MISC, 0x90), + priv->io_base + _REG(VIU_MISC_CTRL1)); +} + +void meson_viu_gxm_disable_osd1_afbc(struct meson_drm *priv) +{ + writel_bits_relaxed(MALI_AFBC_MISC, FIELD_PREP(MALI_AFBC_MISC, 0x00), + priv->io_base + _REG(VIU_MISC_CTRL1)); +} + +void meson_viu_init(struct meson_drm *priv) +{ + uint32_t reg; + + /* Disable OSDs */ + writel_bits_relaxed(VIU_OSD1_OSD_BLK_ENABLE | VIU_OSD1_OSD_ENABLE, 0, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT)); + writel_bits_relaxed(VIU_OSD1_OSD_BLK_ENABLE | VIU_OSD1_OSD_ENABLE, 0, + priv->io_base + _REG(VIU_OSD2_CTRL_STAT)); + + /* On GXL/GXM, Use the 10bit HDR conversion matrix */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || + meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) + meson_viu_load_matrix(priv); + else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + meson_viu_set_g12a_osd1_matrix(priv, RGB709_to_YUV709l_coeff, + true); + /* fix green/pink color distortion from vendor u-boot */ + writel_bits_relaxed(OSD1_HDR2_CTRL_REG_ONLY_MAT | + OSD1_HDR2_CTRL_VDIN0_HDR2_TOP_EN, 0, + priv->io_base + _REG(OSD1_HDR2_CTRL)); + } + + /* Initialize OSD1 fifo control register */ + reg = VIU_OSD_DDR_PRIORITY_URGENT | + VIU_OSD_FIFO_DEPTH_VAL(32) | /* fifo_depth_val: 32*8=256 */ + VIU_OSD_WORDS_PER_BURST(4) | /* 4 words in 1 burst */ + VIU_OSD_FIFO_LIMITS(2); /* fifo_lim: 2*16=32 */ + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + reg |= (VIU_OSD_BURST_LENGTH_32 | VIU_OSD_HOLD_FIFO_LINES(31)); + else + reg |= (VIU_OSD_BURST_LENGTH_64 | VIU_OSD_HOLD_FIFO_LINES(4)); + + writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); + writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT)); + + /* Set OSD alpha replace value */ + writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT, + 0xff << OSD_REPLACE_SHIFT, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); + writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT, + 0xff << OSD_REPLACE_SHIFT, + priv->io_base + _REG(VIU_OSD2_CTRL_STAT2)); + + /* Disable VD1 AFBC */ + /* di_mif0_en=0 mif0_to_vpp_en=0 di_mad_en=0 and afbc vd1 set=0*/ + writel_bits_relaxed(VIU_CTRL0_VD1_AFBC_MASK, 0, + priv->io_base + _REG(VIU_MISC_CTRL0)); + writel_relaxed(0, priv->io_base + _REG(AFBC_ENABLE)); + + writel_relaxed(0x00FF00C0, + priv->io_base + _REG(VD1_IF0_LUMA_FIFO_SIZE)); + writel_relaxed(0x00FF00C0, + priv->io_base + _REG(VD2_IF0_LUMA_FIFO_SIZE)); + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + u32 val = (u32)VIU_OSD_BLEND_REORDER(0, 1) | + (u32)VIU_OSD_BLEND_REORDER(1, 0) | + (u32)VIU_OSD_BLEND_REORDER(2, 0) | + (u32)VIU_OSD_BLEND_REORDER(3, 0) | + (u32)VIU_OSD_BLEND_DIN_EN(1) | + (u32)VIU_OSD_BLEND1_DIN3_BYPASS_TO_DOUT1 | + (u32)VIU_OSD_BLEND1_DOUT_BYPASS_TO_BLEND2 | + (u32)VIU_OSD_BLEND_DIN0_BYPASS_TO_DOUT0 | + (u32)VIU_OSD_BLEND_BLEN2_PREMULT_EN(1) | + (u32)VIU_OSD_BLEND_HOLD_LINES(4); + writel_relaxed(val, priv->io_base + _REG(VIU_OSD_BLEND_CTRL)); + + writel_relaxed(OSD_BLEND_PATH_SEL_ENABLE, + priv->io_base + _REG(OSD1_BLEND_SRC_CTRL)); + writel_relaxed(OSD_BLEND_PATH_SEL_ENABLE, + priv->io_base + _REG(OSD2_BLEND_SRC_CTRL)); + writel_relaxed(0, priv->io_base + _REG(VD1_BLEND_SRC_CTRL)); + writel_relaxed(0, priv->io_base + _REG(VD2_BLEND_SRC_CTRL)); + writel_relaxed(0, + priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_DATA0)); + writel_relaxed(0, + priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_ALPHA)); + + writel_bits_relaxed(DOLBY_BYPASS_EN(0xc), DOLBY_BYPASS_EN(0xc), + priv->io_base + _REG(DOLBY_PATH_CTRL)); + + meson_viu_g12a_disable_osd1_afbc(priv); + } + + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM)) + meson_viu_gxm_disable_osd1_afbc(priv); + + priv->viu.osd1_enabled = false; + priv->viu.osd1_commit = false; + priv->viu.osd1_interlace = false; +} diff --git a/drivers/gpu/drm/meson/meson_viu.h b/drivers/gpu/drm/meson/meson_viu.h new file mode 100644 index 0000000000..e4a2f24d7c --- /dev/null +++ b/drivers/gpu/drm/meson/meson_viu.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +/* Video Input Unit */ + +#ifndef __MESON_VIU_H +#define __MESON_VIU_H + +/* OSDx_BLKx_CFG */ +#define OSD_MALI_SRC_EN BIT(30) + +#define OSD_CANVAS_SEL 16 + +#define OSD_ENDIANNESS_LE BIT(15) +#define OSD_ENDIANNESS_BE (0) + +#define OSD_BLK_MODE_422 (0x03 << 8) +#define OSD_BLK_MODE_16 (0x04 << 8) +#define OSD_BLK_MODE_32 (0x05 << 8) +#define OSD_BLK_MODE_24 (0x07 << 8) + +#define OSD_OUTPUT_COLOR_RGB BIT(7) +#define OSD_OUTPUT_COLOR_YUV (0) + +#define OSD_COLOR_MATRIX_32_RGBA (0x00 << 2) +#define OSD_COLOR_MATRIX_32_ARGB (0x01 << 2) +#define OSD_COLOR_MATRIX_32_ABGR (0x02 << 2) +#define OSD_COLOR_MATRIX_32_BGRA (0x03 << 2) + +#define OSD_COLOR_MATRIX_24_RGB (0x00 << 2) + +#define OSD_COLOR_MATRIX_16_RGB655 (0x00 << 2) +#define OSD_COLOR_MATRIX_16_RGB565 (0x04 << 2) + +#define OSD_MALI_COLOR_MODE_R8 (0 << 8) +#define OSD_MALI_COLOR_MODE_YUV422 (1 << 8) +#define OSD_MALI_COLOR_MODE_RGB565 (2 << 8) +#define OSD_MALI_COLOR_MODE_RGBA5551 (3 << 8) +#define OSD_MALI_COLOR_MODE_RGBA4444 (4 << 8) +#define OSD_MALI_COLOR_MODE_RGBA8888 (5 << 8) +#define OSD_MALI_COLOR_MODE_RGB888 (7 << 8) +#define OSD_MALI_COLOR_MODE_YUV422_10B (8 << 8) +#define OSD_MALI_COLOR_MODE_RGBA1010102 (9 << 8) + +#define OSD_INTERLACE_ENABLED BIT(1) +#define OSD_INTERLACE_ODD BIT(0) +#define OSD_INTERLACE_EVEN (0) + +/* OSDx_CTRL_STAT */ +#define OSD_ENABLE BIT(21) +#define OSD_MEM_LINEAR_ADDR BIT(2) +#define OSD_BLK0_ENABLE BIT(0) + +#define OSD_GLOBAL_ALPHA_SHIFT 12 + +/* OSDx_CTRL_STAT2 */ +#define OSD_DPATH_MALI_AFBCD BIT(15) +#define OSD_REPLACE_EN BIT(14) +#define OSD_REPLACE_SHIFT 6 +#define OSD_PENDING_STAT_CLEAN BIT(1) + +void meson_viu_osd1_reset(struct meson_drm *priv); +void meson_viu_g12a_enable_osd1_afbc(struct meson_drm *priv); +void meson_viu_g12a_disable_osd1_afbc(struct meson_drm *priv); +void meson_viu_gxm_enable_osd1_afbc(struct meson_drm *priv); +void meson_viu_gxm_disable_osd1_afbc(struct meson_drm *priv); +void meson_viu_init(struct meson_drm *priv); + +#endif /* __MESON_VIU_H */ diff --git a/drivers/gpu/drm/meson/meson_vpp.c b/drivers/gpu/drm/meson/meson_vpp.c new file mode 100644 index 0000000000..5df1957c8e --- /dev/null +++ b/drivers/gpu/drm/meson/meson_vpp.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + */ + +#include <linux/export.h> + +#include "meson_drv.h" +#include "meson_registers.h" +#include "meson_vpp.h" + +/** + * DOC: Video Post Processing + * + * VPP Handles all the Post Processing after the Scanout from the VIU + * We handle the following post processings : + * + * - Postblend, Blends the OSD1 only + * We exclude OSD2, VS1, VS1 and Preblend output + * - Vertical OSD Scaler for OSD1 only, we disable vertical scaler and + * use it only for interlace scanout + * - Intermediate FIFO with default Amlogic values + * + * What is missing : + * + * - Preblend for video overlay pre-scaling + * - OSD2 support for cursor framebuffer + * - Video pre-scaling before postblend + * - Full Vertical/Horizontal OSD scaling to support TV overscan + * - HDR conversion + */ + +void meson_vpp_setup_mux(struct meson_drm *priv, unsigned int mux) +{ + writel(mux, priv->io_base + _REG(VPU_VIU_VENC_MUX_CTRL)); +} + +static unsigned int vpp_filter_coefs_4point_bspline[] = { + 0x15561500, 0x14561600, 0x13561700, 0x12561800, + 0x11551a00, 0x11541b00, 0x10541c00, 0x0f541d00, + 0x0f531e00, 0x0e531f00, 0x0d522100, 0x0c522200, + 0x0b522300, 0x0b512400, 0x0a502600, 0x0a4f2700, + 0x094e2900, 0x084e2a00, 0x084d2b00, 0x074c2c01, + 0x074b2d01, 0x064a2f01, 0x06493001, 0x05483201, + 0x05473301, 0x05463401, 0x04453601, 0x04433702, + 0x04423802, 0x03413a02, 0x03403b02, 0x033f3c02, + 0x033d3d03 +}; + +static void meson_vpp_write_scaling_filter_coefs(struct meson_drm *priv, + const unsigned int *coefs, + bool is_horizontal) +{ + int i; + + writel_relaxed(is_horizontal ? VPP_SCALE_HORIZONTAL_COEF : 0, + priv->io_base + _REG(VPP_OSD_SCALE_COEF_IDX)); + for (i = 0; i < 33; i++) + writel_relaxed(coefs[i], + priv->io_base + _REG(VPP_OSD_SCALE_COEF)); +} + +static const uint32_t vpp_filter_coefs_bicubic[] = { + 0x00800000, 0x007f0100, 0xff7f0200, 0xfe7f0300, + 0xfd7e0500, 0xfc7e0600, 0xfb7d0800, 0xfb7c0900, + 0xfa7b0b00, 0xfa7a0dff, 0xf9790fff, 0xf97711ff, + 0xf87613ff, 0xf87416fe, 0xf87218fe, 0xf8701afe, + 0xf76f1dfd, 0xf76d1ffd, 0xf76b21fd, 0xf76824fd, + 0xf76627fc, 0xf76429fc, 0xf7612cfc, 0xf75f2ffb, + 0xf75d31fb, 0xf75a34fb, 0xf75837fa, 0xf7553afa, + 0xf8523cfa, 0xf8503ff9, 0xf84d42f9, 0xf84a45f9, + 0xf84848f8 +}; + +static void meson_vpp_write_vd_scaling_filter_coefs(struct meson_drm *priv, + const unsigned int *coefs, + bool is_horizontal) +{ + int i; + + writel_relaxed(is_horizontal ? VPP_SCALE_HORIZONTAL_COEF : 0, + priv->io_base + _REG(VPP_SCALE_COEF_IDX)); + for (i = 0; i < 33; i++) + writel_relaxed(coefs[i], + priv->io_base + _REG(VPP_SCALE_COEF)); +} + +void meson_vpp_init(struct meson_drm *priv) +{ + /* set dummy data default YUV black */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) + writel_relaxed(0x108080, priv->io_base + _REG(VPP_DUMMY_DATA1)); + else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM)) { + writel_bits_relaxed(0xff << 16, 0xff << 16, + priv->io_base + _REG(VIU_MISC_CTRL1)); + writel_relaxed(VPP_PPS_DUMMY_DATA_MODE, + priv->io_base + _REG(VPP_DOLBY_CTRL)); + writel_relaxed(0x1020080, + priv->io_base + _REG(VPP_DUMMY_DATA1)); + writel_relaxed(0x42020, + priv->io_base + _REG(VPP_DUMMY_DATA)); + } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + writel_relaxed(0xf, priv->io_base + _REG(DOLBY_PATH_CTRL)); + + /* Initialize vpu fifo control registers */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) + writel_relaxed(VPP_OFIFO_SIZE_DEFAULT, + priv->io_base + _REG(VPP_OFIFO_SIZE)); + else + writel_bits_relaxed(VPP_OFIFO_SIZE_MASK, 0x77f, + priv->io_base + _REG(VPP_OFIFO_SIZE)); + writel_relaxed(VPP_POSTBLEND_HOLD_LINES(4) | VPP_PREBLEND_HOLD_LINES(4), + priv->io_base + _REG(VPP_HOLD_LINES)); + + if (!meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + /* Turn off preblend */ + writel_bits_relaxed(VPP_PREBLEND_ENABLE, 0, + priv->io_base + _REG(VPP_MISC)); + + /* Turn off POSTBLEND */ + writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0, + priv->io_base + _REG(VPP_MISC)); + + /* Force all planes off */ + writel_bits_relaxed(VPP_OSD1_POSTBLEND | VPP_OSD2_POSTBLEND | + VPP_VD1_POSTBLEND | VPP_VD2_POSTBLEND | + VPP_VD1_PREBLEND | VPP_VD2_PREBLEND, 0, + priv->io_base + _REG(VPP_MISC)); + + /* Setup default VD settings */ + writel_relaxed(4096, + priv->io_base + _REG(VPP_PREBLEND_VD1_H_START_END)); + writel_relaxed(4096, + priv->io_base + _REG(VPP_BLEND_VD2_H_START_END)); + } + + /* Disable Scalers */ + writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0)); + writel_relaxed(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); + writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); + + /* Set horizontal/vertical bank length and enable video scale out */ + writel_relaxed(VPP_VSC_BANK_LENGTH(4) | VPP_HSC_BANK_LENGTH(4) | + VPP_SC_VD_EN_ENABLE, + priv->io_base + _REG(VPP_SC_MISC)); + + /* Enable minus black level for vadj1 */ + writel_relaxed(VPP_MINUS_BLACK_LVL_VADJ1_ENABLE, + priv->io_base + _REG(VPP_VADJ_CTRL)); + + /* Write in the proper filter coefficients. */ + meson_vpp_write_scaling_filter_coefs(priv, + vpp_filter_coefs_4point_bspline, false); + meson_vpp_write_scaling_filter_coefs(priv, + vpp_filter_coefs_4point_bspline, true); + + /* Write the VD proper filter coefficients. */ + meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic, + false); + meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic, + true); +} diff --git a/drivers/gpu/drm/meson/meson_vpp.h b/drivers/gpu/drm/meson/meson_vpp.h new file mode 100644 index 0000000000..b790042a16 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_vpp.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +/* Video Post Process */ + +#ifndef __MESON_VPP_H +#define __MESON_VPP_H + +struct drm_rect; +struct meson_drm; + +/* Mux VIU/VPP to ENCL */ +#define MESON_VIU_VPP_MUX_ENCL 0x0 +/* Mux VIU/VPP to ENCI */ +#define MESON_VIU_VPP_MUX_ENCI 0x5 +/* Mux VIU/VPP to ENCP */ +#define MESON_VIU_VPP_MUX_ENCP 0xA + +void meson_vpp_setup_mux(struct meson_drm *priv, unsigned int mux); + +void meson_vpp_setup_interlace_vscaler_osd1(struct meson_drm *priv, + struct drm_rect *input); +void meson_vpp_disable_interlace_vscaler_osd1(struct meson_drm *priv); + +void meson_vpp_init(struct meson_drm *priv); + +#endif /* __MESON_VPP_H */ |