diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
commit | 76cb841cb886eef6b3bee341a2266c76578724ad (patch) | |
tree | f5892e5ba6cc11949952a6ce4ecbe6d516d6ce58 /drivers/gpu/drm/meson | |
parent | Initial commit. (diff) | |
download | linux-76cb841cb886eef6b3bee341a2266c76578724ad.tar.xz linux-76cb841cb886eef6b3bee341a2266c76578724ad.zip |
Adding upstream version 4.19.249.upstream/4.19.249
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/gpu/drm/meson')
23 files changed, 7225 insertions, 0 deletions
diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig new file mode 100644 index 000000000..3ce51d8df --- /dev/null +++ b/drivers/gpu/drm/meson/Kconfig @@ -0,0 +1,15 @@ +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_KMS_CMA_HELPER + select DRM_GEM_CMA_HELPER + select VIDEOMODE_HELPERS + select REGMAP_MMIO + +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 diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile new file mode 100644 index 000000000..c5c4cc362 --- /dev/null +++ b/drivers/gpu/drm/meson/Makefile @@ -0,0 +1,5 @@ +meson-drm-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o +meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o + +obj-$(CONFIG_DRM_MESON) += meson-drm.o +obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o diff --git a/drivers/gpu/drm/meson/meson_canvas.c b/drivers/gpu/drm/meson/meson_canvas.c new file mode 100644 index 000000000..08f6073d9 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_canvas.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include "meson_drv.h" +#include "meson_canvas.h" +#include "meson_registers.h" + +/** + * DOC: Canvas + * + * CANVAS is a memory zone where physical memory frames information + * are stored for the VIU to scanout. + */ + +/* DMC Registers */ +#define DMC_CAV_LUT_DATAL 0x48 /* 0x12 offset in data sheet */ +#define CANVAS_WIDTH_LBIT 29 +#define CANVAS_WIDTH_LWID 3 +#define DMC_CAV_LUT_DATAH 0x4c /* 0x13 offset in data sheet */ +#define CANVAS_WIDTH_HBIT 0 +#define CANVAS_HEIGHT_BIT 9 +#define CANVAS_BLKMODE_BIT 24 +#define DMC_CAV_LUT_ADDR 0x50 /* 0x14 offset in data sheet */ +#define CANVAS_LUT_WR_EN (0x2 << 8) +#define CANVAS_LUT_RD_EN (0x1 << 8) + +void meson_canvas_setup(struct meson_drm *priv, + uint32_t canvas_index, uint32_t addr, + uint32_t stride, uint32_t height, + unsigned int wrap, + unsigned int blkmode) +{ + unsigned int val; + + regmap_write(priv->dmc, DMC_CAV_LUT_DATAL, + (((addr + 7) >> 3)) | + (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT)); + + regmap_write(priv->dmc, DMC_CAV_LUT_DATAH, + ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) << + CANVAS_WIDTH_HBIT) | + (height << CANVAS_HEIGHT_BIT) | + (wrap << 22) | + (blkmode << CANVAS_BLKMODE_BIT)); + + regmap_write(priv->dmc, DMC_CAV_LUT_ADDR, + CANVAS_LUT_WR_EN | canvas_index); + + /* Force a read-back to make sure everything is flushed. */ + regmap_read(priv->dmc, DMC_CAV_LUT_DATAH, &val); +} diff --git a/drivers/gpu/drm/meson/meson_canvas.h b/drivers/gpu/drm/meson/meson_canvas.h new file mode 100644 index 000000000..af1759da4 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_canvas.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Canvas LUT Memory */ + +#ifndef __MESON_CANVAS_H +#define __MESON_CANVAS_H + +#define MESON_CANVAS_ID_OSD1 0x4e + +/* Canvas configuration. */ +#define MESON_CANVAS_WRAP_NONE 0x00 +#define MESON_CANVAS_WRAP_X 0x01 +#define MESON_CANVAS_WRAP_Y 0x02 + +#define MESON_CANVAS_BLKMODE_LINEAR 0x00 +#define MESON_CANVAS_BLKMODE_32x32 0x01 +#define MESON_CANVAS_BLKMODE_64x64 0x02 + +void meson_canvas_setup(struct meson_drm *priv, + uint32_t canvas_index, uint32_t addr, + uint32_t stride, uint32_t height, + unsigned int wrap, + unsigned int blkmode); + +#endif /* __MESON_CANVAS_H */ diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c new file mode 100644 index 000000000..709475d5c --- /dev/null +++ b/drivers/gpu/drm/meson/meson_crtc.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_flip_work.h> +#include <drm/drm_crtc_helper.h> + +#include "meson_crtc.h" +#include "meson_plane.h" +#include "meson_venc.h" +#include "meson_vpp.h" +#include "meson_viu.h" +#include "meson_canvas.h" +#include "meson_registers.h" + +/* CRTC definition */ + +struct meson_crtc { + struct drm_crtc base; + struct drm_pending_vblank_event *event; + struct meson_drm *priv; +}; +#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_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; + + 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_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_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)); + + writel_bits_relaxed(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE, + priv->io_base + _REG(VPP_MISC)); + + drm_crtc_vblank_on(crtc); + + priv->viu.osd1_enabled = true; +} + +static void meson_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + struct meson_drm *priv = meson_crtc->priv; + + drm_crtc_vblank_off(crtc); + + priv->viu.osd1_enabled = false; + priv->viu.osd1_commit = false; + + /* Disable VPP Postblend */ + writel_bits_relaxed(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_crtc_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_crtc_state *old_crtc_state) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + struct meson_drm *priv = meson_crtc->priv; + + priv->viu.osd1_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, +}; + +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_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 output is interlace, make use of the Scaler */ + if (priv->viu.osd1_interlace) { + struct drm_plane *plane = priv->primary_plane; + struct drm_plane_state *state = plane->state; + struct drm_rect dest = { + .x1 = state->crtc_x, + .y1 = state->crtc_y, + .x2 = state->crtc_x + state->crtc_w, + .y2 = state->crtc_y + state->crtc_h, + }; + + meson_vpp_setup_interlace_vscaler_osd1(priv, &dest); + } else + meson_vpp_disable_interlace_vscaler_osd1(priv); + + meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1, + priv->viu.osd1_addr, priv->viu.osd1_stride, + priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR); + + /* Enable OSD1 */ + writel_bits_relaxed(VPP_OSD1_POSTBLEND, VPP_OSD1_POSTBLEND, + priv->io_base + _REG(VPP_MISC)); + + priv->viu.osd1_commit = false; + } + + 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; + } + + 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 000000000..b62b9e517 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_crtc.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * 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 000000000..1887473cd --- /dev/null +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -0,0 +1,454 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/component.h> +#include <linux/of_graph.h> + +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_flip_work.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_rect.h> +#include <drm/drm_fb_helper.h> + +#include "meson_drv.h" +#include "meson_plane.h" +#include "meson_crtc.h" +#include "meson_venc_cvbs.h" + +#include "meson_vpp.h" +#include "meson_viu.h" +#include "meson_venc.h" +#include "meson_canvas.h" +#include "meson_registers.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 void meson_fb_output_poll_changed(struct drm_device *dev) +{ + struct meson_drm *priv = dev->dev_private; + + drm_fbdev_cma_hotplug_event(priv->fbdev); +} + +static const struct drm_mode_config_funcs meson_mode_config_funcs = { + .output_poll_changed = meson_fb_output_poll_changed, + .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; +} + +DEFINE_DRM_GEM_CMA_FOPS(fops); + +static struct drm_driver meson_driver = { + .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | + DRIVER_MODESET | DRIVER_PRIME | + DRIVER_ATOMIC, + + /* IRQ */ + .irq_handler = meson_irq, + + /* PRIME Ops */ + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, + + /* GEM Ops */ + .dumb_create = drm_gem_cma_dumb_create, + .gem_free_object_unlocked = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + + /* 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) + 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) +{ + writel_relaxed(0x210000, priv->io_base + _REG(VPU_RDARB_MODE_L1C1)); + writel_relaxed(0x10000, priv->io_base + _REG(VPU_RDARB_MODE_L1C2)); + writel_relaxed(0x900000, priv->io_base + _REG(VPU_RDARB_MODE_L2C1)); + writel_relaxed(0x20000, priv->io_base + _REG(VPU_WRARB_MODE_L2C1)); +} + +static int meson_drv_bind_master(struct device *dev, bool has_components) +{ + struct platform_device *pdev = to_platform_device(dev); + struct meson_drm *priv; + struct drm_device *drm; + struct resource *res; + void __iomem *regs; + int ret; + + /* Checks if an output connector is available */ + if (!meson_vpu_has_available_connectors(dev)) { + dev_err(dev, "No output connector available\n"); + 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; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu"); + regs = devm_ioremap_resource(dev, res); + 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; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc"); + 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->dmc = devm_regmap_init_mmio(dev, regs, + &meson_regmap_config); + if (IS_ERR(priv->dmc)) { + dev_err(&pdev->dev, "Couldn't create the DMC regmap\n"); + ret = PTR_ERR(priv->dmc); + goto free_drm; + } + + priv->vsync_irq = platform_get_irq(pdev, 0); + + ret = drm_vblank_init(drm, 1); + if (ret) + goto free_drm; + + drm_mode_config_init(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); + + /* Encoder Initialization */ + + ret = meson_venc_cvbs_create(priv); + if (ret) + goto free_drm; + + if (has_components) { + ret = component_bind_all(drm->dev, drm); + if (ret) { + dev_err(drm->dev, "Couldn't bind all components\n"); + goto free_drm; + } + } + + ret = meson_plane_create(priv); + if (ret) + goto free_drm; + + ret = meson_crtc_create(priv); + if (ret) + goto free_drm; + + ret = drm_irq_install(drm, priv->vsync_irq); + if (ret) + goto free_drm; + + drm_mode_config_reset(drm); + + priv->fbdev = drm_fbdev_cma_init(drm, 32, + drm->mode_config.num_connector); + if (IS_ERR(priv->fbdev)) { + ret = PTR_ERR(priv->fbdev); + goto free_drm; + } + + drm_kms_helper_poll_init(drm); + + platform_set_drvdata(pdev, priv); + + ret = drm_dev_register(drm, 0); + if (ret) + goto uninstall_irq; + + return 0; + +uninstall_irq: + drm_irq_uninstall(drm); +free_drm: + drm_dev_put(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; + + drm_dev_unregister(drm); + drm_irq_uninstall(drm); + drm_kms_helper_poll_fini(drm); + drm_fbdev_cma_fini(priv->fbdev); + drm_mode_config_cleanup(drm); + drm_dev_put(drm); + +} + +static const struct component_master_ops meson_drv_master_ops = { + .bind = meson_drv_bind, + .unbind = meson_drv_unbind, +}; + +static int compare_of(struct device *dev, void *data) +{ + DRM_DEBUG_DRIVER("Comparing of node %pOF with %pOF\n", + dev->of_node, data); + + return dev->of_node == data; +} + +/* Possible connectors nodes to ignore */ +static const struct of_device_id connectors_match[] = { + { .compatible = "composite-video-connector" }, + { .compatible = "svideo-connector" }, + { .compatible = "hdmi-connector" }, + { .compatible = "dvi-connector" }, + {} +}; + +static int meson_probe_remote(struct platform_device *pdev, + struct component_match **match, + struct device_node *parent, + struct device_node *remote) +{ + struct device_node *ep, *remote_node; + int count = 1; + + /* If node is a connector, return and do not add to match table */ + if (of_match_node(connectors_match, remote)) + return 1; + + component_match_add(&pdev->dev, match, compare_of, remote); + + for_each_endpoint_of_node(remote, ep) { + remote_node = of_graph_get_remote_port_parent(ep); + if (!remote_node || + remote_node == parent || /* Ignore parent endpoint */ + !of_device_is_available(remote_node)) { + of_node_put(remote_node); + continue; + } + + count += meson_probe_remote(pdev, match, remote, remote_node); + + of_node_put(remote_node); + } + + return count; +} + +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); +} + +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; + } + + count += meson_probe_remote(pdev, &match, np, remote); + of_node_put(remote); + } + + 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 const struct of_device_id dt_match[] = { + { .compatible = "amlogic,meson-gxbb-vpu" }, + { .compatible = "amlogic,meson-gxl-vpu" }, + { .compatible = "amlogic,meson-gxm-vpu" }, + {} +}; +MODULE_DEVICE_TABLE(of, dt_match); + +static struct platform_driver meson_drm_platform_driver = { + .probe = meson_drv_probe, + .shutdown = meson_drv_shutdown, + .driver = { + .name = "meson-drm", + .of_match_table = dt_match, + }, +}; + +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 000000000..8450d6ac8 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_drv.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __MESON_DRV_H +#define __MESON_DRV_H + +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/of.h> +#include <drm/drmP.h> + +struct meson_drm { + struct device *dev; + void __iomem *io_base; + struct regmap *hhi; + struct regmap *dmc; + int vsync_irq; + + struct drm_device *drm; + struct drm_crtc *crtc; + struct drm_fbdev_cma *fbdev; + struct drm_plane *primary_plane; + + /* Components Data */ + struct { + bool osd1_enabled; + bool osd1_interlace; + bool osd1_commit; + uint32_t osd1_ctrl_stat; + uint32_t osd1_blk0_cfg[5]; + uint32_t osd1_addr; + uint32_t osd1_stride; + uint32_t osd1_height; + } viu; + + struct { + unsigned int current_mode; + bool hdmi_repeat; + bool venc_repeat; + bool hdmi_use_enci; + } venc; +}; + +static inline int meson_vpu_is_compatible(struct meson_drm *priv, + const char *compat) +{ + return of_device_is_compatible(priv->dev->of_node, compat); +} + +#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 000000000..2cb2ad26d --- /dev/null +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c @@ -0,0 +1,946 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/component.h> +#include <linux/of_graph.h> +#include <linux/reset.h> +#include <linux/clk.h> +#include <linux/regulator/consumer.h> + +#include <drm/drmP.h> +#include <drm/drm_edid.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic_helper.h> +#include <drm/bridge/dw_hdmi.h> + +#include <uapi/linux/media-bus-format.h> +#include <uapi/linux/videodev2.h> + +#include "meson_drv.h" +#include "meson_venc.h" +#include "meson_vclk.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 + +/* 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 */ + +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 drm_encoder encoder; + struct dw_hdmi_plat_data dw_plat_data; + struct meson_drm *priv; + struct device *dev; + void __iomem *hdmitx; + struct reset_control *hdmitx_apb; + struct reset_control *hdmitx_ctrl; + struct reset_control *hdmitx_phy; + struct clk *hdmi_pclk; + struct clk *venci_clk; + struct regulator *hdmi_supply; + u32 irq_stat; + struct dw_hdmi *hdmi; +}; +#define encoder_to_meson_dw_hdmi(x) \ + container_of(x, struct meson_dw_hdmi, encoder) + +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 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); +} + +/* 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_top_read(dw_hdmi, addr); + + data &= ~mask; + data |= val; + + dw_hdmi_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 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); +} + +/* 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_dwc_read(dw_hdmi, addr); + + data &= ~mask; + data |= val; + + dw_hdmi_dwc_write(dw_hdmi, addr, data); +} + +/* Bridge */ + +/* Setup PHY bandwidth modes */ +static void meson_hdmi_phy_setup_mode(struct meson_dw_hdmi *dw_hdmi, + struct drm_display_mode *mode) +{ + struct meson_drm *priv = dw_hdmi->priv; + unsigned int pixel_clock = mode->clock; + + 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); + } + } +} + +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 void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi, + struct drm_display_mode *mode) +{ + struct meson_drm *priv = dw_hdmi->priv; + int vic = drm_match_cea_mode(mode); + unsigned int vclk_freq; + unsigned int venc_freq; + unsigned int hdmi_freq; + + vclk_freq = mode->clock; + + if (!vic) { + meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, vclk_freq, + vclk_freq, vclk_freq, false); + return; + } + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + vclk_freq *= 2; + + venc_freq = vclk_freq; + hdmi_freq = vclk_freq; + + if (meson_venc_hdmi_venc_repeat(vic)) + venc_freq *= 2; + + vclk_freq = max(venc_freq, hdmi_freq); + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + venc_freq /= 2; + + DRM_DEBUG_DRIVER("vclk:%d venc=%d hdmi=%d enci=%d\n", + vclk_freq, venc_freq, hdmi_freq, + priv->venc.hdmi_use_enci); + + meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, vclk_freq, + venc_freq, hdmi_freq, priv->venc.hdmi_use_enci); +} + +static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data, + struct drm_display_mode *mode) +{ + struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data; + struct meson_drm *priv = dw_hdmi->priv; + unsigned int wr_clk = + readl_relaxed(priv->io_base + _REG(VPU_HDMI_SETTING)); + + DRM_DEBUG_DRIVER("%d:\"%s\"\n", mode->base.id, mode->name); + + /* 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_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); + dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_CLK_CNTL, + 0x3 << 4, 0x3 << 4); + + /* Enable normal output to PHY */ + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12)); + + /* TMDS pattern setup (TOFIX pattern for 4k2k scrambling) */ + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01, 0x001f001f); + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23, 0x001f001f); + + /* Load TMDS pattern */ + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x1); + msleep(20); + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x2); + + /* Setup PHY parameters */ + meson_hdmi_phy_setup_mode(dw_hdmi, mode); + + /* 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")) + 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); + + 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_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_top_write(dw_hdmi, HDMITX_TOP_HPD_FILTER, + (0xa << 12) | 0xa0); + + /* Clear interrupts */ + dw_hdmi_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_top_read(dw_hdmi, HDMITX_TOP_INTR_STAT); + dw_hdmi_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->encoder.dev); + } + + return IRQ_HANDLED; +} + +static enum drm_mode_status +dw_hdmi_mode_valid(struct drm_connector *connector, + const struct drm_display_mode *mode) +{ + struct meson_drm *priv = connector->dev->dev_private; + unsigned int vclk_freq; + unsigned int venc_freq; + unsigned int hdmi_freq; + int vic = drm_match_cea_mode(mode); + enum drm_mode_status status; + + DRM_DEBUG_DRIVER("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n", + mode->base.id, mode->name, mode->vrefresh, mode->clock, + mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal, mode->type, mode->flags); + + /* 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; + + /* 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 and 720p modes */ + if (meson_venc_hdmi_venc_repeat(vic)) + venc_freq *= 2; + + vclk_freq = max(venc_freq, hdmi_freq); + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + venc_freq /= 2; + + dev_dbg(connector->dev->dev, "%s: vclk:%d venc=%d hdmi=%d\n", __func__, + vclk_freq, venc_freq, hdmi_freq); + + /* Finally filter by configurable vclk frequencies for VIC modes */ + switch (vclk_freq) { + case 54000: + case 74250: + case 148500: + case 297000: + case 594000: + return MODE_OK; + } + + return MODE_CLOCK_RANGE; +} + +/* Encoder */ + +static void meson_venc_hdmi_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs meson_venc_hdmi_encoder_funcs = { + .destroy = meson_venc_hdmi_encoder_destroy, +}; + +static int meson_venc_hdmi_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + return 0; +} + +static void meson_venc_hdmi_encoder_disable(struct drm_encoder *encoder) +{ + struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder); + struct meson_drm *priv = dw_hdmi->priv; + + DRM_DEBUG_DRIVER("\n"); + + 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 void meson_venc_hdmi_encoder_enable(struct drm_encoder *encoder) +{ + struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder); + struct meson_drm *priv = dw_hdmi->priv; + + DRM_DEBUG_DRIVER("%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_venc_hdmi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder); + struct meson_drm *priv = dw_hdmi->priv; + int vic = drm_match_cea_mode(mode); + + DRM_DEBUG_DRIVER("%d:\"%s\" vic %d\n", + mode->base.id, mode->name, vic); + + /* VENC + VENC-DVI Mode setup */ + meson_venc_hdmi_mode_set(priv, vic, mode); + + /* VCLK Set clock */ + dw_hdmi_set_vclk(dw_hdmi, mode); + + /* Setup YUV444 to HDMI-TX, no 10bit diphering */ + writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); +} + +static const struct drm_encoder_helper_funcs + meson_venc_hdmi_encoder_helper_funcs = { + .atomic_check = meson_venc_hdmi_encoder_atomic_check, + .disable = meson_venc_hdmi_encoder_disable, + .enable = meson_venc_hdmi_encoder_enable, + .mode_set = meson_venc_hdmi_encoder_mode_set, +}; + +/* DW HDMI Regmap */ + +static int meson_dw_hdmi_reg_read(void *context, unsigned int reg, + unsigned int *result) +{ + *result = dw_hdmi_dwc_read(context, reg); + + return 0; + +} + +static int meson_dw_hdmi_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + dw_hdmi_dwc_write(context, 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 bool meson_hdmi_connector_is_available(struct device *dev) +{ + struct device_node *ep, *remote; + + /* HDMI Connector is on the second port, first endpoint */ + ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, 0); + if (!ep) + return false; + + /* If the endpoint node exists, consider it enabled */ + remote = of_graph_get_remote_port(ep); + if (remote) { + of_node_put(ep); + return true; + } + + of_node_put(ep); + of_node_put(remote); + + return false; +} + +static int meson_dw_hdmi_bind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + 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; + struct drm_encoder *encoder; + struct resource *res; + int irq; + int ret; + + DRM_DEBUG_DRIVER("\n"); + + if (!meson_hdmi_connector_is_available(dev)) { + dev_info(drm->dev, "HDMI Output connector not available\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; + dw_plat_data = &meson_dw_hdmi->dw_plat_data; + encoder = &meson_dw_hdmi->encoder; + + meson_dw_hdmi->hdmi_supply = devm_regulator_get_optional(dev, "hdmi"); + if (IS_ERR(meson_dw_hdmi->hdmi_supply)) { + if (PTR_ERR(meson_dw_hdmi->hdmi_supply) == -EPROBE_DEFER) + return -EPROBE_DEFER; + meson_dw_hdmi->hdmi_supply = NULL; + } else { + ret = regulator_enable(meson_dw_hdmi->hdmi_supply); + if (ret) + 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); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + meson_dw_hdmi->hdmitx = devm_ioremap_resource(dev, res); + if (IS_ERR(meson_dw_hdmi->hdmitx)) + return PTR_ERR(meson_dw_hdmi->hdmitx); + + meson_dw_hdmi->hdmi_pclk = devm_clk_get(dev, "isfr"); + if (IS_ERR(meson_dw_hdmi->hdmi_pclk)) { + dev_err(dev, "Unable to get HDMI pclk\n"); + return PTR_ERR(meson_dw_hdmi->hdmi_pclk); + } + clk_prepare_enable(meson_dw_hdmi->hdmi_pclk); + + meson_dw_hdmi->venci_clk = devm_clk_get(dev, "venci"); + if (IS_ERR(meson_dw_hdmi->venci_clk)) { + dev_err(dev, "Unable to get venci clk\n"); + return PTR_ERR(meson_dw_hdmi->venci_clk); + } + clk_prepare_enable(meson_dw_hdmi->venci_clk); + + 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) { + dev_err(dev, "Failed to get hdmi top irq\n"); + 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; + } + + /* Encoder */ + + drm_encoder_helper_add(encoder, &meson_venc_hdmi_encoder_helper_funcs); + + ret = drm_encoder_init(drm, encoder, &meson_venc_hdmi_encoder_funcs, + DRM_MODE_ENCODER_TMDS, "meson_hdmi"); + if (ret) { + dev_err(priv->dev, "Failed to init HDMI encoder\n"); + return ret; + } + + encoder->possible_crtcs = BIT(0); + + DRM_DEBUG_DRIVER("encoder initialized\n"); + + /* 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 */ + 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 */ + dw_hdmi_top_write(meson_dw_hdmi, HDMITX_TOP_SW_RESET, 0); + + msleep(20); + + dw_hdmi_top_write(meson_dw_hdmi, HDMITX_TOP_CLK_CNTL, 0xff); + + /* Enable HDMI-TX Interrupt */ + dw_hdmi_top_write(meson_dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, + HDMITX_TOP_INTR_CORE); + + dw_hdmi_top_write(meson_dw_hdmi, HDMITX_TOP_INTR_MASKN, + HDMITX_TOP_INTR_CORE); + + /* Bridge / Connector */ + + dw_plat_data->mode_valid = dw_hdmi_mode_valid; + 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_format = MEDIA_BUS_FMT_YUV8_1X24; + dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709; + + platform_set_drvdata(pdev, meson_dw_hdmi); + + meson_dw_hdmi->hdmi = dw_hdmi_bind(pdev, encoder, + &meson_dw_hdmi->dw_plat_data); + if (IS_ERR(meson_dw_hdmi->hdmi)) + return PTR_ERR(meson_dw_hdmi->hdmi); + + 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 meson_dw_hdmi_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &meson_dw_hdmi_ops); +} + +static int meson_dw_hdmi_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &meson_dw_hdmi_ops); + + return 0; +} + +static const struct of_device_id meson_dw_hdmi_of_table[] = { + { .compatible = "amlogic,meson-gxbb-dw-hdmi" }, + { .compatible = "amlogic,meson-gxl-dw-hdmi" }, + { .compatible = "amlogic,meson-gxm-dw-hdmi" }, + { } +}; +MODULE_DEVICE_TABLE(of, meson_dw_hdmi_of_table); + +static struct platform_driver meson_dw_hdmi_platform_driver = { + .probe = meson_dw_hdmi_probe, + .remove = meson_dw_hdmi_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = meson_dw_hdmi_of_table, + }, +}; +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 000000000..0b8118312 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __MESON_DW_HDMI_H +#define __MESON_DW_HDMI_H + +/* + * Bit 7 RW Reserved. Default 1. + * Bit 6 RW Reserved. Default 1. + * Bit 5 RW Reserved. Default 1. + * 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 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 4 RW cec_clk_en: 1=enable cec_clk; 0=disable. Default 0. + * 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 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. + * [ 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 2 RW hpd_fall + * Bit 1 RW hpd_rise + * Bit 0 RW IP interrupt + */ +#define HDMITX_TOP_INTR_STAT (0x004) + +/* + * [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) + +/* 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 0 R filtered HPD status. */ +#define HDMITX_TOP_STAT0 (0x00E) + +#endif /* __MESON_DW_HDMI_H */ diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c new file mode 100644 index 000000000..c7daae53f --- /dev/null +++ b/drivers/gpu/drm/meson/meson_plane.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_rect.h> + +#include "meson_plane.h" +#include "meson_vpp.h" +#include "meson_viu.h" +#include "meson_canvas.h" +#include "meson_registers.h" + +struct meson_plane { + struct drm_plane base; + struct meson_drm *priv; +}; +#define to_meson_plane(x) container_of(x, struct meson_plane, base) + +static int meson_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc_state *crtc_state; + + if (!state->crtc) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + return drm_atomic_helper_check_plane_state(state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + 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 void meson_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct meson_plane *meson_plane = to_meson_plane(plane); + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + struct meson_drm *priv = meson_plane->priv; + struct drm_gem_cma_object *gem; + struct drm_rect src = { + .x1 = (state->src_x), + .y1 = (state->src_y), + .x2 = (state->src_x + state->src_w), + .y2 = (state->src_y + state->src_h), + }; + struct drm_rect dest = { + .x1 = state->crtc_x, + .y1 = state->crtc_y, + .x2 = state->crtc_x + state->crtc_w, + .y2 = state->crtc_y + state->crtc_h, + }; + unsigned long flags; + + /* + * Update Coordinates + * Update Formats + * Update Buffer + * Enable Plane + */ + spin_lock_irqsave(&priv->drm->event_lock, flags); + + /* Enable OSD and BLK0, set max global alpha */ + priv->viu.osd1_ctrl_stat = OSD_ENABLE | + (0xFF << OSD_GLOBAL_ALPHA_SHIFT) | + OSD_BLK0_ENABLE; + + /* Set up BLK0 to point to the right canvas */ + priv->viu.osd1_blk0_cfg[0] = ((MESON_CANVAS_ID_OSD1 << OSD_CANVAS_SEL) | + OSD_ENDIANNESS_LE); + + /* On GXBB, Use the old non-HDR RGB2YUV converter */ + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + priv->viu.osd1_blk0_cfg[0] |= OSD_OUTPUT_COLOR_RGB; + + switch (fb->format->format) { + case DRM_FORMAT_XRGB8888: + /* For XRGB, replace the pixel's alpha by 0xFF */ + writel_bits_relaxed(OSD_REPLACE_EN, OSD_REPLACE_EN, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); + priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 | + OSD_COLOR_MATRIX_32_ARGB; + break; + case DRM_FORMAT_XBGR8888: + /* For XRGB, replace the pixel's alpha by 0xFF */ + writel_bits_relaxed(OSD_REPLACE_EN, OSD_REPLACE_EN, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); + priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 | + OSD_COLOR_MATRIX_32_ABGR; + break; + case DRM_FORMAT_ARGB8888: + /* For ARGB, use the pixel's alpha */ + writel_bits_relaxed(OSD_REPLACE_EN, 0, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); + priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 | + OSD_COLOR_MATRIX_32_ARGB; + break; + case DRM_FORMAT_ABGR8888: + /* For ARGB, use the pixel's alpha */ + writel_bits_relaxed(OSD_REPLACE_EN, 0, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); + 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; + }; + + if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) { + priv->viu.osd1_interlace = true; + + dest.y1 /= 2; + dest.y2 /= 2; + } else + priv->viu.osd1_interlace = false; + + /* + * 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(src.x2) - 1) << 16) | + fixed16_to_int(src.x1); + priv->viu.osd1_blk0_cfg[2] = ((fixed16_to_int(src.y2) - 1) << 16) | + fixed16_to_int(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; + + /* Update Canvas with buffer address */ + gem = drm_fb_cma_get_gem_obj(fb, 0); + + priv->viu.osd1_addr = gem->paddr; + priv->viu.osd1_stride = fb->pitches[0]; + priv->viu.osd1_height = fb->height; + + spin_unlock_irqrestore(&priv->drm->event_lock, flags); +} + +static void meson_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct meson_plane *meson_plane = to_meson_plane(plane); + struct meson_drm *priv = meson_plane->priv; + + /* Disable OSD1 */ + writel_bits_relaxed(VPP_OSD1_POSTBLEND, 0, + priv->io_base + _REG(VPP_MISC)); + +} + +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 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, +}; + +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, +}; + +int meson_plane_create(struct meson_drm *priv) +{ + struct meson_plane *meson_plane; + struct drm_plane *plane; + + 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; + + drm_universal_plane_init(priv->drm, plane, 0xFF, + &meson_plane_funcs, + supported_drm_formats, + ARRAY_SIZE(supported_drm_formats), + NULL, + DRM_PLANE_TYPE_PRIMARY, "meson_primary_plane"); + + drm_plane_helper_add(plane, &meson_plane_helper_funcs); + + 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 000000000..e26b8b0aa --- /dev/null +++ b/drivers/gpu/drm/meson/meson_plane.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * 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_registers.h b/drivers/gpu/drm/meson/meson_registers.h new file mode 100644 index 000000000..bca87143e --- /dev/null +++ b/drivers/gpu/drm/meson/meson_registers.h @@ -0,0 +1,1400 @@ +/* + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __MESON_REGISTERS_H +#define __MESON_REGISTERS_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), 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_MISC_CTRL0 0x1a06 +#define VIU_MISC_CTRL1 0x1a07 +#define D2D3_INTF_LENGTH 0x1a08 +#define D2D3_INTF_CTRL0 0x1a09 +#define VIU_OSD1_CTRL_STAT 0x1a10 +#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_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 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 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 + +/* 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_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_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_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_OFIFO_SIZE 0x1d27 +#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_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_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 + +/* 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 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_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_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 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_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_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_PULSE_LO 0x1b51 +#define ENCI_MACV_PULSE_HI 0x1b52 +#define ENCI_MACV_BKP_MAX 0x1b53 +#define ENCI_CFILT_CTRL 0x1b54 +#define ENCI_CFILT7 0x1b55 +#define ENCI_YC_DELAY 0x1b56 +#define ENCI_VIDEO_EN 0x1b57 +#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_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_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_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_VIDEO_MODE_ADV 0x1ca8 +#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_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_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_AUTO2 0x1111 +#define RDMA_ACCESS_AUTO3 0x1112 +#define RDMA_ACCESS_MAN 0x1113 +#define RDMA_CTRL 0x1114 +#define RDMA_STATUS 0x1115 +#define RDMA_STATUS2 0x1116 +#define RDMA_STATUS3 0x1117 +#define L_GAMMA_CNTL_PORT 0x1400 +#define L_GAMMA_DATA_PORT 0x1401 +#define L_GAMMA_ADDR_PORT 0x1402 +#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_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_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 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 + +/* 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 + +#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 000000000..ae5473257 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_vclk.c @@ -0,0 +1,900 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <drm/drmP.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_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 HDMI_PLL_RESET BIT(28) +#define HDMI_PLL_LOCK BIT(31) + +/* 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, +}; + +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, "amlogic,meson-gxbb-vpu")) { + 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); + } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) { + 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); + + /* 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 */ + 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 = 1, +/* 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 +}; + +struct meson_vclk_params { + unsigned int pll_base_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_base_freq = 4320000, + .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_base_freq = 4320000, + .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_base_freq = 2970000, + .pll_od1 = 4, + .pll_od2 = 1, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_74250] = { + .pll_base_freq = 2970000, + .pll_od1 = 2, + .pll_od2 = 2, + .pll_od3 = 2, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_148500] = { + .pll_base_freq = 2970000, + .pll_od1 = 1, + .pll_od2 = 2, + .pll_od3 = 2, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_297000] = { + .pll_base_freq = 2970000, + .pll_od1 = 1, + .pll_od2 = 1, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 2, + }, + [MESON_VCLK_HDMI_594000] = { + .pll_base_freq = 5940000, + .pll_od1 = 1, + .pll_od2 = 1, + .pll_od3 = 2, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, +}; + +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; +} + +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, "amlogic,meson-gxbb-vpu")) { + 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, 0x4 << 28); + + /* 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, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) { + 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); + } + + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 3 << 16, pll_od_to_reg(od1) << 16); + else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, + 3 << 21, pll_od_to_reg(od1) << 21); + + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 3 << 22, pll_od_to_reg(od2) << 22); + else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, + 3 << 23, pll_od_to_reg(od2) << 23); + + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 3 << 18, pll_od_to_reg(od3) << 18); + else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, + 3 << 19, pll_od_to_reg(od3) << 19); + +} + +#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, "amlogic,meson-gxbb-vpu")) + pll_freq /= 2; + + return pll_freq / XTAL_FREQ; +} + +#define HDMI_FRAC_MAX_GXBB 4096 +#define HDMI_FRAC_MAX_GXL 1024 + +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, "amlogic,meson-gxbb-vpu")) { + frac_max = HDMI_FRAC_MAX_GXBB; + parent_freq *= 2; + } + + /* 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, "amlogic,meson-gxbb-vpu")) { + /* 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, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) { + /* Empiric supported min/max dividers */ + if (m < 106 || m > 247) + return false; + if (frac >= HDMI_FRAC_MAX_GXL) + 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; + + 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)) { + 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); +} + +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) +{ + /* 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, "amlogic,meson-gxbb-vpu")) { + switch (pll_base_freq) { + case 2970000: + meson_hdmi_pll_set_params(priv, 0x3d, 0xe00, + od1, od2, od3); + break; + case 4320000: + meson_hdmi_pll_set_params(priv, 0x5a, 0, + od1, od2, od3); + break; + case 5940000: + meson_hdmi_pll_set_params(priv, 0x7b, 0xc00, + od1, od2, od3); + break; + } + } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) { + switch (pll_base_freq) { + case 2970000: + meson_hdmi_pll_set_params(priv, 0x7b, 0x300, + od1, od2, od3); + break; + case 4320000: + meson_hdmi_pll_set_params(priv, 0xb4, 0, + od1, od2, od3); + break; + case 5940000: + meson_hdmi_pll_set_params(priv, 0xf7, 0x200, + od1, od2, od3); + break; + } + } + + /* 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 vclk_freq, unsigned int venc_freq, + unsigned int dac_freq, bool hdmi_use_enci) +{ + 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, vclk_freq * 10, 0, 0, 0, + VID_PLL_DIV_5, 2, 1, 1, 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; + } + + switch (vclk_freq) { + case 54000: + if (hdmi_use_enci) + freq = MESON_VCLK_HDMI_ENCI_54000; + else + freq = MESON_VCLK_HDMI_DDR_54000; + break; + case 74250: + freq = MESON_VCLK_HDMI_74250; + break; + case 148500: + if (dac_freq != 148500) + freq = MESON_VCLK_HDMI_DDR_148500; + else + freq = MESON_VCLK_HDMI_148500; + break; + case 297000: + freq = MESON_VCLK_HDMI_297000; + break; + case 594000: + freq = MESON_VCLK_HDMI_594000; + break; + default: + pr_err("Fatal Error, invalid HDMI vclk freq %d\n", + vclk_freq); + return; + } + + meson_vclk_set(priv, params[freq].pll_base_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); +} +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 000000000..869fa3a30 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_vclk.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Video Clock */ + +#ifndef __MESON_VCLK_H +#define __MESON_VCLK_H + +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); + +void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + 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 000000000..7a3a6ed9f --- /dev/null +++ b/drivers/gpu/drm/meson/meson_venc.c @@ -0,0 +1,1568 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <drm/drmP.h> +#include "meson_drv.h" +#include "meson_venc.h" +#include "meson_vpp.h" +#include "meson_vclk.h" +#include "meson_registers.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 initialy desined 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_CNTL1 0x2F8 /* 0xbe 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; +}; + +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 = 0x810b, + .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, + }, +}; + +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 = 8107, + .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, + }, +}; + +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, + }, +}; + +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, + }, +}; + +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, + }, +}; + +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, + }, +}; + +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, + }, +}; + +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, + }, +}; + +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, + }, +}; + +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, + }, +}; + +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, + }, +}; + +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, + }, +}; + +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 }, + { 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 < 640 || mode->hdisplay > 1920) + return MODE_BAD_HVALUE; + + if (mode->vdisplay < 480 || mode->vdisplay > 1200) + 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); + +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, + 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; + + 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 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; + } + + /* 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(0x12, priv->io_base + _REG(ENCI_CFILT_CTRL)); + writel_relaxed(0x12, 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(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(0x26, 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, Y_Cb_Y_Cr select */ + writel_relaxed(0x4e01, 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(1, 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(BIT(14), BIT(14), + 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); + } + + writel_relaxed((use_enci ? 1 : 2) | + (mode->flags & DRM_MODE_FLAG_PHSYNC ? 1 << 2 : 0) | + (mode->flags & DRM_MODE_FLAG_PVSYNC ? 1 << 3 : 0) | + 4 << 5 | + (venc_repeat ? 1 << 8 : 0) | + (hdmi_repeat ? 1 << 12 : 0), + 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); + +void meson_venci_cvbs_mode_set(struct meson_drm *priv, + struct meson_cvbs_enci_mode *mode) +{ + if (mode->mode_tag == priv->venc.current_mode) + return; + + /* CVBS Filter settings */ + writel_relaxed(0x12, priv->io_base + _REG(ENCI_CFILT_CTRL)); + writel_relaxed(0x12, 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(0x8100 + 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(0x26, 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, Y_Cb_Y_Cr select */ + writel_relaxed(0x4e01, priv->io_base + _REG(ENCI_VFIFO2VD_CTL)); + + /* Power UP Dacs */ + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_SETTING)); + + /* Video Upsampling */ + writel_relaxed(0x0061, priv->io_base + _REG(VENC_UPSAMPLE_CTRL0)); + writel_relaxed(0x4061, priv->io_base + _REG(VENC_UPSAMPLE_CTRL1)); + writel_relaxed(0x5061, 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(0x2000, 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(1, 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(0x1, 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) +{ + writel_relaxed(2, 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 */ + 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(0x3, 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 000000000..97eaebbfa --- /dev/null +++ b/drivers/gpu/drm/meson/meson_venc.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * 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 + +enum { + MESON_VENC_MODE_NONE = 0, + MESON_VENC_MODE_CVBS_PAL, + MESON_VENC_MODE_CVBS_NTSC, + MESON_VENC_MODE_HDMI, +}; + +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; +}; + +/* 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, + 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_venc_cvbs.c b/drivers/gpu/drm/meson/meson_venc_cvbs.c new file mode 100644 index 000000000..e1760140e --- /dev/null +++ b/drivers/gpu/drm/meson/meson_venc_cvbs.c @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_graph.h> + +#include <drm/drmP.h> +#include <drm/drm_edid.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic_helper.h> + +#include "meson_venc_cvbs.h" +#include "meson_venc.h" +#include "meson_vclk.h" +#include "meson_registers.h" + +/* HHI VDAC Registers */ +#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ +#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ + +struct meson_venc_cvbs { + struct drm_encoder encoder; + struct drm_connector connector; + struct meson_drm *priv; +}; +#define encoder_to_meson_venc_cvbs(x) \ + container_of(x, struct meson_venc_cvbs, encoder) + +#define connector_to_meson_venc_cvbs(x) \ + container_of(x, struct meson_venc_cvbs, connector) + +/* 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), + .vrefresh = 50, + .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), + .vrefresh = 60, + .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; +} + +/* Connector */ + +static void meson_cvbs_connector_destroy(struct drm_connector *connector) +{ + drm_connector_cleanup(connector); +} + +static enum drm_connector_status +meson_cvbs_connector_detect(struct drm_connector *connector, bool force) +{ + /* FIXME: Add load-detect or jack-detect if possible */ + return connector_status_connected; +} + +static int meson_cvbs_connector_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + 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(dev, &meson_mode->mode); + if (!mode) { + DRM_ERROR("Failed to create a new display mode\n"); + return 0; + } + + drm_mode_probed_add(connector, mode); + } + + return i; +} + +static int meson_cvbs_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + /* Validate the modes added in get_modes */ + return MODE_OK; +} + +static const struct drm_connector_funcs meson_cvbs_connector_funcs = { + .detect = meson_cvbs_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = meson_cvbs_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const +struct drm_connector_helper_funcs meson_cvbs_connector_helper_funcs = { + .get_modes = meson_cvbs_connector_get_modes, + .mode_valid = meson_cvbs_connector_mode_valid, +}; + +/* Encoder */ + +static void meson_venc_cvbs_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs meson_venc_cvbs_encoder_funcs = { + .destroy = meson_venc_cvbs_encoder_destroy, +}; + +static int meson_venc_cvbs_encoder_atomic_check(struct drm_encoder *encoder, + 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_venc_cvbs_encoder_disable(struct drm_encoder *encoder) +{ + struct meson_venc_cvbs *meson_venc_cvbs = + encoder_to_meson_venc_cvbs(encoder); + struct meson_drm *priv = meson_venc_cvbs->priv; + + /* Disable CVBS VDAC */ + regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0); + regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8); +} + +static void meson_venc_cvbs_encoder_enable(struct drm_encoder *encoder) +{ + struct meson_venc_cvbs *meson_venc_cvbs = + encoder_to_meson_venc_cvbs(encoder); + struct meson_drm *priv = meson_venc_cvbs->priv; + + /* VDAC0 source is not from ATV */ + writel_bits_relaxed(BIT(5), 0, priv->io_base + _REG(VENC_VDAC_DACSEL0)); + + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + regmap_write(priv->hhi, HHI_VDAC_CNTL0, 1); + else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0xf0001); + + regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0); +} + +static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + const struct meson_cvbs_mode *meson_mode = meson_cvbs_get_mode(mode); + struct meson_venc_cvbs *meson_venc_cvbs = + encoder_to_meson_venc_cvbs(encoder); + struct meson_drm *priv = meson_venc_cvbs->priv; + + if (meson_mode) { + 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, true); + } +} + +static const struct drm_encoder_helper_funcs + meson_venc_cvbs_encoder_helper_funcs = { + .atomic_check = meson_venc_cvbs_encoder_atomic_check, + .disable = meson_venc_cvbs_encoder_disable, + .enable = meson_venc_cvbs_encoder_enable, + .mode_set = meson_venc_cvbs_encoder_mode_set, +}; + +static bool meson_venc_cvbs_connector_is_available(struct meson_drm *priv) +{ + struct device_node *remote; + + remote = of_graph_get_remote_node(priv->dev->of_node, 0, 0); + if (!remote) + return false; + + of_node_put(remote); + return true; +} + +int meson_venc_cvbs_create(struct meson_drm *priv) +{ + struct drm_device *drm = priv->drm; + struct meson_venc_cvbs *meson_venc_cvbs; + struct drm_connector *connector; + struct drm_encoder *encoder; + int ret; + + if (!meson_venc_cvbs_connector_is_available(priv)) { + dev_info(drm->dev, "CVBS Output connector not available\n"); + return 0; + } + + meson_venc_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_venc_cvbs), + GFP_KERNEL); + if (!meson_venc_cvbs) + return -ENOMEM; + + meson_venc_cvbs->priv = priv; + encoder = &meson_venc_cvbs->encoder; + connector = &meson_venc_cvbs->connector; + + /* Connector */ + + drm_connector_helper_add(connector, + &meson_cvbs_connector_helper_funcs); + + ret = drm_connector_init(drm, connector, &meson_cvbs_connector_funcs, + DRM_MODE_CONNECTOR_Composite); + if (ret) { + dev_err(priv->dev, "Failed to init CVBS connector\n"); + return ret; + } + + connector->interlace_allowed = 1; + + /* Encoder */ + + drm_encoder_helper_add(encoder, &meson_venc_cvbs_encoder_helper_funcs); + + ret = drm_encoder_init(drm, encoder, &meson_venc_cvbs_encoder_funcs, + DRM_MODE_ENCODER_TVDAC, "meson_venc_cvbs"); + if (ret) { + dev_err(priv->dev, "Failed to init CVBS encoder\n"); + return ret; + } + + encoder->possible_crtcs = BIT(0); + + drm_connector_attach_encoder(connector, encoder); + + return 0; +} diff --git a/drivers/gpu/drm/meson/meson_venc_cvbs.h b/drivers/gpu/drm/meson/meson_venc_cvbs.h new file mode 100644 index 000000000..9256ccf9d --- /dev/null +++ b/drivers/gpu/drm/meson/meson_venc_cvbs.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * 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_venc_cvbs_create(struct meson_drm *priv); + +#endif /* __MESON_VENC_CVBS_H */ diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c new file mode 100644 index 000000000..26a085787 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_viu.c @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <drm/drmP.h> +#include "meson_drv.h" +#include "meson_viu.h" +#include "meson_vpp.h" +#include "meson_venc.h" +#include "meson_canvas.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 */ +}; + +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 + +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); +} + +void meson_viu_init(struct meson_drm *priv) +{ + uint32_t reg; + + /* Disable OSDs */ + writel_bits_relaxed(BIT(0) | BIT(21), 0, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT)); + writel_bits_relaxed(BIT(0) | BIT(21), 0, + priv->io_base + _REG(VIU_OSD2_CTRL_STAT)); + + /* On GXL/GXM, Use the 10bit HDR conversion matrix */ + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + meson_viu_load_matrix(priv); + + /* Initialize OSD1 fifo control register */ + reg = BIT(0) | /* Urgent DDR request priority */ + (4 << 5) | /* hold_fifo_lines */ + (3 << 10) | /* burst length 64 */ + (32 << 12) | /* fifo_depth_val: 32*8=256 */ + (2 << 22) | /* 4 words in 1 burst */ + (2 << 24); + 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)); + + 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 000000000..073b1910b --- /dev/null +++ b/drivers/gpu/drm/meson/meson_viu.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Video Input Unit */ + +#ifndef __MESON_VIU_H +#define __MESON_VIU_H + +/* OSDx_BLKx_CFG */ +#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_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_BLK0_ENABLE BIT(0) + +#define OSD_GLOBAL_ALPHA_SHIFT 12 + +/* OSDx_CTRL_STAT2 */ +#define OSD_REPLACE_EN BIT(14) +#define OSD_REPLACE_SHIFT 6 + +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 000000000..27356f81a --- /dev/null +++ b/drivers/gpu/drm/meson/meson_vpp.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <drm/drmP.h> +#include "meson_drv.h" +#include "meson_vpp.h" +#include "meson_registers.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)); +} + +/* + * 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. + */ +void meson_vpp_setup_interlace_vscaler_osd1(struct meson_drm *priv, + struct drm_rect *input) +{ + writel_relaxed(BIT(3) /* Enable scaler */ | + BIT(2), /* Select OSD1 */ + priv->io_base + _REG(VPP_OSD_SC_CTRL0)); + + writel_relaxed(((drm_rect_width(input) - 1) << 16) | + (drm_rect_height(input) - 1), + priv->io_base + _REG(VPP_OSD_SCI_WH_M1)); + /* 2:1 scaling */ + writel_relaxed(((input->x1) << 16) | (input->x2), + priv->io_base + _REG(VPP_OSD_SCO_H_START_END)); + writel_relaxed(((input->y1 >> 1) << 16) | (input->y2 >> 1), + priv->io_base + _REG(VPP_OSD_SCO_V_START_END)); + + /* 2:1 scaling values */ + writel_relaxed(BIT(16), priv->io_base + _REG(VPP_OSD_VSC_INI_PHASE)); + writel_relaxed(BIT(25), priv->io_base + _REG(VPP_OSD_VSC_PHASE_STEP)); + + writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); + + writel_relaxed((4 << 0) /* osd_vsc_bank_length */ | + (4 << 3) /* osd_vsc_top_ini_rcv_num0 */ | + (1 << 8) /* osd_vsc_top_rpt_p0_num0 */ | + (6 << 11) /* osd_vsc_bot_ini_rcv_num0 */ | + (2 << 16) /* osd_vsc_bot_rpt_p0_num0 */ | + BIT(23) /* osd_prog_interlace */ | + BIT(24), /* Enable vertical scaler */ + priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); +} + +void meson_vpp_disable_interlace_vscaler_osd1(struct meson_drm *priv) +{ + 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)); +} + +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 ? BIT(8) : 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)); +} + +void meson_vpp_init(struct meson_drm *priv) +{ + /* set dummy data default YUV black */ + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + writel_relaxed(0x108080, priv->io_base + _REG(VPP_DUMMY_DATA1)); + else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu")) { + writel_bits_relaxed(0xff << 16, 0xff << 16, + priv->io_base + _REG(VIU_MISC_CTRL1)); + writel_relaxed(0x20000, priv->io_base + _REG(VPP_DOLBY_CTRL)); + writel_relaxed(0x1020080, + priv->io_base + _REG(VPP_DUMMY_DATA1)); + } + + /* Initialize vpu fifo control registers */ + writel_relaxed(readl_relaxed(priv->io_base + _REG(VPP_OFIFO_SIZE)) | + 0x77f, priv->io_base + _REG(VPP_OFIFO_SIZE)); + writel_relaxed(0x08080808, priv->io_base + _REG(VPP_HOLD_LINES)); + + /* 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, 0, + priv->io_base + _REG(VPP_MISC)); + + /* 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)); + + /* 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); +} diff --git a/drivers/gpu/drm/meson/meson_vpp.h b/drivers/gpu/drm/meson/meson_vpp.h new file mode 100644 index 000000000..815177cc7 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_vpp.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Video Post Process */ + +#ifndef __MESON_VPP_H +#define __MESON_VPP_H + +/* 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 */ |