source "drivers/gpu/drm/exynos/Kconfig"
+source "drivers/gpu/drm/rockchip/Kconfig"
+
source "drivers/gpu/drm/vmwgfx/Kconfig"
source "drivers/gpu/drm/gma500/Kconfig"
obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
+obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
obj-$(CONFIG_DRM_GMA500) += gma500/
obj-$(CONFIG_DRM_UDL) += udl/
obj-$(CONFIG_DRM_AST) += ast/
ret = -ENOSPC;
goto out;
}
-
+ fb->pixel_format = crtc->fb->pixel_format;
if (crtc->fb->pixel_format != fb->pixel_format) {
DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
ret = -EINVAL;
#include <drm/drmP.h>
#include <drm/drm_core.h>
-unsigned int drm_debug = 0; /* 1 to enable debug output */
+unsigned int drm_debug = 0xf; /* 1 to enable debug output */
EXPORT_SYMBOL(drm_debug);
unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */
--- /dev/null
+config DRM_ROCKCHIP
+ tristate "DRM Support for ROCKCHIP "
+ depends on DRM && ARCH_MULTIPLATFORM
+ select DRM_KMS_HELPER
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+ help
+ Choose this option if you have a ROCKCHIP soc chipset.
+ If M is selected the module will be called rockchipdrm.
+
+config DRM_ROCKCHIP_IOMMU
+ bool "ROCKCHIP DRM IOMMU Support"
+ depends on DRM_ROCKCHIP && ARM_DMA_USE_IOMMU
+ help
+ Choose this option if you want to use IOMMU feature for DRM.
+
+config DRM_ROCKCHIP_DMABUF
+ bool "ROCKCHIP DRM DMABUF"
+ depends on DRM_ROCKCHIP
+ help
+ Choose this option if you want to use DMABUF feature for DRM.
+
+config DRM_RK3188_FIMD
+ bool "RK3188 DRM FIMD"
+ depends on OF && DRM_ROCKCHIP
+ select FB_MODE_HELPERS
+ select VIDEOMODE_HELPERS
+ help
+ Choose this option if you want to use Rockchip FIMD for DRM.
+
+config DRM_RK3288_FIMD
+ bool "RK3288 DRM FIMD"
+ depends on OF && DRM_ROCKCHIP
+ select FB_MODE_HELPERS
+ select VIDEOMODE_HELPERS
+ help
+ Choose this option if you want to use Rockchip FIMD for DRM.
+
+
+config DRM_ROCKCHIP_HDMI
+ bool "Rockchip DRM HDMI"
+ depends on DRM_ROCKCHIP
+ help
+ Choose this option if you want to use Rockchip HDMI for DRM.
+
+config DRM_ROCKCHIP_VIDI
+ bool "Rockchip DRM Virtual Display"
+ depends on DRM_ROCKCHIP
+ help
+ Choose this option if you want to use rockchip VIDI for DRM.
+
+source "drivers/gpu/drm/rockchip/screen/Kconfig"
--- /dev/null
+#
+# Makefile for the drm device driver. This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
+rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_encoder.o rockchip_drm_connector.o \
+ rockchip_drm_crtc.o rockchip_drm_fbdev.o rockchip_drm_fb.o \
+ rockchip_drm_buf.o rockchip_drm_gem.o rockchip_drm_core.o \
+ rockchip_drm_plane.o
+
+rockchipdrm-$(CONFIG_DRM_ROCKCHIP_IOMMU) += rockchip_drm_iommu.o
+rockchipdrm-$(CONFIG_DRM_ROCKCHIP_DMABUF) += rockchip_drm_dmabuf.o
+rockchipdrm-$(CONFIG_DRM_RK3188_FIMD) += rk3188_drm_fimd.o
+rockchipdrm-$(CONFIG_DRM_RK3288_FIMD) += rk3288_drm_fimd.o
+rockchipdrm-$(CONFIG_DRM_ROCKCHIP_HDMI) += rockchip_hdmi.o rockchip_mixer.o \
+ rockchip_ddc.o rockchip_hdmiphy.o \
+ rockchip_drm_hdmi.o
+rockchipdrm-$(CONFIG_DRM_ROCKCHIP_VIDI) += rockchip_drm_vidi.o
+
+obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
+obj-y += screen/
+obj-y += transmitter/
--- /dev/null
+/*
+ * rk3188_drm_fimd.c
+ *
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <drm/drmP.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+
+#include <video/of_display_timing.h>
+#include <drm/rockchip_drm.h>
+#include <linux/rockchip/cpu.h>
+#include <linux/rk_fb.h>
+#include <video/display_timing.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_crtc.h"
+#include "rockchip_drm_iommu.h"
+
+/*
+ * FIMD is stand for Fully Interactive Mobile Display and
+ * as a display controller, it transfers contents drawn on memory
+ * to a LCD Panel through Display Interfaces such as RGB or
+ * CPU Interface.
+ */
+
+/* position control register for hardware window 0, 2 ~ 4.*/
+#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16)
+#define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16)
+/*
+ * size control register for hardware windows 0 and alpha control register
+ * for hardware windows 1 ~ 4
+ */
+#define VIDOSD_C(win) (VIDOSD_BASE + 0x08 + (win) * 16)
+/* size control register for hardware windows 1 ~ 2. */
+#define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16)
+
+#define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8)
+#define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8)
+#define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4)
+
+/* color key control register for hardware window 1 ~ 4. */
+#define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + ((x - 1) * 8))
+/* color key value register for hardware window 1 ~ 4. */
+#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8))
+
+/* FIMD has totally five hardware windows. */
+#define WINDOWS_NR 4
+
+#define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev))
+
+struct fimd_driver_data {
+ unsigned int timing_base;
+};
+
+static struct fimd_driver_data rockchip4_fimd_driver_data = {
+ .timing_base = 0x0,
+};
+
+static struct fimd_driver_data rockchip5_fimd_driver_data = {
+ .timing_base = 0x20000,
+};
+
+struct fimd_win_data {
+ unsigned int offset_x;
+ unsigned int offset_y;
+ unsigned int ovl_width;
+ unsigned int ovl_height;
+ unsigned int fb_width;
+ unsigned int fb_height;
+ unsigned int bpp;
+ dma_addr_t dma_addr;
+ unsigned int buf_offsize;
+ unsigned int line_size; /* bytes */
+ bool enabled;
+ bool resume;
+};
+
+struct fimd_context {
+ struct rockchip_drm_subdrv subdrv;
+ int irq;
+ struct drm_crtc *crtc;
+ struct clk *pd; //lcdc power domain
+ struct clk *hclk; //lcdc AHP clk
+ struct clk *dclk; //lcdc dclk
+ struct clk *aclk; //lcdc share memory frequency
+ void __iomem *regs;
+ struct fimd_win_data win_data[WINDOWS_NR];
+ unsigned int clkdiv;
+ unsigned int default_win;
+ unsigned long irq_flags;
+ u32 vidcon0;
+ u32 vidcon1;
+ int lcdc_id;
+ bool suspended;
+ struct mutex lock;
+ wait_queue_head_t wait_vsync_queue;
+ atomic_t wait_vsync_event;
+
+ int clkon;
+ void *regsbak; //back up reg
+ struct rockchip_drm_panel_info *panel;
+ struct rk_screen *screen;
+};
+
+#include "rk3188_drm_fimd.h"
+static int rk3188_lcdc_get_id(u32 phy_base)
+{
+ if (cpu_is_rk319x()) {
+ if (phy_base == 0xffc40000)
+ return 0;
+ else if (phy_base == 0xffc50000)
+ return 1;
+ else
+ return -EINVAL;
+ } else if (cpu_is_rk3188()) {
+ if (phy_base == 0x1010c000)
+ return 0;
+ else if (phy_base == 0x1010e000)
+ return 1;
+ else
+ return -EINVAL;
+ } else if (cpu_is_rk3026()) {
+ if (phy_base == 0x1010e000)
+ return 0;
+ else if (phy_base == 0x01110000)
+ return 1;
+ else
+ return -EINVAL;
+ } else {
+ pr_err("un supported platform \n");
+ return -EINVAL;
+ }
+
+}
+
+
+static bool fimd_display_is_connected(struct device *dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+
+ return true;
+}
+
+static void *fimd_get_panel(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ return ctx->panel;
+}
+
+static int fimd_check_timing(struct device *dev, void *timing)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+
+ return 0;
+}
+
+static int fimd_display_power_on(struct device *dev, int mode)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO */
+
+ return 0;
+}
+
+static struct rockchip_drm_display_ops fimd_display_ops = {
+ .type = ROCKCHIP_DISPLAY_TYPE_LCD,
+ .is_connected = fimd_display_is_connected,
+ .get_panel = fimd_get_panel,
+ .check_timing = fimd_check_timing,
+ .power_on = fimd_display_power_on,
+};
+
+static void fimd_dpms(struct device *subdrv_dev, int mode)
+{
+ struct fimd_context *ctx = get_fimd_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
+
+ mutex_lock(&ctx->lock);
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ /*
+ * enable fimd hardware only if suspended status.
+ *
+ * P.S. fimd_dpms function would be called at booting time so
+ * clk_enable could be called double time.
+ */
+ if (ctx->suspended)
+ pm_runtime_get_sync(subdrv_dev);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ if (!ctx->suspended)
+ pm_runtime_put_sync(subdrv_dev);
+ break;
+ default:
+ DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+ break;
+ }
+
+ mutex_unlock(&ctx->lock);
+}
+
+static void fimd_apply(struct device *subdrv_dev)
+{
+ struct fimd_context *ctx = get_fimd_context(subdrv_dev);
+ struct rockchip_drm_manager *mgr = ctx->subdrv.manager;
+ struct rockchip_drm_manager_ops *mgr_ops = mgr->ops;
+ struct rockchip_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
+ struct fimd_win_data *win_data;
+ int i;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ for (i = 0; i < WINDOWS_NR; i++) {
+ win_data = &ctx->win_data[i];
+ if (win_data->enabled && (ovl_ops && ovl_ops->commit))
+ ovl_ops->commit(subdrv_dev, i);
+ }
+
+ if (mgr_ops && mgr_ops->commit)
+ mgr_ops->commit(subdrv_dev);
+}
+
+static void fimd_commit(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct rockchip_drm_panel_info *panel = ctx->panel;
+ struct rk_screen *screen = ctx->screen;
+ u16 right_margin = screen->mode.right_margin;
+ u16 left_margin = screen->mode.left_margin;
+ u16 lower_margin = screen->mode.lower_margin;
+ u16 upper_margin = screen->mode.upper_margin;
+ u16 x_res = screen->mode.xres;
+ u16 y_res = screen->mode.yres;
+ u32 mask, val;
+ int face;
+
+ printk(KERN_ERR"%s %d\n", __func__,__LINE__);
+ if (ctx->suspended)
+ return;
+
+ printk(KERN_ERR"%s %d\n", __func__,__LINE__);
+ if(!ctx->clkon)
+ return;
+#if 1
+ switch (screen->face) {
+ case OUT_P565:
+ face = OUT_P565;
+ mask = m_DITHER_DOWN_EN | m_DITHER_DOWN_MODE |
+ m_DITHER_DOWN_SEL;
+ val = v_DITHER_DOWN_EN(1) | v_DITHER_DOWN_MODE(0) |
+ v_DITHER_DOWN_SEL(1);
+ lcdc_msk_reg(ctx, DSP_CTRL0, mask, val);
+ break;
+ case OUT_P666:
+ face = OUT_P666;
+ mask = m_DITHER_DOWN_EN | m_DITHER_DOWN_MODE |
+ m_DITHER_DOWN_SEL;
+ val = v_DITHER_DOWN_EN(1) | v_DITHER_DOWN_MODE(1) |
+ v_DITHER_DOWN_SEL(1);
+ lcdc_msk_reg(ctx, DSP_CTRL0, mask, val);
+ break;
+ case OUT_D888_P565:
+ face = OUT_P888;
+ mask = m_DITHER_DOWN_EN | m_DITHER_DOWN_MODE |
+ m_DITHER_DOWN_SEL;
+ val = v_DITHER_DOWN_EN(1) | v_DITHER_DOWN_MODE(0) |
+ v_DITHER_DOWN_SEL(1);
+ lcdc_msk_reg(ctx, DSP_CTRL0, mask, val);
+ break;
+ case OUT_D888_P666:
+ face = OUT_P888;
+ mask = m_DITHER_DOWN_EN | m_DITHER_DOWN_MODE |
+ m_DITHER_DOWN_SEL;
+ val = v_DITHER_DOWN_EN(1) | v_DITHER_DOWN_MODE(1) |
+ v_DITHER_DOWN_SEL(1);
+ lcdc_msk_reg(ctx, DSP_CTRL0, mask, val);
+ break;
+ case OUT_P888:
+ face = OUT_P888;
+ mask = m_DITHER_DOWN_EN | m_DITHER_UP_EN;
+ val = v_DITHER_DOWN_EN(0) | v_DITHER_UP_EN(0);
+ lcdc_msk_reg(ctx, DSP_CTRL0, mask, val);
+ break;
+ default:
+ printk( "un supported interface!\n");
+ break;
+ }
+ mask = m_DSP_OUT_FORMAT | m_HSYNC_POL | m_VSYNC_POL |
+ m_DEN_POL | m_DCLK_POL;
+ val = v_DSP_OUT_FORMAT(face) | v_HSYNC_POL(screen->pin_hsync) |
+ v_VSYNC_POL(screen->pin_vsync) | v_DEN_POL(screen->pin_den) |
+ v_DCLK_POL(screen->pin_dclk);
+ lcdc_msk_reg(ctx, DSP_CTRL0, mask, val);
+
+ mask = m_BG_COLOR | m_DSP_BG_SWAP | m_DSP_RB_SWAP |
+ m_DSP_RG_SWAP | m_DSP_DELTA_SWAP |
+ m_DSP_DUMMY_SWAP | m_BLANK_EN;
+ val = v_BG_COLOR(0x000000) | v_DSP_BG_SWAP(screen->swap_gb) |
+ v_DSP_RB_SWAP(screen->swap_rb) | v_DSP_RG_SWAP(screen->
+ swap_rg) |
+ v_DSP_DELTA_SWAP(screen->
+ swap_delta) | v_DSP_DUMMY_SWAP(screen->
+ swap_dumy) |
+ v_BLANK_EN(0) | v_BLACK_EN(0);
+ lcdc_msk_reg(ctx, DSP_CTRL1, mask, val);
+ val =
+ v_HSYNC(screen->mode.hsync_len) | v_HORPRD(screen->mode.
+ hsync_len +
+ left_margin +
+ x_res +
+ right_margin);
+ lcdc_writel(ctx, DSP_HTOTAL_HS_END, val);
+ val = v_HAEP(screen->mode.hsync_len + left_margin + x_res) |
+ v_HASP(screen->mode.hsync_len + left_margin);
+ lcdc_writel(ctx, DSP_HACT_ST_END, val);
+
+ val =
+ v_VSYNC(screen->mode.vsync_len) | v_VERPRD(screen->mode.
+ vsync_len +
+ upper_margin +
+ y_res +
+ lower_margin);
+ lcdc_writel(ctx, DSP_VTOTAL_VS_END, val);
+
+ val = v_VAEP(screen->mode.vsync_len + upper_margin + y_res) |
+ v_VASP(screen->mode.vsync_len + screen->mode.upper_margin);
+ lcdc_writel(ctx, DSP_VACT_ST_END, val);
+
+ printk(KERN_ERR"------>yzq %d SYS_CTRL=%x \n",__LINE__,lcdc_readl(ctx,SYS_CTRL));
+ lcdc_msk_reg(ctx, SYS_CTRL, m_LCDC_STANDBY,
+ v_LCDC_STANDBY(0));
+ printk(KERN_ERR"------>yzq %d SYS_CTRL=%x \n",__LINE__,lcdc_readl(ctx,SYS_CTRL));
+ lcdc_msk_reg(ctx, SYS_CTRL,
+ m_WIN0_EN | m_WIN1_EN | m_WIN0_RB_SWAP |
+ m_WIN1_RB_SWAP,
+ v_WIN0_EN(1) | v_WIN1_EN(0) |
+ v_WIN0_RB_SWAP(screen->swap_rb) |
+ v_WIN1_RB_SWAP(screen->swap_rb));
+ lcdc_cfg_done(ctx);
+ printk(KERN_ERR"------>yzq %d SYS_CTRL=%x \n",__LINE__,lcdc_readl(ctx,SYS_CTRL));
+#endif
+}
+
+static int fimd_enable_vblank(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ u32 val,mask;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ctx->suspended)
+ return -EPERM;
+
+ if (!test_and_set_bit(0, &ctx->irq_flags)) {
+#if 1
+ mask = m_HS_INT_CLEAR | m_HS_INT_EN | m_FS_INT_CLEAR |
+ m_FS_INT_EN | m_LF_INT_EN | m_LF_INT_CLEAR |
+ m_LF_INT_NUM | m_BUS_ERR_INT_CLEAR | m_BUS_ERR_INT_EN;
+ val = v_FS_INT_CLEAR(1) | v_FS_INT_EN(1) | v_HS_INT_CLEAR(0) |
+ v_HS_INT_EN(0) | v_LF_INT_CLEAR(0) | v_LF_INT_EN(0);
+ lcdc_msk_reg(ctx, INT_STATUS, mask, val);
+ lcdc_cfg_done(ctx);
+#endif
+ }
+
+ return 0;
+}
+
+static void fimd_disable_vblank(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ u32 val,mask;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ctx->suspended)
+ return;
+
+ if (test_and_clear_bit(0, &ctx->irq_flags)) {
+#if 1
+ lcdc_msk_reg(ctx, INT_STATUS, m_FS_INT_CLEAR,
+ v_FS_INT_CLEAR(1));
+ mask = m_HS_INT_EN | m_FS_INT_EN | m_LF_INT_EN |
+ m_BUS_ERR_INT_EN;
+ val = v_HS_INT_EN(0) | v_FS_INT_EN(0) |
+ v_LF_INT_EN(0) | v_BUS_ERR_INT_EN(0);
+ lcdc_msk_reg(ctx, INT_STATUS, mask, val);
+#endif
+
+ }
+}
+
+static void fimd_wait_for_vblank(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+
+ if (ctx->suspended)
+ return;
+
+ atomic_set(&ctx->wait_vsync_event, 1);
+
+ /*
+ * wait for FIMD to signal VSYNC interrupt or return after
+ * timeout which is set to 50ms (refresh rate of 20).
+ */
+ if (!wait_event_timeout(ctx->wait_vsync_queue,
+ !atomic_read(&ctx->wait_vsync_event),
+ DRM_HZ/20))
+ DRM_DEBUG_KMS("vblank wait timed out.\n");
+}
+
+static struct rockchip_drm_manager_ops fimd_manager_ops = {
+ .dpms = fimd_dpms,
+ .apply = fimd_apply,
+ .commit = fimd_commit,
+ .enable_vblank = fimd_enable_vblank,
+ .disable_vblank = fimd_disable_vblank,
+ .wait_for_vblank = fimd_wait_for_vblank,
+};
+
+static void fimd_win_mode_set(struct device *dev,
+ struct rockchip_drm_overlay *overlay)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data;
+ int win;
+ unsigned long offset;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!overlay) {
+ dev_err(dev, "overlay is NULL\n");
+ return;
+ }
+
+ win = overlay->zpos;
+ if (win == DEFAULT_ZPOS)
+ win = ctx->default_win;
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ offset = overlay->fb_x * (overlay->bpp >> 3);
+ offset += overlay->fb_y * overlay->pitch;
+
+ DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch);
+
+ win_data = &ctx->win_data[win];
+
+ win_data->offset_x = overlay->crtc_x;
+ win_data->offset_y = overlay->crtc_y;
+ win_data->ovl_width = overlay->crtc_width;
+ win_data->ovl_height = overlay->crtc_height;
+ win_data->fb_width = overlay->fb_width;
+ win_data->fb_height = overlay->fb_height;
+ win_data->dma_addr = overlay->dma_addr[0] + offset;
+ win_data->bpp = overlay->bpp;
+ win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
+ (overlay->bpp >> 3);
+ win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3);
+
+ DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
+ win_data->offset_x, win_data->offset_y);
+ DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
+ win_data->ovl_width, win_data->ovl_height);
+ DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr);
+ DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
+ overlay->fb_width, overlay->crtc_width);
+}
+
+static void fimd_win_set_pixfmt(struct device *dev, unsigned int win)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data = &ctx->win_data[win];
+ u8 fmt_cfg = 0;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+ switch(win_data->bpp){
+ case 32:
+ fmt_cfg = 0;
+ break;
+ case 24:
+ fmt_cfg = 1;
+ break;
+ case 16:
+ fmt_cfg = 2;
+ break;
+ default:
+ printk("not support format %d\n",win_data->bpp);
+ break;
+ }
+
+
+ printk(KERN_ERR"------>yzq %d SYS_CTRL=%x \n",__LINE__,lcdc_readl(ctx,SYS_CTRL));
+ lcdc_msk_reg(ctx , SYS_CTRL, m_WIN0_FORMAT, v_WIN0_FORMAT(fmt_cfg));
+ printk(KERN_ERR"------>yzq %d SYS_CTRL=%x \n",__LINE__,lcdc_readl(ctx,SYS_CTRL));
+}
+
+static void fimd_win_set_colkey(struct device *dev, unsigned int win)
+{
+// struct fimd_context *ctx = get_fimd_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+}
+
+static void fimd_win_commit(struct device *dev, int zpos)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data;
+ struct rk_screen *screen = ctx->screen;
+ int win = zpos;
+ unsigned long val, size;
+ u32 xpos, ypos;
+
+ printk(KERN_ERR"%s %d\n", __func__,__LINE__);
+
+ if (ctx->suspended)
+ return;
+
+ if (!ctx->clkon)
+ return;
+ printk(KERN_ERR"%s %d\n", __func__,__LINE__);
+ if (win == DEFAULT_ZPOS)
+ win = ctx->default_win;
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ win_data = &ctx->win_data[win];
+#if 1
+ /*
+ * SHADOWCON register is used for enabling timing.
+ *
+ * for example, once only width value of a register is set,
+ * if the dma is started then fimd hardware could malfunction so
+ * with protect window setting, the register fields with prefix '_F'
+ * wouldn't be updated at vsync also but updated once unprotect window
+ * is set.
+ */
+
+ /* buffer end address */
+ printk(KERN_ERR"%s %d\n", __func__,__LINE__);
+ size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3);
+ val = (unsigned long)(win_data->dma_addr + size);
+ printk(KERN_ERR"%s %d\n", __func__,__LINE__);
+ printk(KERN_ERR"ctx->regs=%x\n", __func__,__LINE__,ctx->regs);
+ printk(KERN_ERR"-->yzq dma_addr=%x buf_offsize=%x win_data->fb_width=%d \nwin_data->fb_height=%d win_data->ovl_height=%d win_data->ovl_width=%d \n win_data->offset_x=%d win_data->offset_y=%d win_data->line_size=%d\n win_data->bpp=%d ",win_data->dma_addr,win_data->buf_offsize,win_data->fb_width,win_data->fb_height,win_data->ovl_height, win_data->ovl_width,win_data->offset_x,win_data->offset_y,win_data->line_size,win_data->bpp);
+ xpos = win_data->offset_x + screen->mode.left_margin + screen->mode.hsync_len;
+ ypos = win_data->offset_y + screen->mode.upper_margin + screen->mode.vsync_len;
+
+ lcdc_writel(ctx, WIN0_YRGB_MST0, win_data->dma_addr +win_data->buf_offsize );
+ //lcdc_writel(ctx, WIN0_CBR_MST0, win0->area[0].uv_addr);
+
+// lcdc_writel(ctx, WIN0_SCL_FACTOR_YRGB,
+// v_X_SCL_FACTOR(win0->scale_yrgb_x) |
+// v_Y_SCL_FACTOR(win0->scale_yrgb_y));
+// lcdc_writel(ctx, WIN0_SCL_FACTOR_CBR,
+// v_X_SCL_FACTOR(win0->scale_cbcr_x) |
+// v_Y_SCL_FACTOR(win0->scale_cbcr_y));
+ lcdc_writel(ctx, WIN0_ACT_INFO, v_ACT_WIDTH(win_data->ovl_width) |
+ v_ACT_HEIGHT(win_data->ovl_height));
+ lcdc_writel(ctx, WIN0_DSP_ST, v_DSP_STX(xpos) |
+ v_DSP_STY(ypos));
+ lcdc_writel(ctx, WIN0_DSP_INFO, v_DSP_WIDTH(win_data->fb_width) |
+ v_DSP_HEIGHT(win_data->fb_height));
+ lcdc_msk_reg(ctx, WIN_VIR, m_WIN0_VIR,
+ v_WIN0_VIR_VAL(win_data->line_size/(win_data->bpp>>3)));
+ lcdc_cfg_done(ctx);
+
+#if 0
+ /* protect windows */
+ val = readl(ctx->regs + SHADOWCON);
+ val |= SHADOWCON_WINx_PROTECT(win);
+ writel(val, ctx->regs + SHADOWCON);
+
+ /* buffer start address */
+ val = (unsigned long)win_data->dma_addr;
+ writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
+
+
+ DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
+ (unsigned long)win_data->dma_addr, val, size);
+ DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
+ win_data->ovl_width, win_data->ovl_height);
+
+ /* buffer size */
+ val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) |
+ VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size) |
+ VIDW_BUF_SIZE_OFFSET_E(win_data->buf_offsize) |
+ VIDW_BUF_SIZE_PAGEWIDTH_E(win_data->line_size);
+ writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
+
+ /* OSD position */
+ val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) |
+ VIDOSDxA_TOPLEFT_Y(win_data->offset_y) |
+ VIDOSDxA_TOPLEFT_X_E(win_data->offset_x) |
+ VIDOSDxA_TOPLEFT_Y_E(win_data->offset_y);
+ writel(val, ctx->regs + VIDOSD_A(win));
+
+ last_x = win_data->offset_x + win_data->ovl_width;
+ if (last_x)
+ last_x--;
+ last_y = win_data->offset_y + win_data->ovl_height;
+ if (last_y)
+ last_y--;
+
+ val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) |
+ VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y);
+
+ writel(val, ctx->regs + VIDOSD_B(win));
+
+ DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
+ win_data->offset_x, win_data->offset_y, last_x, last_y);
+
+ /* hardware window 0 doesn't support alpha channel. */
+ if (win != 0) {
+ /* OSD alpha */
+ alpha = VIDISD14C_ALPHA1_R(0xf) |
+ VIDISD14C_ALPHA1_G(0xf) |
+ VIDISD14C_ALPHA1_B(0xf);
+
+ writel(alpha, ctx->regs + VIDOSD_C(win));
+ }
+
+ /* OSD size */
+ if (win != 3 && win != 4) {
+ u32 offset = VIDOSD_D(win);
+ if (win == 0)
+ offset = VIDOSD_C(win);
+ val = win_data->ovl_width * win_data->ovl_height;
+ writel(val, ctx->regs + offset);
+
+ DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
+ }
+
+ fimd_win_set_pixfmt(dev, win);
+
+ /* hardware window 0 doesn't support color key. */
+ if (win != 0)
+ fimd_win_set_colkey(dev, win);
+
+ /* wincon */
+ val = readl(ctx->regs + WINCON(win));
+ val |= WINCONx_ENWIN;
+ writel(val, ctx->regs + WINCON(win));
+
+ /* Enable DMA channel and unprotect windows */
+ val = readl(ctx->regs + SHADOWCON);
+ val |= SHADOWCON_CHx_ENABLE(win);
+ val &= ~SHADOWCON_WINx_PROTECT(win);
+ writel(val, ctx->regs + SHADOWCON);
+#endif
+#endif
+ win_data->enabled = true;
+
+}
+
+static void fimd_win_disable(struct device *dev, int zpos)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data;
+ int win = zpos;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (win == DEFAULT_ZPOS)
+ win = ctx->default_win;
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ win_data = &ctx->win_data[win];
+
+ if (ctx->suspended) {
+ /* do not resume this window*/
+ win_data->resume = false;
+ return;
+ }
+
+ win_data->enabled = false;
+}
+
+static struct rockchip_drm_overlay_ops fimd_overlay_ops = {
+ .mode_set = fimd_win_mode_set,
+ .commit = fimd_win_commit,
+ .disable = fimd_win_disable,
+};
+
+static struct rockchip_drm_manager fimd_manager = {
+ .pipe = -1,
+ .ops = &fimd_manager_ops,
+ .overlay_ops = &fimd_overlay_ops,
+ .display_ops = &fimd_display_ops,
+};
+
+static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
+{
+ struct fimd_context *ctx = (struct fimd_context *)dev_id;
+ struct rockchip_drm_subdrv *subdrv = &ctx->subdrv;
+ struct drm_device *drm_dev = subdrv->drm_dev;
+ struct rockchip_drm_manager *manager = subdrv->manager;
+ u32 int_reg = lcdc_readl(ctx, INT_STATUS);
+
+ if (int_reg & m_FS_INT_STA) {
+ lcdc_msk_reg(ctx, INT_STATUS, m_FS_INT_CLEAR,
+ v_FS_INT_CLEAR(1));
+ }
+
+ /* check the crtc is detached already from encoder */
+ if (manager->pipe < 0)
+ goto out;
+
+ drm_handle_vblank(drm_dev, manager->pipe);
+ rockchip_drm_crtc_finish_pageflip(drm_dev, manager->pipe);
+
+ /* set wait vsync event to zero and wake up queue. */
+ if (atomic_read(&ctx->wait_vsync_event)) {
+ atomic_set(&ctx->wait_vsync_event, 0);
+ DRM_WAKEUP(&ctx->wait_vsync_queue);
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * enable drm irq mode.
+ * - with irq_enabled = 1, we can use the vblank feature.
+ *
+ * P.S. note that we wouldn't use drm irq handler but
+ * just specific driver own one instead because
+ * drm framework supports only one irq handler.
+ */
+ drm_dev->irq_enabled = 1;
+
+ /*
+ * with vblank_disable_allowed = 1, vblank interrupt will be disabled
+ * by drm timer once a current process gives up ownership of
+ * vblank event.(after drm_vblank_put function is called)
+ */
+ drm_dev->vblank_disable_allowed = 1;
+
+ /* attach this sub driver to iommu mapping if supported. */
+ if (is_drm_iommu_supported(drm_dev))
+ drm_iommu_attach_device(drm_dev, dev);
+
+ return 0;
+}
+
+static void fimd_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* detach this sub driver from iommu mapping if supported. */
+ if (is_drm_iommu_supported(drm_dev))
+ drm_iommu_detach_device(drm_dev, dev);
+}
+
+
+static void fimd_clear_win(struct fimd_context *ctx, int win)
+{
+ u32 val;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+}
+
+static int fimd_clock(struct fimd_context *ctx, bool enable)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+printk(KERN_ERR"---->yzq %s %d \n",__func__,__LINE__);
+ if (enable) {
+ if(ctx->clkon)
+ return 0;
+ int ret;
+
+ ret = clk_prepare_enable(ctx->hclk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(ctx->dclk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(ctx->aclk);
+ if (ret < 0)
+ return ret;
+ ctx->clkon=1;
+ } else {
+ if(!ctx->clkon)
+ return 0;
+ clk_disable_unprepare(ctx->aclk);
+ clk_disable_unprepare(ctx->dclk);
+ clk_disable_unprepare(ctx->hclk);
+ ctx->clkon=0;
+ }
+
+ return 0;
+}
+
+static void fimd_window_suspend(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data;
+ int i;
+
+ for (i = 0; i < WINDOWS_NR; i++) {
+ win_data = &ctx->win_data[i];
+ win_data->resume = win_data->enabled;
+ fimd_win_disable(dev, i);
+ }
+ fimd_wait_for_vblank(dev);
+}
+
+static void fimd_window_resume(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data;
+ int i;
+
+ for (i = 0; i < WINDOWS_NR; i++) {
+ win_data = &ctx->win_data[i];
+ win_data->enabled = win_data->resume;
+ win_data->resume = false;
+ }
+}
+
+static int fimd_activate(struct fimd_context *ctx, bool enable)
+{
+ struct device *dev = ctx->subdrv.dev;
+ if (enable) {
+ int ret;
+
+ ret = fimd_clock(ctx, true);
+ if (ret < 0)
+ return ret;
+
+ ctx->suspended = false;
+
+ /* if vblank was enabled status, enable it again. */
+ if (test_and_clear_bit(0, &ctx->irq_flags))
+ fimd_enable_vblank(dev);
+
+ fimd_window_resume(dev);
+ } else {
+ fimd_window_suspend(dev);
+
+ fimd_clock(ctx, false);
+ ctx->suspended = true;
+ }
+
+ return 0;
+}
+
+int rk_fb_video_mode_from_timing(const struct display_timing *dt,
+ struct rk_screen *screen)
+{
+ screen->mode.pixclock = dt->pixelclock.typ;
+ screen->mode.left_margin = dt->hback_porch.typ;
+ screen->mode.right_margin = dt->hfront_porch.typ;
+ screen->mode.xres = dt->hactive.typ;
+ screen->mode.hsync_len = dt->hsync_len.typ;
+ screen->mode.upper_margin = dt->vback_porch.typ;
+ screen->mode.lower_margin = dt->vfront_porch.typ;
+ screen->mode.yres = dt->vactive.typ;
+ screen->mode.vsync_len = dt->vsync_len.typ;
+ screen->type = dt->screen_type;
+ screen->lvds_format = dt->lvds_format;
+ screen->face = dt->face;
+
+ if (dt->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
+ screen->pin_dclk = 1;
+ else
+ screen->pin_dclk = 0;
+ if(dt->flags & DISPLAY_FLAGS_HSYNC_HIGH)
+ screen->pin_hsync = 1;
+ else
+ screen->pin_hsync = 0;
+ if(dt->flags & DISPLAY_FLAGS_VSYNC_HIGH)
+ screen->pin_vsync = 1;
+ else
+ screen->pin_vsync = 0;
+ if(dt->flags & DISPLAY_FLAGS_DE_HIGH)
+ screen->pin_den = 1;
+ else
+ screen->pin_den = 0;
+
+ return 0;
+
+}
+
+int rk_fb_prase_timing_dt(struct device_node *np, struct rk_screen *screen)
+{
+ struct display_timings *disp_timing;
+ struct display_timing *dt;
+ disp_timing = of_get_display_timings(np);
+ if (!disp_timing) {
+ pr_err("parse display timing err\n");
+ return -EINVAL;
+ }
+ dt = display_timings_get(disp_timing, 0);
+ rk_fb_video_mode_from_timing(dt, screen);
+ printk(KERN_DEBUG "dclk:%d\n"
+ "hactive:%d\n"
+ "hback_porch:%d\n"
+ "hfront_porch:%d\n"
+ "hsync_len:%d\n"
+ "vactive:%d\n"
+ "vback_porch:%d\n"
+ "vfront_porch:%d\n"
+ "vsync_len:%d\n"
+ "screen_type:%d\n"
+ "lvds_format:%d\n"
+ "face:%d\n",
+ dt->pixelclock.typ,
+ dt->hactive.typ,
+ dt->hback_porch.typ,
+ dt->hfront_porch.typ,
+ dt->hsync_len.typ,
+ dt->vactive.typ,
+ dt->vback_porch.typ,
+ dt->vfront_porch.typ,
+ dt->vsync_len.typ,
+ dt->screen_type,
+ dt->lvds_format,
+ dt->face);
+ return 0;
+
+}
+
+static int fimd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fimd_context *ctx;
+ struct rockchip_drm_subdrv *subdrv;
+ struct rockchip_drm_fimd_pdata *pdata;
+ struct rockchip_drm_panel_info *panel;
+ struct device_node *np = pdev->dev.of_node;
+ struct rk_screen *screen;
+ int prop;
+ int reg_len;
+ struct resource *res;
+ int win;
+ int ret = -EINVAL;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+ of_property_read_u32(np, "rockchip,prop", &prop);
+ if (prop == EXTEND) {
+ printk("---->%s not support lcdc EXTEND\n");
+ return 0;
+ }
+ printk("------>yzq dev=%x \n",dev);
+ if (dev->of_node) {
+ printk("------>yzq %s %d \n",__func__,__LINE__);
+ panel = devm_kzalloc(dev, sizeof(struct rockchip_drm_panel_info), GFP_KERNEL);
+ screen = devm_kzalloc(dev, sizeof(struct rk_screen), GFP_KERNEL);
+ rk_fb_get_prmry_screen(screen);
+ memcpy(&panel->timing,&screen->mode,sizeof(struct fb_videomode));
+ } else {
+ DRM_ERROR("no platform data specified\n");
+ return -EINVAL;
+ }
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ ctx->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctx->regs))
+ return PTR_ERR(ctx->regs);
+ reg_len = resource_size(res);
+ ctx->regsbak = devm_kzalloc(dev,reg_len,GFP_KERNEL);
+ ctx->lcdc_id = rk3188_lcdc_get_id(res->start);
+ ctx->screen = screen;
+ if(ctx->lcdc_id == 0){
+ ctx->hclk = clk_get(NULL, "g_h_lcdc0");
+ ctx->aclk = clk_get(NULL, "aclk_lcdc0");
+ ctx->dclk = clk_get(NULL, "dclk_lcdc0");
+ }else{
+ ctx->hclk = clk_get(NULL, "g_h_lcdc1");
+ ctx->aclk = clk_get(NULL, "aclk_lcdc1");
+ ctx->dclk = clk_get(NULL, "dclk_lcdc1");
+ }
+
+ ctx->irq = platform_get_irq(pdev, 0);
+ if (ctx->irq < 0) {
+ dev_err(dev, "cannot find IRQ for lcdc%d\n",
+ ctx->lcdc_id);
+ return -ENXIO;
+ }
+ ret = devm_request_irq(dev, ctx->irq, fimd_irq_handler,
+ 0, "drm_fimd", ctx);
+ if (ret) {
+ dev_err(dev, "irq request failed.\n");
+ return ret;
+ }
+
+ ctx->default_win = 0;// pdata->default_win;
+ ctx->panel = panel;
+ DRM_INIT_WAITQUEUE(&ctx->wait_vsync_queue);
+ atomic_set(&ctx->wait_vsync_event, 0);
+
+ subdrv = &ctx->subdrv;
+
+ subdrv->dev = dev;
+ subdrv->manager = &fimd_manager;
+ subdrv->probe = fimd_subdrv_probe;
+ subdrv->remove = fimd_subdrv_remove;
+
+ mutex_init(&ctx->lock);
+
+ platform_set_drvdata(pdev, ctx);
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ ret = clk_set_rate(ctx->dclk, ctx->screen->mode.pixclock);
+ if (ret)
+ printk( "set lcdc%d dclk failed\n", ctx->lcdc_id);
+
+ fimd_activate(ctx, true);
+
+ memcpy(ctx->regsbak,ctx->regs,reg_len);
+ rockchip_drm_subdrv_register(subdrv);
+
+ return 0;
+}
+
+static int fimd_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fimd_context *ctx = platform_get_drvdata(pdev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ rockchip_drm_subdrv_unregister(&ctx->subdrv);
+
+ if (ctx->suspended)
+ goto out;
+
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_sync(dev);
+
+out:
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fimd_suspend(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+
+ /*
+ * do not use pm_runtime_suspend(). if pm_runtime_suspend() is
+ * called here, an error would be returned by that interface
+ * because the usage_count of pm runtime is more than 1.
+ */
+ if (!pm_runtime_suspended(dev))
+ return fimd_activate(ctx, false);
+
+ return 0;
+}
+
+static int fimd_resume(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+
+ /*
+ * if entered to sleep when lcd panel was on, the usage_count
+ * of pm runtime would still be 1 so in this case, fimd driver
+ * should be on directly not drawing on pm runtime interface.
+ */
+ if (!pm_runtime_suspended(dev)) {
+ int ret;
+
+ ret = fimd_activate(ctx, true);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * in case of dpms on(standby), fimd_apply function will
+ * be called by encoder's dpms callback to update fimd's
+ * registers but in case of sleep wakeup, it's not.
+ * so fimd_apply function should be called at here.
+ */
+ fimd_apply(dev);
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int fimd_runtime_suspend(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ return fimd_activate(ctx, false);
+}
+
+static int fimd_runtime_resume(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ return fimd_activate(ctx, true);
+}
+#endif
+#if defined(CONFIG_OF)
+static const struct of_device_id rk3188_lcdc_dt_ids[] = {
+ {.compatible = "rockchip,rk3188-lcdc",},
+ {}
+};
+#endif
+
+static const struct dev_pm_ops fimd_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume)
+ SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL)
+};
+
+struct platform_driver fimd_driver = {
+ .probe = fimd_probe,
+ .remove = fimd_remove,
+ .id_table = rk3188_lcdc_dt_ids,
+ .driver = {
+ .name = "rk3188-lcdc",
+ .owner = THIS_MODULE,
+ .pm = &fimd_pm_ops,
+ .of_match_table = of_match_ptr(rk3188_lcdc_dt_ids),
+ },
+};
--- /dev/null
+/********************************************************************
+** display output interface supported by rockchip lcdc *
+********************************************************************/
+/* */
+#define OUT_P888 0 //24bit screen,connect to lcdc D0~D23
+#define OUT_P666 1 //18bit screen,connect to lcdc D0~D17
+#define OUT_P565 2
+#define OUT_S888x 4
+#define OUT_CCIR656 6
+#define OUT_S888 8
+#define OUT_S888DUMY 12
+#define OUT_RGB_AAA 15
+#define OUT_P16BPP4 24
+#define OUT_D888_P666 0x21 //18bit screen,connect to lcdc D2~D7, D10~D15, D18~D23
+#define OUT_D888_P565 0x22
+
+/*******************register definition**********************/
+
+#define SYS_CTRL (0x00)
+#define m_WIN0_EN (1<<0)
+#define m_WIN1_EN (1<<1)
+#define m_HWC_EN (1<<2)
+#define m_WIN0_FORMAT (7<<3)
+#define m_WIN1_FORMAT (7<<6)
+#define m_HWC_COLOR_MODE (1<<9)
+#define m_HWC_SIZE (1<<10)
+#define m_WIN0_3D_EN (1<<11)
+#define m_WIN0_3D_MODE (7<<12)
+#define m_WIN0_RB_SWAP (1<<15)
+#define m_WIN0_ALPHA_SWAP (1<<16)
+#define m_WIN0_Y8_SWAP (1<<17)
+#define m_WIN0_UV_SWAP (1<<18)
+#define m_WIN1_RB_SWAP (1<<19)
+#define m_WIN1_ALPHA_SWAP (1<<20)
+#define m_WIN1_BL_SWAP (1<<21)
+#define m_WIN0_OTSD_DISABLE (1<<22)
+#define m_WIN1_OTSD_DISABLE (1<<23)
+#define m_DMA_BURST_LENGTH (3<<24)
+#define m_HWC_LODAD_EN (1<<26)
+#define m_WIN1_LUT_EN (1<<27)
+#define m_DSP_LUT_EN (1<<28)
+#define m_DMA_STOP (1<<29)
+#define m_LCDC_STANDBY (1<<30)
+#define m_AUTO_GATING_EN (1<<31)
+#define v_WIN0_EN(x) (((x)&1)<<0)
+#define v_WIN1_EN(x) (((x)&1)<<1)
+#define v_HWC_EN(x) (((x)&1)<<2)
+#define v_WIN0_FORMAT(x) (((x)&7)<<3)
+#define v_WIN1_FORMAT(x) (((x)&7)<<6)
+#define v_HWC_COLOR_MODE(x) (((x)&1)<<9)
+#define v_HWC_SIZE(x) (((x)&1)<<10)
+#define v_WIN0_3D_EN(x) (((x)&1)<<11)
+#define v_WIN0_3D_MODE(x) (((x)&7)<<12)
+#define v_WIN0_RB_SWAP(x) (((x)&1)<<15)
+#define v_WIN0_ALPHA_SWAP(x) (((x)&1)<<16)
+#define v_WIN0_Y8_SWAP(x) (((x)&1)<<17)
+#define v_WIN0_UV_SWAP(x) (((x)&1)<<18)
+#define v_WIN1_RB_SWAP(x) (((x)&1)<<19)
+#define v_WIN1_ALPHA_SWAP(x) (((x)&1)<<20)
+#define v_WIN1_BL_SWAP(x) (((x)&1)<<21)
+#define v_WIN0_OTSD_DISABLE(x) (((x)&1)<<22)
+#define v_WIN1_OTSD_DISABLE(x) (((x)&1)<<23)
+#define v_DMA_BURST_LENGTH(x) (((x)&3)<<24)
+#define v_HWC_LODAD_EN(x) (((x)&1)<<26)
+#define v_WIN1_LUT_EN(x) (((x)&1)<<27)
+#define v_DSP_LUT_EN(x) (((x)&1)<<28)
+#define v_DMA_STOP(x) (((x)&1)<<29)
+#define v_LCDC_STANDBY(x) (((x)&1)<<30)
+#define v_AUTO_GATING_EN(x) (((x)&1)<<31)
+
+
+#define DSP_CTRL0 (0x04)
+#define m_DSP_OUT_FORMAT (0x0f<<0)
+#define m_HSYNC_POL (1<<4)
+#define m_VSYNC_POL (1<<5)
+#define m_DEN_POL (1<<6)
+#define m_DCLK_POL (1<<7)
+#define m_WIN0_TOP (1<<8)
+#define m_DITHER_UP_EN (1<<9)
+#define m_DITHER_DOWN_MODE (1<<10)
+#define m_DITHER_DOWN_EN (1<<11)
+#define m_INTERLACE_DSP_EN (1<<12)
+#define m_INTERLACE_POL (1<<13)
+#define m_WIN0_INTERLACE_EN (1<<14)
+#define m_WIN1_INTERLACE_EN (1<<15)
+#define m_WIN0_YRGB_DEFLICK_EN (1<<16)
+#define m_WIN0_CBR_DEFLICK_EN (1<<17)
+#define m_WIN0_ALPHA_MODE (1<<18)
+#define m_WIN1_ALPHA_MODE (1<<19)
+#define m_WIN0_CSC_MODE (3<<20)
+#define m_WIN1_CSC_MODE (1<<22)
+#define m_WIN0_YUV_CLIP (1<<23)
+#define m_DSP_CCIR656_AVG (1<<24)
+#define m_DCLK_OUTPUT_MODE (1<<25)
+#define m_DCLK_PHASE_LOCK (1<<26)
+#define m_DITHER_DOWN_SEL (3<<27)
+#define m_ALPHA_MODE_SEL0 (1<<29)
+#define m_ALPHA_MODE_SEL1 (1<<30)
+#define m_DIFF_DCLK_EN (1<<31)
+#define v_DSP_OUT_FORMAT(x) (((x)&0x0f)<<0)
+#define v_HSYNC_POL(x) (((x)&1)<<4)
+#define v_VSYNC_POL(x) (((x)&1)<<5)
+#define v_DEN_POL(x) (((x)&1)<<6)
+#define v_DCLK_POL(x) (((x)&1)<<7)
+#define v_WIN0_TOP(x) (((x)&1)<<8)
+#define v_DITHER_UP_EN(x) (((x)&1)<<9)
+#define v_DITHER_DOWN_MODE(x) (((x)&1)<<10)
+#define v_DITHER_DOWN_EN(x) (((x)&1)<<11)
+#define v_INTERLACE_DSP_EN(x) (((x)&1)<<12)
+#define v_INTERLACE_POL(x) (((x)&1)<<13)
+#define v_WIN0_INTERLACE_EN(x) (((x)&1)<<14)
+#define v_WIN1_INTERLACE_EN(x) (((x)&1)<<15)
+#define v_WIN0_YRGB_DEFLICK_EN(x) (((x)&1)<<16)
+#define v_WIN0_CBR_DEFLICK_EN(x) (((x)&1)<<17)
+#define v_WIN0_ALPHA_MODE(x) (((x)&1)<<18)
+#define v_WIN1_ALPHA_MODE(x) (((x)&1)<<19)
+#define v_WIN0_CSC_MODE(x) (((x)&3)<<20)
+#define v_WIN1_CSC_MODE(x) (((x)&1)<<22)
+#define v_WIN0_YUV_CLIP(x) (((x)&1)<<23)
+#define v_DSP_CCIR656_AVG(x) (((x)&1)<<24)
+#define v_DCLK_OUTPUT_MODE(x) (((x)&1)<<25)
+#define v_DCLK_PHASE_LOCK(x) (((x)&1)<<26)
+#define v_DITHER_DOWN_SEL(x) (((x)&1)<<27)
+#define v_ALPHA_MODE_SEL0(x) (((x)&1)<<29)
+#define v_ALPHA_MODE_SEL1(x) (((x)&1)<<30)
+#define v_DIFF_DCLK_EN(x) (((x)&1)<<31)
+
+
+#define DSP_CTRL1 (0x08)
+#define m_BG_COLOR (0xffffff<<0)
+#define m_BG_B (0xff<<0)
+#define m_BG_G (0xff<<8)
+#define m_BG_R (0xff<<16)
+#define m_BLANK_EN (1<<24)
+#define m_BLACK_EN (1<<25)
+#define m_DSP_BG_SWAP (1<<26)
+#define m_DSP_RB_SWAP (1<<27)
+#define m_DSP_RG_SWAP (1<<28)
+#define m_DSP_DELTA_SWAP (1<<29)
+#define m_DSP_DUMMY_SWAP (1<<30)
+#define m_DSP_OUT_ZERO (1<<31)
+#define v_BG_COLOR(x) (((x)&0xffffff)<<0)
+#define v_BG_B(x) (((x)&0xff)<<0)
+#define v_BG_G(x) (((x)&0xff)<<8)
+#define v_BG_R(x) (((x)&0xff)<<16)
+#define v_BLANK_EN(x) (((x)&1)<<24)
+#define v_BLACK_EN(x) (((x)&1)<<25)
+#define v_DSP_BG_SWAP(x) (((x)&1)<<26)
+#define v_DSP_RB_SWAP(x) (((x)&1)<<27)
+#define v_DSP_RG_SWAP(x) (((x)&1)<<28)
+#define v_DSP_DELTA_SWAP(x) (((x)&1)<<29)
+#define v_DSP_DUMMY_SWAP(x) (((x)&1)<<30)
+#define v_DSP_OUT_ZERO(x) (((x)&1)<<31)
+
+
+#define MCU_CTRL (0x0c)
+#define m_MCU_PIX_TOTAL (0x3f<<0)
+#define m_MCU_CS_ST (0x0f<<6)
+#define m_MCU_CS_END (0x3f<<10)
+#define m_MCU_RW_ST (0x0f<<16)
+#define m_MCU_RW_END (0x3f<<20)
+#define m_MCU_CLK_SEL (1<<26)
+#define m_MCU_HOLD_MODE (1<<27)
+#define m_MCU_FS_HOLD_STA (1<<28)
+#define m_MCU_RS_SELECT (1<<29)
+#define m_MCU_BYPASS (1<<30)
+#define m_MCU_TYPE (1<<31)
+
+#define v_MCU_PIX_TOTAL(x) (((x)&0x3f)<<0)
+#define v_MCU_CS_ST(x) (((x)&0x0f)<<6)
+#define v_MCU_CS_END(x) (((x)&0x3f)<<10)
+#define v_MCU_RW_ST(x) (((x)&0x0f)<<16)
+#define v_MCU_RW_END(x) (((x)&0x3f)<<20)
+#define v_MCU_CLK_SEL(x) (((x)&1)<<26)
+#define v_MCU_HOLD_MODE(x) (((x)&1)<<27)
+#define v_MCU_FS_HOLD_STA(x) (((x)&1)<<28)
+#define v_MCU_RS_SELECT(x) (((x)&1)<<29)
+#define v_MCU_BYPASS(x) (((x)&1)<<30)
+#define v_MCU_TYPE(x) (((x)&1)<<31)
+
+#define INT_STATUS (0x10)
+#define m_HS_INT_STA (1<<0) //status
+#define m_FS_INT_STA (1<<1)
+#define m_LF_INT_STA (1<<2)
+#define m_BUS_ERR_INT_STA (1<<3)
+#define m_HS_INT_EN (1<<4) //enable
+#define m_FS_INT_EN (1<<5)
+#define m_LF_INT_EN (1<<6)
+#define m_BUS_ERR_INT_EN (1<<7)
+#define m_HS_INT_CLEAR (1<<8) //auto clear
+#define m_FS_INT_CLEAR (1<<9)
+#define m_LF_INT_CLEAR (1<<10)
+#define m_BUS_ERR_INT_CLEAR (1<<11)
+#define m_LF_INT_NUM (0xfff<<12)
+#define v_HS_INT_EN(x) (((x)&1)<<4)
+#define v_FS_INT_EN(x) (((x)&1)<<5)
+#define v_LF_INT_EN(x) (((x)&1)<<6)
+#define v_BUS_ERR_INT_EN(x) (((x)&1)<<7)
+#define v_HS_INT_CLEAR(x) (((x)&1)<<8)
+#define v_FS_INT_CLEAR(x) (((x)&1)<<9)
+#define v_LF_INT_CLEAR(x) (((x)&1)<<10)
+#define v_BUS_ERR_INT_CLEAR(x) (((x)&1)<<11)
+#define v_LF_INT_NUM(x) (((x)&0xfff)<<12)
+
+
+#define ALPHA_CTRL (0x14)
+#define m_WIN0_ALPHA_EN (1<<0)
+#define m_WIN1_ALPHA_EN (1<<1)
+#define m_HWC_ALPAH_EN (1<<2)
+#define m_WIN0_ALPHA_VAL (0xff<<4)
+#define m_WIN1_ALPHA_VAL (0xff<<12)
+#define m_HWC_ALPAH_VAL (0x0f<<20)
+#define v_WIN0_ALPHA_EN(x) (((x)&1)<<0)
+#define v_WIN1_ALPHA_EN(x) (((x)&1)<<1)
+#define v_HWC_ALPAH_EN(x) (((x)&1)<<2)
+#define v_WIN0_ALPHA_VAL(x) (((x)&0xff)<<4)
+#define v_WIN1_ALPHA_VAL(x) (((x)&0xff)<<12)
+#define v_HWC_ALPAH_VAL(x) (((x)&0x0f)<<20)
+
+#define WIN0_COLOR_KEY (0x18)
+#define m_COLOR_KEY_VAL (0xffffff<<0)
+#define m_COLOR_KEY_EN (1<<24)
+#define v_COLOR_KEY_VAL(x) (((x)&0xffffff)<<0)
+#define v_COLOR_KEY_EN(x) (((x)&1)<<24)
+
+#define WIN1_COLOR_KEY (0x1C)
+
+
+#define WIN0_YRGB_MST0 (0x20)
+#define WIN0_CBR_MST0 (0x24)
+#define WIN0_YRGB_MST1 (0x28)
+#define WIN0_CBR_MST1 (0x2C)
+#define WIN_VIR (0x30)
+#define m_WIN0_VIR (0x1fff << 0)
+#define m_WIN1_VIR (0x1fff << 16)
+#define v_WIN0_VIR_VAL(x) ((x)<<0)
+#define v_WIN1_VIR_VAL(x) ((x)<<16)
+#define v_ARGB888_VIRWIDTH(x) (((x)&0x1fff)<<0)
+#define v_RGB888_VIRWIDTH(x) (((((x*3)>>2)+((x)%3))&0x1fff)<<0)
+#define v_RGB565_VIRWIDTH(x) ((DIV_ROUND_UP(x,2)&0x1fff)<<0)
+#define v_YUV_VIRWIDTH(x) ((DIV_ROUND_UP(x,4)&0x1fff)<<0)
+#define v_WIN1_ARGB888_VIRWIDTH(x) (((x)&0x1fff)<<16)
+#define v_WIN1_RGB888_VIRWIDTH(x) (((((x*3)>>2)+((x)%3))&0x1fff)<<16)
+#define v_WIN1_RGB565_VIRWIDTH(x) ((DIV_ROUND_UP(x,2)&0x1fff)<<16)
+
+
+
+#define WIN0_ACT_INFO (0x34)
+#define m_ACT_WIDTH (0x1fff<<0)
+#define m_ACT_HEIGHT (0x1fff<<16)
+#define v_ACT_WIDTH(x) (((x-1)&0x1fff)<<0)
+#define v_ACT_HEIGHT(x) (((x-1)&0x1fff)<<16)
+
+#define WIN0_DSP_INFO (0x38)
+#define v_DSP_WIDTH(x) (((x-1)&0x7ff)<<0)
+#define v_DSP_HEIGHT(x) (((x-1)&0x7ff)<<16)
+
+#define WIN0_DSP_ST (0x3C)
+#define v_DSP_STX(x) (((x)&0xfff)<<0)
+#define v_DSP_STY(x) (((x)&0xfff)<<16)
+
+#define WIN0_SCL_FACTOR_YRGB (0x40)
+#define v_X_SCL_FACTOR(x) (((x)&0xffff)<<0)
+#define v_Y_SCL_FACTOR(x) (((x)&0xffff)<<16)
+
+#define WIN0_SCL_FACTOR_CBR (0x44)
+#define WIN0_SCL_OFFSET (0x48)
+#define WIN1_MST (0x4C)
+#define WIN1_DSP_INFO (0x50)
+#define WIN1_DSP_ST (0x54)
+#define HWC_MST (0x58)
+#define HWC_DSP_ST (0x5C)
+#define HWC_COLOR_LUT0 (0x60)
+#define HWC_COLOR_LUT1 (0x64)
+#define HWC_COLOR_LUT2 (0x68)
+#define DSP_HTOTAL_HS_END (0x6C)
+#define v_HSYNC(x) (((x)&0xfff)<<0) //hsync pulse width
+#define v_HORPRD(x) (((x)&0xfff)<<16) //horizontal period
+
+#define DSP_HACT_ST_END (0x70)
+#define v_HAEP(x) (((x)&0xfff)<<0) //horizontal active end point
+#define v_HASP(x) (((x)&0xfff)<<16) //horizontal active start point
+
+#define DSP_VTOTAL_VS_END (0x74)
+#define v_VSYNC(x) (((x)&0xfff)<<0)
+#define v_VERPRD(x) (((x)&0xfff)<<16)
+#define DSP_VACT_ST_END (0x78)
+#define v_VAEP(x) (((x)&0xfff)<<0)
+#define v_VASP(x) (((x)&0xfff)<<16)
+
+#define DSP_VS_ST_END_F1 (0x7C)
+#define DSP_VACT_ST_END_F1 (0x80)
+#define REG_CFG_DONE (0x90)
+#define MCU_BYPASS_WPORT (0x100)
+#define MCU_BYPASS_RPORT (0x200)
+#define WIN1_LUT_ADDR (0x400)
+#define DSP_LUT_ADDR (0x800)
+
+/*
+ RK3026/RK3028A max output resolution 1920x1080
+ support IEP instead of 3d
+*/
+//#ifdef CONFIG_ARCH_RK3026
+//SYS_CTRL 0x00
+#define m_DIRECT_PATCH_EN (1<<11)
+#define m_DIRECT_PATH_LAY_SEL (1<<12)
+
+#define v_DIRECT_PATCH_EN(x) (((x)&1)<<11)
+#define v_DIRECT_PATH_LAY_SEL(x) (((x)&1)<<12)
+
+//INT_STATUS 0x10
+#define m_WIN0_EMPTY_INTR_EN (1<<24)
+#define m_WIN1_EMPTY_INTR_EN (1<<25)
+#define m_WIN0_EMPTY_INTR_CLR (1<<26)
+#define m_WIN1_EMPTY_INTR_CLR (1<<27)
+#define m_WIN0_EMPTY_INTR_STA (1<<28)
+#define m_WIN1_EMPTY_INTR_STA (1<<29)
+
+#define v_WIN0_EMPTY_INTR_EN(x) (((x)&1)<<24)
+#define v_WIN1_EMPTY_INTR_EN(x) (((x)&1)<<25)
+#define v_WIN0_EMPTY_INTR_CLR(x) (((x)&1)<<26)
+#define v_WIN1_EMPTY_INTR_CLR(x) (((x)&1)<<27)
+#define v_WIN0_EMPTY_INTR_STA(x) (((x)&1)<<28)
+#define v_WIN1_EMPTY_INTR_STA(x) (((x)&1)<<29)
+//#endif
+
+
+#define CalScale(x, y) ((((u32)(x-1))*0x1000)/(y-1))
+
+static inline void lcdc_writel(struct fimd_context *ctx,u32 offset,u32 v)
+{
+ u32 *_pv = (u32*)ctx->regsbak;
+ _pv += (offset >> 2);
+ *_pv = v;
+ writel_relaxed(v,ctx->regs+offset);
+}
+
+static inline u32 lcdc_readl(struct fimd_context *ctx,u32 offset)
+{
+ u32 v;
+ u32 *_pv = (u32*)ctx->regsbak;
+ _pv += (offset >> 2);
+ v = readl_relaxed(ctx->regs+offset);
+ *_pv = v;
+ return v;
+}
+
+static inline u32 lcdc_read_bit(struct fimd_context *ctx,u32 offset,u32 msk)
+{
+ u32 _v = readl_relaxed(ctx->regs+offset);
+ _v &= msk;
+ return (_v >> msk);
+}
+
+static inline void lcdc_set_bit(struct fimd_context *ctx,u32 offset,u32 msk)
+{
+ u32* _pv = (u32*)ctx->regsbak;
+ _pv += (offset >> 2);
+ (*_pv) |= msk;
+ writel_relaxed(*_pv,ctx->regs + offset);
+}
+
+static inline void lcdc_clr_bit(struct fimd_context *ctx,u32 offset,u32 msk)
+{
+ u32* _pv = (u32*)ctx->regsbak;
+ _pv += (offset >> 2);
+ (*_pv) &= (~msk);
+ writel_relaxed(*_pv,ctx->regs + offset);
+}
+
+static inline void lcdc_msk_reg(struct fimd_context *ctx,u32 offset,u32 msk,u32 v)
+{
+ u32 *_pv = (u32*)ctx->regsbak;
+ _pv += (offset >> 2);
+ (*_pv) &= (~msk);
+ (*_pv) |= v;
+ writel_relaxed(*_pv,ctx->regs+offset);
+}
+
+static inline void lcdc_cfg_done(struct fimd_context *ctx)
+{
+ writel_relaxed(0x01,ctx->regs+REG_CFG_DONE);
+ dsb();
+}
+
+
--- /dev/null
+/*
+ * rk3288_drm_fimd.c
+ *
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <drm/drmP.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+
+#include <video/of_display_timing.h>
+#include <drm/rockchip_drm.h>
+#include <linux/rockchip/cpu.h>
+#include <linux/rockchip/iomap.h>
+#include <linux/rk_fb.h>
+#include <video/display_timing.h>
+#include <linux/rockchip/cpu.h>
+#include <linux/rockchip/iomap.h>
+#include <linux/rockchip/grf.h>
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_crtc.h"
+#include "rockchip_drm_iommu.h"
+
+/*
+ * FIMD is stand for Fully Interactive Mobile Display and
+ * as a display controller, it transfers contents drawn on memory
+ * to a LCD Panel through Display Interfaces such as RGB or
+ * CPU Interface.
+ */
+
+/* position control register for hardware window 0, 2 ~ 4.*/
+#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16)
+#define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16)
+/*
+ * size control register for hardware windows 0 and alpha control register
+ * for hardware windows 1 ~ 4
+ */
+#define VIDOSD_C(win) (VIDOSD_BASE + 0x08 + (win) * 16)
+/* size control register for hardware windows 1 ~ 2. */
+#define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16)
+
+#define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8)
+#define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8)
+#define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4)
+
+/* color key control register for hardware window 1 ~ 4. */
+#define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + ((x - 1) * 8))
+/* color key value register for hardware window 1 ~ 4. */
+#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8))
+
+/* FIMD has totally five hardware windows. */
+#define WINDOWS_NR 4
+/*****************************************************************************************************/
+#define SCALE_FACTOR_BILI_DN_FIXPOINT_SHIFT 12 /* 4.12*/
+#define SCALE_FACTOR_BILI_DN_FIXPOINT(x) ((INT32)((x)*(1 << SCALE_FACTOR_BILI_DN_FIXPOINT_SHIFT)))
+
+#define SCALE_FACTOR_BILI_UP_FIXPOINT_SHIFT 16 /* 0.16*/
+
+#define SCALE_FACTOR_AVRG_FIXPOINT_SHIFT 16 /*0.16*/
+#define SCALE_FACTOR_AVRG_FIXPOINT(x) ((INT32)((x)*(1 << SCALE_FACTOR_AVRG_FIXPOINT_SHIFT)))
+
+#define SCALE_FACTOR_BIC_FIXPOINT_SHIFT 16 /* 0.16*/
+#define SCALE_FACTOR_BIC_FIXPOINT(x) ((INT32)((x)*(1 << SCALE_FACTOR_BIC_FIXPOINT_SHIFT)))
+
+#define SCALE_FACTOR_DEFAULT_FIXPOINT_SHIFT 12 /*NONE SCALE,vsd_bil*/
+#define SCALE_FACTOR_VSDBIL_FIXPOINT_SHIFT 12 /*VER SCALE DOWN BIL*/
+
+/*****************************************************************************************************/
+
+/*#define GET_SCALE_FACTOR_BILI(src, dst) ((((src) - 1) << SCALE_FACTOR_BILI_FIXPOINT_SHIFT) / ((dst) - 1))*/
+/*#define GET_SCALE_FACTOR_BIC(src, dst) ((((src) - 1) << SCALE_FACTOR_BIC_FIXPOINT_SHIFT) / ((dst) - 1))*/
+/*modified by hpz*/
+#define GET_SCALE_FACTOR_BILI_DN(src, dst) ((((src)*2 - 3) << (SCALE_FACTOR_BILI_DN_FIXPOINT_SHIFT-1)) / ((dst) - 1))
+#define GET_SCALE_FACTOR_BILI_UP(src, dst) ((((src)*2 - 3) << (SCALE_FACTOR_BILI_UP_FIXPOINT_SHIFT-1)) / ((dst) - 1))
+#define GET_SCALE_FACTOR_BIC(src, dst) ((((src)*2 - 3) << (SCALE_FACTOR_BIC_FIXPOINT_SHIFT-1)) / ((dst) - 1))
+
+#define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev))
+
+static struct rk_fb_trsm_ops *trsm_lvds_ops;
+static struct rk_fb_trsm_ops *trsm_edp_ops;
+static struct rk_fb_trsm_ops *trsm_mipi_ops;
+
+int rk_fb_trsm_ops_register(struct rk_fb_trsm_ops *ops, int type)
+{
+ switch (type) {
+ case SCREEN_RGB:
+ case SCREEN_LVDS:
+ case SCREEN_DUAL_LVDS:
+ trsm_lvds_ops = ops;
+ break;
+ case SCREEN_EDP:
+ trsm_edp_ops = ops;
+ break;
+ case SCREEN_MIPI:
+ case SCREEN_DUAL_MIPI:
+ trsm_mipi_ops = ops;
+ break;
+ default:
+ printk(KERN_WARNING "%s:un supported transmitter:%d!\n",
+ __func__, type);
+ break;
+ }
+ return 0;
+}
+struct fimd_driver_data {
+ unsigned int timing_base;
+};
+
+static struct fimd_driver_data rockchip4_fimd_driver_data = {
+ .timing_base = 0x0,
+};
+
+static struct fimd_driver_data rockchip5_fimd_driver_data = {
+ .timing_base = 0x20000,
+};
+
+struct fimd_win_data {
+ unsigned int offset_x;
+ unsigned int offset_y;
+ unsigned int ovl_width;
+ unsigned int ovl_height;
+ unsigned int fb_width;
+ unsigned int fb_height;
+ unsigned int bpp;
+ dma_addr_t dma_addr;
+ unsigned int buf_offsize;
+ unsigned int line_size; /* bytes */
+ bool enabled;
+ bool resume;
+};
+
+struct fimd_context {
+ struct rockchip_drm_subdrv subdrv;
+ int irq;
+ struct drm_crtc *crtc;
+ struct clk *pd; //lcdc power domain
+ struct clk *hclk; //lcdc AHP clk
+ struct clk *dclk; //lcdc dclk
+ struct clk *aclk; //lcdc share memory frequency
+ void __iomem *regs;
+ struct fimd_win_data win_data[WINDOWS_NR];
+ unsigned int clkdiv;
+ unsigned int default_win;
+ unsigned long irq_flags;
+ u32 vidcon0;
+ u32 vidcon1;
+ int lcdc_id;
+ bool suspended;
+ struct mutex lock;
+ wait_queue_head_t wait_vsync_queue;
+ atomic_t wait_vsync_event;
+
+ int clkon;
+ void *regsbak; //back up reg
+ struct rockchip_drm_panel_info *panel;
+ struct rk_screen *screen;
+};
+
+#include "rk3288_drm_fimd.h"
+static int rk3288_lcdc_get_id(u32 phy_base)
+{
+ if (cpu_is_rk3288()) {
+ if (phy_base == 0xff930000)/*vop big*/
+ return 0;
+ else if (phy_base == 0xff940000)/*vop lit*/
+ return 1;
+ else
+ return -EINVAL;
+ } else {
+ pr_err("un supported platform \n");
+ return -EINVAL;
+ }
+}
+
+
+static bool fimd_display_is_connected(struct device *dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+
+ return true;
+}
+
+static void *fimd_get_panel(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ return ctx->panel;
+}
+
+static int fimd_check_timing(struct device *dev, void *timing)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+
+ return 0;
+}
+
+static int fimd_display_power_on(struct device *dev, int mode)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO */
+
+ return 0;
+}
+
+static struct rockchip_drm_display_ops fimd_display_ops = {
+ .type = ROCKCHIP_DISPLAY_TYPE_LCD,
+ .is_connected = fimd_display_is_connected,
+ .get_panel = fimd_get_panel,
+ .check_timing = fimd_check_timing,
+ .power_on = fimd_display_power_on,
+};
+
+static void fimd_dpms(struct device *subdrv_dev, int mode)
+{
+ struct fimd_context *ctx = get_fimd_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
+
+ mutex_lock(&ctx->lock);
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ /*
+ * enable fimd hardware only if suspended status.
+ *
+ * P.S. fimd_dpms function would be called at booting time so
+ * clk_enable could be called double time.
+ */
+
+ if(trsm_lvds_ops != NULL){
+ printk(KERN_ERR"------>yzq enable lvds\n");
+ trsm_lvds_ops->enable();
+ }
+ if (ctx->suspended)
+ pm_runtime_get_sync(subdrv_dev);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ if (!ctx->suspended)
+ pm_runtime_put_sync(subdrv_dev);
+ break;
+ default:
+ DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+ break;
+ }
+
+ mutex_unlock(&ctx->lock);
+}
+
+static void fimd_apply(struct device *subdrv_dev)
+{
+ struct fimd_context *ctx = get_fimd_context(subdrv_dev);
+ struct rockchip_drm_manager *mgr = ctx->subdrv.manager;
+ struct rockchip_drm_manager_ops *mgr_ops = mgr->ops;
+ struct rockchip_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
+ struct fimd_win_data *win_data;
+ int i;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ for (i = 0; i < WINDOWS_NR; i++) {
+ win_data = &ctx->win_data[i];
+ if (win_data->enabled && (ovl_ops && ovl_ops->commit))
+ ovl_ops->commit(subdrv_dev, i);
+ }
+
+ if (mgr_ops && mgr_ops->commit)
+ mgr_ops->commit(subdrv_dev);
+}
+
+static int rk3288_lcdc_alpha_cfg(struct fimd_context *ctx,int win_id)
+{
+ struct alpha_config alpha_config;
+ struct fimd_win_data *win_data;
+ enum alpha_mode alpha_mode;
+ u32 mask, val;
+ int ppixel_alpha,global_alpha;
+ u32 src_alpha_ctl,dst_alpha_ctl;
+ int g_alpha_val=0;
+
+ win_data = &ctx->win_data[win_id];
+ ppixel_alpha = (win_data->bpp==32) ? 1 : 0;
+ global_alpha = 1;
+ alpha_config.src_global_alpha_val = 1;
+ alpha_mode = AB_SRC_OVER;
+ global_alpha = (g_alpha_val == 0) ? 0 : 1;
+ alpha_config.src_global_alpha_val = g_alpha_val;
+ /*printk("%s,alpha_mode=%d,alpha_en=%d,ppixel_a=%d,gla_a=%d\n",
+ __func__,win->alpha_mode,win->alpha_en,ppixel_alpha,global_alpha);*/
+ switch(alpha_mode){
+ case AB_USER_DEFINE:
+ break;
+ case AB_CLEAR:
+ alpha_config.src_factor_mode=AA_ZERO;
+ alpha_config.dst_factor_mode=AA_ZERO;
+ break;
+ case AB_SRC:
+ alpha_config.src_factor_mode=AA_ONE;
+ alpha_config.dst_factor_mode=AA_ZERO;
+ break;
+ case AB_DST:
+ alpha_config.src_factor_mode=AA_ZERO;
+ alpha_config.dst_factor_mode=AA_ONE;
+ break;
+ case AB_SRC_OVER:
+ alpha_config.src_color_mode=AA_SRC_PRE_MUL;
+ alpha_config.src_factor_mode=AA_ONE;
+ alpha_config.dst_factor_mode=AA_SRC_INVERSE;
+ break;
+ case AB_DST_OVER:
+ alpha_config.src_color_mode=AA_SRC_PRE_MUL;
+ alpha_config.src_factor_mode=AA_SRC_INVERSE;
+ alpha_config.dst_factor_mode=AA_ONE;
+ break;
+ case AB_SRC_IN:
+ alpha_config.src_color_mode=AA_SRC_PRE_MUL;
+ alpha_config.src_factor_mode=AA_SRC;
+ alpha_config.dst_factor_mode=AA_ZERO;
+ break;
+ case AB_DST_IN:
+ alpha_config.src_factor_mode=AA_ZERO;
+ alpha_config.dst_factor_mode=AA_SRC;
+ break;
+ case AB_SRC_OUT:
+ alpha_config.src_color_mode=AA_SRC_PRE_MUL;
+ alpha_config.src_factor_mode=AA_SRC_INVERSE;
+ alpha_config.dst_factor_mode=AA_ZERO;
+ break;
+ case AB_DST_OUT:
+ alpha_config.src_factor_mode=AA_ZERO;
+ alpha_config.dst_factor_mode=AA_SRC_INVERSE;
+ break;
+ case AB_SRC_ATOP:
+ alpha_config.src_color_mode=AA_SRC_PRE_MUL;
+ alpha_config.src_factor_mode=AA_SRC;
+ alpha_config.dst_factor_mode=AA_SRC_INVERSE;
+ break;
+ case AB_DST_ATOP:
+ alpha_config.src_color_mode=AA_SRC_PRE_MUL;
+ alpha_config.src_factor_mode=AA_SRC_INVERSE;
+ alpha_config.dst_factor_mode=AA_SRC;
+ break;
+ case XOR:
+ alpha_config.src_color_mode=AA_SRC_PRE_MUL;
+ alpha_config.src_factor_mode=AA_SRC_INVERSE;
+ alpha_config.dst_factor_mode=AA_SRC_INVERSE;
+ break;
+ case AB_SRC_OVER_GLOBAL:
+ alpha_config.src_global_alpha_mode=AA_PER_PIX_GLOBAL;
+ alpha_config.src_color_mode=AA_SRC_NO_PRE_MUL;
+ alpha_config.src_factor_mode=AA_SRC_GLOBAL;
+ alpha_config.dst_factor_mode=AA_SRC_INVERSE;
+ break;
+ default:
+ pr_err("alpha mode error\n");
+ break;
+ }
+ if((ppixel_alpha == 1)&&(global_alpha == 1)){
+ alpha_config.src_global_alpha_mode = AA_PER_PIX_GLOBAL;
+ }else if(ppixel_alpha == 1){
+ alpha_config.src_global_alpha_mode = AA_PER_PIX;
+ }else if(global_alpha == 1){
+ alpha_config.src_global_alpha_mode = AA_GLOBAL;
+ }else{
+ pr_err("alpha_en should be 0\n");
+ }
+ alpha_config.src_alpha_mode = AA_STRAIGHT;
+ alpha_config.src_alpha_cal_m0 = AA_NO_SAT;
+
+ switch(win_id){
+ case 0:
+ src_alpha_ctl = 0x60;
+ dst_alpha_ctl = 0x64;
+ break;
+ case 1:
+ src_alpha_ctl = 0xa0;
+ dst_alpha_ctl = 0xa4;
+ break;
+ case 2:
+ src_alpha_ctl = 0xdc;
+ dst_alpha_ctl = 0xec;
+ break;
+ case 3:
+ src_alpha_ctl = 0x12c;
+ dst_alpha_ctl = 0x13c;
+ break;
+ }
+ mask = m_WIN0_DST_FACTOR_M0;
+ val = v_WIN0_DST_FACTOR_M0(alpha_config.dst_factor_mode);
+ lcdc_msk_reg(ctx, dst_alpha_ctl, mask, val);
+ mask = m_WIN0_SRC_ALPHA_EN | m_WIN0_SRC_COLOR_M0 |
+ m_WIN0_SRC_ALPHA_M0 | m_WIN0_SRC_BLEND_M0 |
+ m_WIN0_SRC_ALPHA_CAL_M0 | m_WIN0_SRC_FACTOR_M0|
+ m_WIN0_SRC_GLOBAL_ALPHA;
+ val = v_WIN0_SRC_ALPHA_EN(1) |
+ v_WIN0_SRC_COLOR_M0(alpha_config.src_color_mode) |
+ v_WIN0_SRC_ALPHA_M0(alpha_config.src_alpha_mode) |
+ v_WIN0_SRC_BLEND_M0(alpha_config.src_global_alpha_mode) |
+ v_WIN0_SRC_ALPHA_CAL_M0(alpha_config.src_alpha_cal_m0) |
+ v_WIN0_SRC_FACTOR_M0(alpha_config.src_factor_mode) |
+ v_WIN0_SRC_GLOBAL_ALPHA(alpha_config.src_global_alpha_val);
+ lcdc_msk_reg(ctx, src_alpha_ctl, mask, val);
+
+ return 0;
+}
+static int rk3288_win_0_1_reg_update(struct fimd_context *ctx,int win_id)
+{
+ struct fimd_win_data *win_data;
+ unsigned int mask, val, off;
+ struct rk_screen *screen = ctx->screen;
+ u8 fmt_cfg = 0;
+ u32 xpos, ypos;
+ off = win_id * 0x40;
+ win_data = &ctx->win_data[win_id];
+ switch(win_data->bpp){
+ case 32:
+ fmt_cfg = 0;
+ break;
+ case 24:
+ fmt_cfg = 1;
+ break;
+ case 16:
+ fmt_cfg = 2;
+ break;
+ default:
+ printk("not support format %d\n",win_data->bpp);
+ break;
+ }
+
+ xpos = win_data->offset_x + screen->mode.left_margin + screen->mode.hsync_len;
+ ypos = win_data->offset_y + screen->mode.upper_margin + screen->mode.vsync_len;
+ mask = m_WIN0_EN | m_WIN0_DATA_FMT ;
+ val = v_WIN0_EN(1) | v_WIN0_DATA_FMT(fmt_cfg);
+ lcdc_msk_reg(ctx, WIN0_CTRL0+off, mask,val);
+
+ val = v_WIN0_VIR_STRIDE(win_data->line_size/(win_data->bpp>>3));
+ lcdc_writel(ctx, WIN0_VIR+off, val);
+ val = v_WIN0_ACT_WIDTH(win_data->ovl_width) |
+ v_WIN0_ACT_HEIGHT(win_data->ovl_height);
+ lcdc_writel(ctx, WIN0_ACT_INFO+off, val);
+
+ val = v_WIN0_DSP_WIDTH(win_data->fb_width) |
+ v_WIN0_DSP_HEIGHT(win_data->fb_height);
+ lcdc_writel(ctx, WIN0_DSP_INFO+off, val);
+
+ val = v_WIN0_DSP_XST(xpos) |
+ v_WIN0_DSP_YST(ypos);
+ lcdc_writel(ctx, WIN0_DSP_ST+off, val);
+ lcdc_writel(ctx, WIN0_YRGB_MST+off, win_data->dma_addr +win_data->buf_offsize );
+
+ if(win_id == 1)
+ rk3288_lcdc_alpha_cfg(ctx,win_id);
+ lcdc_cfg_done(ctx);
+ return 0;
+}
+
+static int rk3288_lcdc_post_cfg(struct fimd_context *ctx)
+{
+ struct rk_screen *screen = ctx->screen;
+ u16 x_res = screen->mode.xres;
+ u16 y_res = screen->mode.yres;
+ u32 mask, val;
+ u16 h_total,v_total;
+ u16 post_hsd_en,post_vsd_en;
+ u16 post_dsp_hact_st,post_dsp_hact_end;
+ u16 post_dsp_vact_st,post_dsp_vact_end;
+ u16 post_dsp_vact_st_f1,post_dsp_vact_end_f1;
+ u16 post_h_fac,post_v_fac;
+
+ h_total = screen->mode.hsync_len+screen->mode.left_margin +
+ x_res + screen->mode.right_margin;
+ v_total = screen->mode.vsync_len+screen->mode.upper_margin +
+ y_res + screen->mode.lower_margin;
+
+ if(screen->post_dsp_stx + screen->post_xsize > x_res){
+ printk(KERN_ERR"post:stx[%d] + xsize[%d] > x_res[%d]\n",
+ screen->post_dsp_stx,screen->post_xsize,x_res);
+ screen->post_dsp_stx = x_res - screen->post_xsize;
+ }
+ if(screen->x_mirror == 0){
+ post_dsp_hact_st=screen->post_dsp_stx +
+ screen->mode.hsync_len+screen->mode.left_margin;
+ post_dsp_hact_end = post_dsp_hact_st + screen->post_xsize;
+ }else{
+ post_dsp_hact_end = h_total - screen->mode.right_margin -
+ screen->post_dsp_stx;
+ post_dsp_hact_st = post_dsp_hact_end - screen->post_xsize;
+ }
+ if((screen->post_xsize < x_res)&&(screen->post_xsize != 0)){
+ post_hsd_en = 1;
+ post_h_fac =
+ GET_SCALE_FACTOR_BILI_DN(x_res , screen->post_xsize);
+ }else{
+ post_hsd_en = 0;
+ post_h_fac = 0x1000;
+ }
+
+
+ if(screen->post_dsp_sty + screen->post_ysize > y_res){
+ printk(KERN_ERR "post:sty[%d] + ysize[%d] > y_res[%d]\n",
+ screen->post_dsp_sty,screen->post_ysize,y_res);
+ screen->post_dsp_sty = y_res - screen->post_ysize;
+ }
+
+ if(screen->y_mirror == 0){
+ post_dsp_vact_st = screen->post_dsp_sty +
+ screen->mode.vsync_len+screen->mode.upper_margin;
+ post_dsp_vact_end = post_dsp_vact_st + screen->post_ysize;
+ }else{
+ post_dsp_vact_end = v_total - screen->mode.lower_margin -
+ - screen->post_dsp_sty;
+ post_dsp_hact_st = post_dsp_vact_end - screen->post_ysize;
+ }
+ if((screen->post_ysize < y_res)&&(screen->post_ysize != 0)){
+ post_vsd_en = 1;
+ post_v_fac = GET_SCALE_FACTOR_BILI_DN(y_res, screen->post_ysize);
+ }else{
+ post_vsd_en = 0;
+ post_v_fac = 0x1000;
+ }
+
+ if(screen->interlace == 1){
+ post_dsp_vact_st_f1 = v_total + post_dsp_vact_st;
+ post_dsp_vact_end_f1 = post_dsp_vact_st_f1 + screen->post_ysize;
+ }else{
+ post_dsp_vact_st_f1 = 0;
+ post_dsp_vact_end_f1 = 0;
+ }
+ printk(KERN_ERR"post:xsize=%d,ysize=%d,xpos=%d,ypos=%d,"
+ "hsd_en=%d,h_fac=%d,vsd_en=%d,v_fac=%d\n",
+ screen->post_xsize,screen->post_ysize,screen->xpos,screen->ypos,
+ post_hsd_en,post_h_fac,post_vsd_en,post_v_fac);
+ mask = m_DSP_HACT_END_POST | m_DSP_HACT_ST_POST;
+ val = v_DSP_HACT_END_POST(post_dsp_hact_end) |
+ v_DSP_HACT_ST_POST(post_dsp_hact_st);
+ lcdc_msk_reg(ctx, POST_DSP_HACT_INFO, mask, val);
+
+ mask = m_DSP_VACT_END_POST | m_DSP_VACT_ST_POST;
+ val = v_DSP_VACT_END_POST(post_dsp_vact_end) |
+ v_DSP_VACT_ST_POST(post_dsp_vact_st);
+ lcdc_msk_reg(ctx, POST_DSP_VACT_INFO, mask, val);
+
+ mask = m_POST_HS_FACTOR_YRGB | m_POST_VS_FACTOR_YRGB;
+ val = v_POST_HS_FACTOR_YRGB(post_h_fac) |
+ v_POST_VS_FACTOR_YRGB(post_v_fac);
+ lcdc_msk_reg(ctx, POST_SCL_FACTOR_YRGB, mask, val);
+
+ mask = m_DSP_VACT_END_POST_F1 | m_DSP_VACT_ST_POST_F1;
+ val = v_DSP_VACT_END_POST_F1(post_dsp_vact_end_f1) |
+ v_DSP_VACT_ST_POST_F1(post_dsp_vact_st_f1);
+ lcdc_msk_reg(ctx, POST_DSP_VACT_INFO_F1, mask, val);
+
+ mask = m_POST_HOR_SD_EN | m_POST_VER_SD_EN;
+ val = v_POST_HOR_SD_EN(post_hsd_en) | v_POST_VER_SD_EN(post_vsd_en);
+ lcdc_msk_reg(ctx, POST_SCL_CTRL, mask, val);
+ return 0;
+}
+static void fimd_commit(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct rockchip_drm_panel_info *panel = ctx->panel;
+ struct rk_screen *screen = ctx->screen;
+ u16 hsync_len = screen->mode.hsync_len;
+ u16 left_margin = screen->mode.left_margin;
+ u16 right_margin = screen->mode.right_margin;
+ u16 vsync_len = screen->mode.vsync_len;
+ u16 upper_margin = screen->mode.upper_margin;
+ u16 lower_margin = screen->mode.lower_margin;
+ u16 x_res = screen->mode.xres;
+ u16 y_res = screen->mode.yres;
+ u32 mask, val;
+ int face;
+ u32 v=0;
+ u16 h_total,v_total;
+ h_total = hsync_len + left_margin + x_res + right_margin;
+ v_total = vsync_len + upper_margin + y_res + lower_margin;
+ screen->post_dsp_stx=0;
+ screen->post_dsp_sty=0;
+ screen->post_xsize =x_res;
+ screen->post_ysize = y_res;
+
+ printk(KERN_ERR"%s %d\n", __func__,__LINE__);
+ if (ctx->suspended)
+ return;
+
+ printk(KERN_ERR"%s %d\n", __func__,__LINE__);
+ if(!ctx->clkon)
+ return;
+#if 1
+ switch (screen->face) {
+ case OUT_P565:
+ face = OUT_P565;
+ mask = m_DITHER_DOWN_EN | m_DITHER_DOWN_MODE |
+ m_DITHER_DOWN_SEL;
+ val = v_DITHER_DOWN_EN(1) | v_DITHER_DOWN_MODE(0) |
+ v_DITHER_DOWN_SEL(1);
+ lcdc_msk_reg(ctx, DSP_CTRL1, mask, val);
+ break;
+ case OUT_P666:
+ face = OUT_P666;
+ mask = m_DITHER_DOWN_EN | m_DITHER_DOWN_MODE |
+ m_DITHER_DOWN_SEL;
+ val = v_DITHER_DOWN_EN(1) | v_DITHER_DOWN_MODE(1) |
+ v_DITHER_DOWN_SEL(1);
+ lcdc_msk_reg(ctx, DSP_CTRL1, mask, val);
+ break;
+ case OUT_D888_P565:
+ face = OUT_P888;
+ mask = m_DITHER_DOWN_EN | m_DITHER_DOWN_MODE |
+ m_DITHER_DOWN_SEL;
+ val = v_DITHER_DOWN_EN(1) | v_DITHER_DOWN_MODE(0) |
+ v_DITHER_DOWN_SEL(1);
+ lcdc_msk_reg(ctx, DSP_CTRL1, mask, val);
+ break;
+ case OUT_D888_P666:
+ face = OUT_P888;
+ mask = m_DITHER_DOWN_EN | m_DITHER_DOWN_MODE |
+ m_DITHER_DOWN_SEL;
+ val = v_DITHER_DOWN_EN(1) | v_DITHER_DOWN_MODE(1) |
+ v_DITHER_DOWN_SEL(1);
+ lcdc_msk_reg(ctx, DSP_CTRL1, mask, val);
+ break;
+ case OUT_P888:
+ face = OUT_P888;
+ mask = m_DITHER_DOWN_EN | m_DITHER_UP_EN;
+ val = v_DITHER_DOWN_EN(0) | v_DITHER_UP_EN(0);
+ lcdc_msk_reg(ctx, DSP_CTRL1, mask, val);
+ break;
+ default:
+ printk("un supported interface!\n");
+ break;
+ }
+ switch(screen->type){
+ case SCREEN_RGB:
+ case SCREEN_LVDS:
+ case SCREEN_DUAL_LVDS:
+ mask = m_RGB_OUT_EN;
+ val = v_RGB_OUT_EN(1);
+ v = 1 << (3+16);
+ v |= (ctx->lcdc_id << 3);
+ break;
+ case SCREEN_HDMI:
+ mask = m_HDMI_OUT_EN;
+ val = v_HDMI_OUT_EN(1);
+ /*v = 1 << (4+16);
+ v |= (ctx->id << 4);*/
+ break;
+ case SCREEN_MIPI:
+ mask = m_MIPI_OUT_EN;
+ val = v_MIPI_OUT_EN(1);
+ /*v = (1 << (6+16))||(1 << (9+16));
+ v |= (ctx->id << 6);
+ v |= (ctx->id << 9);*/
+ break;
+ case SCREEN_DUAL_MIPI:
+ mask = m_MIPI_OUT_EN | m_DOUB_CHANNEL_EN;
+ val = v_MIPI_OUT_EN(1) | v_DOUB_CHANNEL_EN(1);
+ /*v = (1 << (6+16))||(1 << (9+16));
+ v |= (ctx->id << 6);
+ v |= (ctx->id << 9);*/
+ break;
+ case SCREEN_EDP:
+ face = OUT_RGB_AAA; /*RGB AAA output*/
+ mask = m_DITHER_DOWN_EN | m_DITHER_UP_EN;
+ val = v_DITHER_DOWN_EN(0) | v_DITHER_UP_EN(0);
+ lcdc_msk_reg(ctx, DSP_CTRL1, mask, val);
+ mask = m_EDP_OUT_EN;
+ val = v_EDP_OUT_EN(1);
+ /*v = 1 << (5+16);
+ v |= (ctx->id << 5);*/
+ break;
+ }
+ lcdc_msk_reg(ctx, SYS_CTRL, mask, val);
+#ifdef USE_ION_MMU
+ mask = m_MMU_EN;
+ val = v_MMU_EN(1);
+ lcdc_msk_reg(ctx, SYS_CTRL, mask, val);
+ mask = m_AXI_MAX_OUTSTANDING_EN | m_AXI_OUTSTANDING_MAX_NUM;
+ val = v_AXI_OUTSTANDING_MAX_NUM(31) | v_AXI_MAX_OUTSTANDING_EN(1);
+ lcdc_msk_reg(ctx, SYS_CTRL1, mask, val);
+#endif
+ writel_relaxed(v, RK_GRF_VIRT + RK3288_GRF_SOC_CON6);
+ mask = m_DSP_OUT_MODE | m_DSP_HSYNC_POL | m_DSP_VSYNC_POL |
+ m_DSP_DEN_POL | m_DSP_DCLK_POL | m_DSP_BG_SWAP |
+ m_DSP_RB_SWAP | m_DSP_RG_SWAP | m_DSP_DELTA_SWAP |
+ m_DSP_DUMMY_SWAP | m_DSP_OUT_ZERO | m_DSP_BLANK_EN |
+ m_DSP_BLACK_EN | m_DSP_X_MIR_EN | m_DSP_Y_MIR_EN;
+ val = v_DSP_OUT_MODE(face) | v_DSP_HSYNC_POL(screen->pin_hsync) |
+ v_DSP_VSYNC_POL(screen->pin_vsync) |
+ v_DSP_DEN_POL(screen->pin_den) | v_DSP_DCLK_POL(screen->pin_dclk) |
+ v_DSP_BG_SWAP(screen->swap_gb) | v_DSP_RB_SWAP(screen->swap_rb) |
+ v_DSP_RG_SWAP(screen->swap_rg) |
+ v_DSP_DELTA_SWAP(screen->swap_delta) |
+ v_DSP_DUMMY_SWAP(screen->swap_dumy) | v_DSP_OUT_ZERO(0) |
+ v_DSP_BLANK_EN(0) | v_DSP_BLACK_EN(0) |
+ v_DSP_X_MIR_EN(screen->x_mirror) | v_DSP_Y_MIR_EN(screen->y_mirror);
+ lcdc_msk_reg(ctx, DSP_CTRL0, mask, val);
+
+ mask = m_DSP_BG_BLUE | m_DSP_BG_GREEN | m_DSP_BG_RED;
+ val = v_DSP_BG_BLUE(0x3ff) | v_DSP_BG_GREEN(0) | v_DSP_BG_RED(0);
+ lcdc_msk_reg(ctx, DSP_BG, mask, val);
+
+ mask = m_DSP_HS_PW | m_DSP_HTOTAL;
+ val = v_DSP_HS_PW(hsync_len) | v_DSP_HTOTAL(h_total);
+ lcdc_msk_reg(ctx, DSP_HTOTAL_HS_END, mask, val);
+
+ mask = m_DSP_HACT_END | m_DSP_HACT_ST;
+ val = v_DSP_HACT_END(hsync_len + left_margin + x_res) |
+ v_DSP_HACT_ST(hsync_len + left_margin);
+ lcdc_msk_reg(ctx, DSP_HACT_ST_END, mask, val);
+
+ mask = m_DSP_VS_PW | m_DSP_VTOTAL;
+ val = v_DSP_VS_PW(vsync_len) | v_DSP_VTOTAL(v_total);
+ lcdc_msk_reg(ctx, DSP_VTOTAL_VS_END, mask, val);
+
+ mask = m_DSP_VACT_END | m_DSP_VACT_ST;
+ val = v_DSP_VACT_END(vsync_len + upper_margin + y_res) |
+ v_DSP_VACT_ST(vsync_len + upper_margin);
+ lcdc_msk_reg(ctx, DSP_VACT_ST_END, mask, val);
+
+ rk3288_lcdc_post_cfg(ctx);
+#endif
+}
+
+static int fimd_enable_vblank(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ u32 val,mask;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ctx->suspended)
+ return -EPERM;
+
+ if (!test_and_set_bit(0, &ctx->irq_flags)) {
+ mask = m_FS_INTR_CLR | m_FS_INTR_EN | m_LINE_FLAG_INTR_CLR |
+ m_LINE_FLAG_INTR_EN | m_BUS_ERROR_INTR_CLR |
+ m_BUS_ERROR_INTR_EN | m_DSP_LINE_FLAG_NUM;
+ val = v_FS_INTR_CLR(1) | v_FS_INTR_EN(1) | v_LINE_FLAG_INTR_CLR(0) |
+ v_LINE_FLAG_INTR_EN(0) | v_BUS_ERROR_INTR_CLR(0) | v_BUS_ERROR_INTR_EN(0) ;
+ lcdc_msk_reg(ctx, INTR_CTRL0, mask, val);
+ lcdc_cfg_done(ctx);
+ }
+
+ return 0;
+}
+
+static void fimd_disable_vblank(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ u32 val,mask;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ctx->suspended)
+ return;
+
+ if (test_and_clear_bit(0, &ctx->irq_flags)) {
+ mask = m_DSP_HOLD_VALID_INTR_EN | m_FS_INTR_EN |
+ m_LINE_FLAG_INTR_EN | m_BUS_ERROR_INTR_EN;
+ val = v_DSP_HOLD_VALID_INTR_EN(0) | v_FS_INTR_EN(0) |
+ v_LINE_FLAG_INTR_EN(0) | v_BUS_ERROR_INTR_EN(0);
+ lcdc_msk_reg(ctx, INTR_CTRL0, mask, val);
+
+ mask = m_DSP_HOLD_VALID_INTR_CLR | m_FS_INTR_CLR |
+ m_LINE_FLAG_INTR_CLR | m_LINE_FLAG_INTR_CLR;
+ val = v_DSP_HOLD_VALID_INTR_CLR(0) | v_FS_INTR_CLR(0) |
+ v_LINE_FLAG_INTR_CLR(0) | v_BUS_ERROR_INTR_CLR(0);
+ lcdc_msk_reg(ctx, INTR_CTRL0, mask, val);
+
+ mask = m_WIN0_EMPTY_INTR_EN | m_WIN1_EMPTY_INTR_EN |
+ m_WIN2_EMPTY_INTR_EN | m_WIN3_EMPTY_INTR_EN |
+ m_HWC_EMPTY_INTR_EN | m_POST_BUF_EMPTY_INTR_EN |
+ m_POST_BUF_EMPTY_INTR_EN;
+ val = v_WIN0_EMPTY_INTR_EN(0) | v_WIN1_EMPTY_INTR_EN(0) |
+ v_WIN2_EMPTY_INTR_EN(0) | v_WIN3_EMPTY_INTR_EN(0) |
+ v_HWC_EMPTY_INTR_EN(0) | v_POST_BUF_EMPTY_INTR_EN(0) |
+ v_PWM_GEN_INTR_EN(0);
+ lcdc_msk_reg(ctx, INTR_CTRL1, mask, val);
+
+ mask = m_WIN0_EMPTY_INTR_CLR | m_WIN1_EMPTY_INTR_CLR |
+ m_WIN2_EMPTY_INTR_CLR | m_WIN3_EMPTY_INTR_CLR |
+ m_HWC_EMPTY_INTR_CLR | m_POST_BUF_EMPTY_INTR_CLR |
+ m_POST_BUF_EMPTY_INTR_CLR;
+ val = v_WIN0_EMPTY_INTR_CLR(0) | v_WIN1_EMPTY_INTR_CLR(0) |
+ v_WIN2_EMPTY_INTR_CLR(0) | v_WIN3_EMPTY_INTR_CLR(0) |
+ v_HWC_EMPTY_INTR_CLR(0) | v_POST_BUF_EMPTY_INTR_CLR(0) |
+ v_PWM_GEN_INTR_CLR(0);
+ lcdc_msk_reg(ctx, INTR_CTRL1, mask, val);
+ lcdc_cfg_done(ctx);
+
+
+ }
+}
+
+static void fimd_wait_for_vblank(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+
+ if (ctx->suspended)
+ return;
+
+ atomic_set(&ctx->wait_vsync_event, 1);
+
+ /*
+ * wait for FIMD to signal VSYNC interrupt or return after
+ * timeout which is set to 50ms (refresh rate of 20).
+ */
+ if (!wait_event_timeout(ctx->wait_vsync_queue,
+ !atomic_read(&ctx->wait_vsync_event),
+ DRM_HZ/20))
+ DRM_DEBUG_KMS("vblank wait timed out.\n");
+}
+
+static struct rockchip_drm_manager_ops fimd_manager_ops = {
+ .dpms = fimd_dpms,
+ .apply = fimd_apply,
+ .commit = fimd_commit,
+ .enable_vblank = fimd_enable_vblank,
+ .disable_vblank = fimd_disable_vblank,
+ .wait_for_vblank = fimd_wait_for_vblank,
+};
+
+static void fimd_win_mode_set(struct device *dev,
+ struct rockchip_drm_overlay *overlay)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data;
+ int win;
+ unsigned long offset;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!overlay) {
+ dev_err(dev, "overlay is NULL\n");
+ return;
+ }
+
+ win = overlay->zpos;
+ if (win == DEFAULT_ZPOS)
+ win = ctx->default_win;
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ offset = overlay->fb_x * (overlay->bpp >> 3);
+ offset += overlay->fb_y * overlay->pitch;
+
+ DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch);
+
+ win_data = &ctx->win_data[win];
+
+ win_data->offset_x = overlay->crtc_x;
+ win_data->offset_y = overlay->crtc_y;
+ win_data->ovl_width = overlay->crtc_width;
+ win_data->ovl_height = overlay->crtc_height;
+ win_data->fb_width = overlay->fb_width;
+ win_data->fb_height = overlay->fb_height;
+ win_data->dma_addr = overlay->dma_addr[0] + offset;
+ win_data->bpp = overlay->bpp;
+ win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
+ (overlay->bpp >> 3);
+ win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3);
+
+ DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
+ win_data->offset_x, win_data->offset_y);
+ DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
+ win_data->ovl_width, win_data->ovl_height);
+ DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr);
+ DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
+ overlay->fb_width, overlay->crtc_width);
+}
+
+static void fimd_win_set_pixfmt(struct device *dev, unsigned int win)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data = &ctx->win_data[win];
+ u8 fmt_cfg = 0;
+
+#if 0
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+ switch(win_data->bpp){
+ case 32:
+ fmt_cfg = 0;
+ break;
+ case 24:
+ fmt_cfg = 1;
+ break;
+ case 16:
+ fmt_cfg = 2;
+ break;
+ default:
+ printk("not support format %d\n",win_data->bpp);
+ break;
+ }
+
+
+ printk(KERN_ERR"------>yzq %d SYS_CTRL=%x \n",__LINE__,lcdc_readl(ctx,SYS_CTRL));
+ lcdc_msk_reg(ctx , SYS_CTRL, m_WIN0_FORMAT, v_WIN0_FORMAT(fmt_cfg));
+ printk(KERN_ERR"------>yzq %d SYS_CTRL=%x \n",__LINE__,lcdc_readl(ctx,SYS_CTRL));
+#endif
+}
+
+static void fimd_win_set_colkey(struct device *dev, unsigned int win)
+{
+// struct fimd_context *ctx = get_fimd_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+}
+
+static void fimd_win_commit(struct device *dev, int zpos)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data;
+ struct rk_screen *screen = ctx->screen;
+ int win = zpos;
+ unsigned long val, size;
+ u32 xpos, ypos;
+
+// printk(KERN_ERR"%s %d\n", __func__,__LINE__);
+
+ if (ctx->suspended)
+ return;
+
+ if (!ctx->clkon)
+ return;
+// printk(KERN_ERR"%s %d\n", __func__,__LINE__);
+ if (win == DEFAULT_ZPOS)
+ win = ctx->default_win;
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ win_data = &ctx->win_data[win];
+ switch(win){
+ case 0:
+ rk3288_win_0_1_reg_update(ctx,0);
+ break;
+ case 1:
+// printk(KERN_ERR"-->yzq dma_addr=%x buf_offsize=%x win_data->fb_width=%d \nwin_data->fb_height=%d win_data->ovl_height=%d win_data->ovl_width=%d \n win_data->offset_x=%d win_data->offset_y=%d win_data->line_size=%d\n win_data->bpp=%d ",win_data->dma_addr,win_data->buf_offsize,win_data->fb_width,win_data->fb_height,win_data->ovl_height, win_data->ovl_width,win_data->offset_x,win_data->offset_y,win_data->line_size,win_data->bpp);
+ rk3288_win_0_1_reg_update(ctx,1);
+ break;
+ case 2:
+ printk("----->yzq not support now win_id=%d\n",win);
+ // rk3288_win_2_3_reg_update(ctx,2);
+ break;
+ case 3:
+ printk("----->yzq not support now win_id=%d\n",win);
+ // rk3288_win_2_3_reg_update(ctx,3);
+ break;
+ default:
+ printk("----->yzq not support now win_id=%d\n",win);
+ break;
+ }
+// printk("----->yzq now win_id=%d\n",win);
+
+ //rk3288_lcdc_post_cfg(screen);
+ win_data->enabled = true;
+
+}
+
+static void fimd_win_disable(struct device *dev, int zpos)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data;
+ int win = zpos;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (win == DEFAULT_ZPOS)
+ win = ctx->default_win;
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ win_data = &ctx->win_data[win];
+
+ if (ctx->suspended) {
+ /* do not resume this window*/
+ win_data->resume = false;
+ return;
+ }
+
+ win_data->enabled = false;
+}
+
+static struct rockchip_drm_overlay_ops fimd_overlay_ops = {
+ .mode_set = fimd_win_mode_set,
+ .commit = fimd_win_commit,
+ .disable = fimd_win_disable,
+};
+
+static struct rockchip_drm_manager fimd_manager = {
+ .pipe = -1,
+ .ops = &fimd_manager_ops,
+ .overlay_ops = &fimd_overlay_ops,
+ .display_ops = &fimd_display_ops,
+};
+
+static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
+{
+ struct fimd_context *ctx = (struct fimd_context *)dev_id;
+ struct rockchip_drm_subdrv *subdrv = &ctx->subdrv;
+ struct drm_device *drm_dev = subdrv->drm_dev;
+ struct rockchip_drm_manager *manager = subdrv->manager;
+ u32 intr0_reg;
+
+ intr0_reg = lcdc_readl(ctx, INTR_CTRL0);
+
+ if(intr0_reg & m_FS_INTR_STS){
+ lcdc_msk_reg(ctx, INTR_CTRL0, m_FS_INTR_CLR,
+ v_FS_INTR_CLR(1));
+
+ }
+
+ /* check the crtc is detached already from encoder */
+ if (manager->pipe < 0)
+ goto out;
+
+ drm_handle_vblank(drm_dev, manager->pipe);
+ rockchip_drm_crtc_finish_pageflip(drm_dev, manager->pipe);
+
+ /* set wait vsync event to zero and wake up queue. */
+ if (atomic_read(&ctx->wait_vsync_event)) {
+ atomic_set(&ctx->wait_vsync_event, 0);
+ DRM_WAKEUP(&ctx->wait_vsync_queue);
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * enable drm irq mode.
+ * - with irq_enabled = 1, we can use the vblank feature.
+ *
+ * P.S. note that we wouldn't use drm irq handler but
+ * just specific driver own one instead because
+ * drm framework supports only one irq handler.
+ */
+ drm_dev->irq_enabled = 1;
+
+ /*
+ * with vblank_disable_allowed = 1, vblank interrupt will be disabled
+ * by drm timer once a current process gives up ownership of
+ * vblank event.(after drm_vblank_put function is called)
+ */
+ drm_dev->vblank_disable_allowed = 1;
+
+ /* attach this sub driver to iommu mapping if supported. */
+ if (is_drm_iommu_supported(drm_dev))
+ drm_iommu_attach_device(drm_dev, dev);
+
+ return 0;
+}
+
+static void fimd_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* detach this sub driver from iommu mapping if supported. */
+ if (is_drm_iommu_supported(drm_dev))
+ drm_iommu_detach_device(drm_dev, dev);
+}
+
+
+static void fimd_clear_win(struct fimd_context *ctx, int win)
+{
+ u32 val;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+}
+
+static int fimd_clock(struct fimd_context *ctx, bool enable)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+printk(KERN_ERR"---->yzq %s %d \n",__func__,__LINE__);
+ if (enable) {
+ if(ctx->clkon)
+ return 0;
+ int ret;
+
+ ret = clk_prepare_enable(ctx->hclk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(ctx->dclk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(ctx->aclk);
+ if (ret < 0)
+ return ret;
+ ctx->clkon=1;
+ } else {
+ if(!ctx->clkon)
+ return 0;
+ clk_disable_unprepare(ctx->aclk);
+ clk_disable_unprepare(ctx->dclk);
+ clk_disable_unprepare(ctx->hclk);
+ ctx->clkon=0;
+ }
+
+ return 0;
+}
+
+static void fimd_window_suspend(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data;
+ int i;
+
+ for (i = 0; i < WINDOWS_NR; i++) {
+ win_data = &ctx->win_data[i];
+ win_data->resume = win_data->enabled;
+ fimd_win_disable(dev, i);
+ }
+ fimd_wait_for_vblank(dev);
+}
+
+static void fimd_window_resume(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_win_data *win_data;
+ int i;
+
+ for (i = 0; i < WINDOWS_NR; i++) {
+ win_data = &ctx->win_data[i];
+ win_data->enabled = win_data->resume;
+ win_data->resume = false;
+ }
+}
+
+static int fimd_activate(struct fimd_context *ctx, bool enable)
+{
+ struct device *dev = ctx->subdrv.dev;
+ if (enable) {
+ int ret;
+
+ ret = fimd_clock(ctx, true);
+ if (ret < 0)
+ return ret;
+
+ ctx->suspended = false;
+
+ /* if vblank was enabled status, enable it again. */
+ if (test_and_clear_bit(0, &ctx->irq_flags))
+ fimd_enable_vblank(dev);
+
+ fimd_window_resume(dev);
+ } else {
+ fimd_window_suspend(dev);
+
+ fimd_clock(ctx, false);
+ ctx->suspended = true;
+ }
+
+ return 0;
+}
+
+int rk_fb_video_mode_from_timing(const struct display_timing *dt,
+ struct rk_screen *screen)
+{
+ screen->mode.pixclock = dt->pixelclock.typ;
+ screen->mode.left_margin = dt->hback_porch.typ;
+ screen->mode.right_margin = dt->hfront_porch.typ;
+ screen->mode.xres = dt->hactive.typ;
+ screen->mode.hsync_len = dt->hsync_len.typ;
+ screen->mode.upper_margin = dt->vback_porch.typ;
+ screen->mode.lower_margin = dt->vfront_porch.typ;
+ screen->mode.yres = dt->vactive.typ;
+ screen->mode.vsync_len = dt->vsync_len.typ;
+ screen->type = SCREEN_LVDS;
+ screen->lvds_format = LVDS_8BIT_2;
+ screen->face = OUT_D888_P666;
+
+ if (dt->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
+ screen->pin_dclk = 1;
+ else
+ screen->pin_dclk = 0;
+ if(dt->flags & DISPLAY_FLAGS_HSYNC_HIGH)
+ screen->pin_hsync = 1;
+ else
+ screen->pin_hsync = 0;
+ if(dt->flags & DISPLAY_FLAGS_VSYNC_HIGH)
+ screen->pin_vsync = 1;
+ else
+ screen->pin_vsync = 0;
+ if(dt->flags & DISPLAY_FLAGS_DE_HIGH)
+ screen->pin_den = 1;
+ else
+ screen->pin_den = 0;
+
+ return 0;
+
+}
+
+int rk_fb_prase_timing_dt(struct device_node *np, struct rk_screen *screen)
+{
+ struct display_timings *disp_timing;
+ struct display_timing *dt;
+ disp_timing = of_get_display_timings(np);
+ if (!disp_timing) {
+ pr_err("parse display timing err\n");
+ return -EINVAL;
+ }
+ dt = display_timings_get(disp_timing, 0);
+ rk_fb_video_mode_from_timing(dt, screen);
+ printk(KERN_ERR "dclk:%d\n"
+ "hactive:%d\n"
+ "hback_porch:%d\n"
+ "hfront_porch:%d\n"
+ "hsync_len:%d\n"
+ "vactive:%d\n"
+ "vback_porch:%d\n"
+ "vfront_porch:%d\n"
+ "vsync_len:%d\n"
+ "screen_type:%d\n"
+ "lvds_format:%d\n"
+ "face:%d\n",
+ dt->pixelclock.typ,
+ dt->hactive.typ,
+ dt->hback_porch.typ,
+ dt->hfront_porch.typ,
+ dt->hsync_len.typ,
+ dt->vactive.typ,
+ dt->vback_porch.typ,
+ dt->vfront_porch.typ,
+ dt->vsync_len.typ,
+ dt->screen_type,
+ dt->lvds_format,
+ dt->face);
+ return 0;
+
+}
+
+static int fimd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fimd_context *ctx;
+ struct rockchip_drm_subdrv *subdrv;
+ struct rockchip_drm_fimd_pdata *pdata;
+ struct rockchip_drm_panel_info *panel;
+ struct device_node *np = pdev->dev.of_node;
+ struct rk_screen *screen;
+ int prop;
+ int reg_len;
+ struct resource *res;
+ int win;
+ int val;
+ int ret = -EINVAL;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+ of_property_read_u32(np, "rockchip,prop", &prop);
+ if (prop == EXTEND) {
+ printk("---->%s not support lcdc EXTEND now\n",__func__);
+ return 0;
+ }
+ if (of_property_read_u32(np, "rockchip,pwr18", &val))
+ {
+ printk("----->%s default set it as 3.xv power supply",__func__);
+ }
+ else{
+ if(val){
+ printk("----->%s lcdc pwr is 1.8, not supply now",__func__);
+ }else{
+ printk("----->%s lcdc pwr is 3.3v",__func__);
+ }
+ }
+
+ if (dev->of_node) {
+ panel = devm_kzalloc(dev, sizeof(struct rockchip_drm_panel_info), GFP_KERNEL);
+ screen = devm_kzalloc(dev, sizeof(struct rk_screen), GFP_KERNEL);
+ rk_fb_get_prmry_screen(screen);
+ memcpy(&panel->timing,&screen->mode,sizeof(struct fb_videomode));
+ } else {
+ DRM_ERROR("no platform data specified\n");
+ return -EINVAL;
+ }
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ ctx->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctx->regs))
+ return PTR_ERR(ctx->regs);
+ reg_len = resource_size(res);
+ ctx->regsbak = devm_kzalloc(dev,reg_len,GFP_KERNEL);
+ ctx->lcdc_id = rk3288_lcdc_get_id(res->start);
+ ctx->screen = screen;
+ ctx->hclk = devm_clk_get(dev, "hclk_lcdc");
+ ctx->aclk = devm_clk_get(dev, "aclk_lcdc");
+ ctx->dclk = devm_clk_get(dev, "dclk_lcdc");
+
+ ctx->irq = platform_get_irq(pdev, 0);
+ if (ctx->irq < 0) {
+ dev_err(dev, "cannot find IRQ for lcdc%d\n",
+ ctx->lcdc_id);
+ return -ENXIO;
+ }
+ ret = devm_request_irq(dev, ctx->irq, fimd_irq_handler,
+ 0, "drm_fimd", ctx);
+ if (ret) {
+ dev_err(dev, "irq request failed.\n");
+ return ret;
+ }
+
+ ctx->default_win = 0;// pdata->default_win;
+ ctx->panel = panel;
+ DRM_INIT_WAITQUEUE(&ctx->wait_vsync_queue);
+ atomic_set(&ctx->wait_vsync_event, 0);
+
+ subdrv = &ctx->subdrv;
+
+ subdrv->dev = dev;
+ subdrv->manager = &fimd_manager;
+ subdrv->probe = fimd_subdrv_probe;
+ subdrv->remove = fimd_subdrv_remove;
+
+ mutex_init(&ctx->lock);
+
+ platform_set_drvdata(pdev, ctx);
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ ret = clk_set_rate(ctx->dclk, ctx->screen->mode.pixclock);
+ if (ret)
+ printk( "set lcdc%d dclk failed\n", ctx->lcdc_id);
+
+ fimd_activate(ctx, true);
+
+ if(trsm_lvds_ops != NULL){
+ printk(KERN_ERR"------>yzq enable lvds\n");
+ trsm_lvds_ops->enable();
+ }
+ memcpy(ctx->regsbak,ctx->regs,reg_len);
+ rockchip_drm_subdrv_register(subdrv);
+
+ return 0;
+}
+
+static int fimd_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fimd_context *ctx = platform_get_drvdata(pdev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ rockchip_drm_subdrv_unregister(&ctx->subdrv);
+
+ if (ctx->suspended)
+ goto out;
+
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_sync(dev);
+
+out:
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fimd_suspend(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+
+ /*
+ * do not use pm_runtime_suspend(). if pm_runtime_suspend() is
+ * called here, an error would be returned by that interface
+ * because the usage_count of pm runtime is more than 1.
+ */
+ if (!pm_runtime_suspended(dev))
+ return fimd_activate(ctx, false);
+
+ return 0;
+}
+
+static int fimd_resume(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+
+ /*
+ * if entered to sleep when lcd panel was on, the usage_count
+ * of pm runtime would still be 1 so in this case, fimd driver
+ * should be on directly not drawing on pm runtime interface.
+ */
+ if (!pm_runtime_suspended(dev)) {
+ int ret;
+
+ ret = fimd_activate(ctx, true);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * in case of dpms on(standby), fimd_apply function will
+ * be called by encoder's dpms callback to update fimd's
+ * registers but in case of sleep wakeup, it's not.
+ * so fimd_apply function should be called at here.
+ */
+ fimd_apply(dev);
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int fimd_runtime_suspend(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ return fimd_activate(ctx, false);
+}
+
+static int fimd_runtime_resume(struct device *dev)
+{
+ struct fimd_context *ctx = get_fimd_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ return fimd_activate(ctx, true);
+}
+#endif
+#if defined(CONFIG_OF)
+static const struct of_device_id rk3288_lcdc_dt_ids[] = {
+ {.compatible = "rockchip,rk3288-lcdc",},
+ {}
+};
+#endif
+
+static const struct dev_pm_ops fimd_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume)
+ SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL)
+};
+
+struct platform_driver fimd_driver = {
+ .probe = fimd_probe,
+ .remove = fimd_remove,
+ .id_table = rk3288_lcdc_dt_ids,
+ .driver = {
+ .name = "rk3288-lcdc",
+ .owner = THIS_MODULE,
+ .pm = &fimd_pm_ops,
+ .of_match_table = of_match_ptr(rk3288_lcdc_dt_ids),
+ },
+};
--- /dev/null
+/********************************************************************
+** display output interface supported by rockchip lcdc *
+********************************************************************/
+/* */
+#define OUT_P888 0 //24bit screen,connect to lcdc D0~D23
+#define OUT_P666 1 //18bit screen,connect to lcdc D0~D17
+#define OUT_P565 2
+#define OUT_S888x 4
+#define OUT_CCIR656 6
+#define OUT_S888 8
+#define OUT_S888DUMY 12
+#define OUT_RGB_AAA 15
+#define OUT_P16BPP4 24
+#define OUT_D888_P666 0x21 //18bit screen,connect to lcdc D2~D7, D10~D15, D18~D23
+#define OUT_D888_P565 0x22
+
+/*******************register definition**********************/
+
+#define REG_CFG_DONE (0x0000)
+#define VERSION_INFO (0x0004)
+#define m_RTL_VERSION (0xffff<<0)
+#define m_FPGA_VERSION (0xffff<<16)
+#define SYS_CTRL (0x0008)
+#define v_DIRECT_PATH_EN(x) (((x)&1)<<0)
+#define v_DIRECT_PATCH_SEL(x) (((x)&3)<<1)
+#define v_DOUB_CHANNEL_EN(x) (((x)&1)<<3)
+#define v_DOUB_CH_OVERLAP_NUM(x) (((x)&0xf)<<4)
+#define v_EDPI_HALT_EN(x) (((x)&1)<<8)
+#define v_EDPI_WMS_MODE(x) (((x)&1)<<9)
+#define v_EDPI_WMS_FS(x) (((x)&1)<<10)
+#define v_RGB_OUT_EN(x) (((x)&1)<<12)
+#define v_HDMI_OUT_EN(x) (((x)&1)<<13)
+#define v_EDP_OUT_EN(x) (((x)&1)<<14)
+#define v_MIPI_OUT_EN(x) (((x)&1)<<15)
+#define v_DMA_BURST_LENGTH(x) (((x)&3)<<18)
+#define v_MMU_EN(x) (((x)&1)<<20)
+#define v_DMA_STOP(x) (((x)&1)<<21)
+#define v_STANDBY_EN(x) (((x)&1)<<22)
+#define v_AUTO_GATING_EN(x) (((x)&1)<<23)
+
+#define m_DIRECT_PATH_EN (1<<0)
+#define m_DIRECT_PATCH_SEL (3<<1)
+#define m_DOUB_CHANNEL_EN (1<<3)
+#define m_DOUB_CH_OVERLAP_NUM (0xf<<4)
+#define m_EDPI_HALT_EN (1<<8)
+#define m_EDPI_WMS_MODE (1<<9)
+#define m_EDPI_WMS_FS (1<<10)
+#define m_RGB_OUT_EN (1<<12)
+#define m_HDMI_OUT_EN (1<<13)
+#define m_EDP_OUT_EN (1<<14)
+#define m_MIPI_OUT_EN (1<<15)
+#define m_DMA_BURST_LENGTH (3<<18)
+#define m_MMU_EN (1<<20)
+#define m_DMA_STOP (1<<21)
+#define m_STANDBY_EN (1<<22)
+#define m_AUTO_GATING_EN (1<<23)
+#define SYS_CTRL1 (0x000c)
+#define v_NOC_HURRY_EN(x) (((x)&0x1 )<<0 )
+#define v_NOC_HURRY_VALUE(x) (((x)&0x3 )<<1 )
+#define v_NOC_HURRY_THRESHOLD(x) (((x)&0x3f)<<3 )
+#define v_NOC_QOS_EN(x) (((x)&0x1 )<<9 )
+#define v_NOC_WIN_QOS(x) (((x)&0x3 )<<10)
+#define v_AXI_MAX_OUTSTANDING_EN(x) (((x)&0x1 )<<12)
+#define v_AXI_OUTSTANDING_MAX_NUM(x) (((x)&0x1f)<<13)
+
+#define m_NOC_HURRY_EN (0x1 <<0 )
+#define m_NOC_HURRY_VALUE (0x3 <<1 )
+#define m_NOC_HURRY_THRESHOLD (0x3f<<3 )
+#define m_NOC_QOS_EN (0x1 <<9 )
+#define m_NOC_WIN_QOS (0x3 <<10)
+#define m_AXI_MAX_OUTSTANDING_EN (0x1 <<12)
+#define m_AXI_OUTSTANDING_MAX_NUM (0x1f<<13)
+
+#define DSP_CTRL0 (0x0010)
+#define v_DSP_OUT_MODE(x) (((x)&0x0f)<<0)
+#define v_DSP_HSYNC_POL(x) (((x)&1)<<4)
+#define v_DSP_VSYNC_POL(x) (((x)&1)<<5)
+#define v_DSP_DEN_POL(x) (((x)&1)<<6)
+#define v_DSP_DCLK_POL(x) (((x)&1)<<7)
+#define v_DSP_DCLK_DDR(x) (((x)&1)<<8)
+#define v_DSP_DDR_PHASE(x) (((x)&1)<<9)
+#define v_DSP_INTERLACE(x) (((x)&1)<<10)
+#define v_DSP_FIELD_POL(x) (((x)&1)<<11)
+#define v_DSP_BG_SWAP(x) (((x)&1)<<12)
+#define v_DSP_RB_SWAP(x) (((x)&1)<<13)
+#define v_DSP_RG_SWAP(x) (((x)&1)<<14)
+#define v_DSP_DELTA_SWAP(x) (((x)&1)<<15)
+#define v_DSP_DUMMY_SWAP(x) (((x)&1)<<16)
+#define v_DSP_OUT_ZERO(x) (((x)&1)<<17)
+#define v_DSP_BLANK_EN(x) (((x)&1)<<18)
+#define v_DSP_BLACK_EN(x) (((x)&1)<<19)
+#define v_DSP_CCIR656_AVG(x) (((x)&1)<<20)
+#define v_DSP_YUV_CLIP(x) (((x)&1)<<21)
+#define v_DSP_X_MIR_EN(x) (((x)&1)<<22)
+#define v_DSP_Y_MIR_EN(x) (((x)&1)<<23)
+#define m_DSP_OUT_MODE (0x0f<<0)
+#define m_DSP_HSYNC_POL (1<<4)
+#define m_DSP_VSYNC_POL (1<<5)
+#define m_DSP_DEN_POL (1<<6)
+#define m_DSP_DCLK_POL (1<<7)
+#define m_DSP_DCLK_DDR (1<<8)
+#define m_DSP_DDR_PHASE (1<<9)
+#define m_DSP_INTERLACE (1<<10)
+#define m_DSP_FIELD_POL (1<<11)
+#define m_DSP_BG_SWAP (1<<12)
+#define m_DSP_RB_SWAP (1<<13)
+#define m_DSP_RG_SWAP (1<<14)
+#define m_DSP_DELTA_SWAP (1<<15)
+#define m_DSP_DUMMY_SWAP (1<<16)
+#define m_DSP_OUT_ZERO (1<<17)
+#define m_DSP_BLANK_EN (1<<18)
+#define m_DSP_BLACK_EN (1<<19)
+#define m_DSP_CCIR656_AVG (1<<20)
+#define m_DSP_YUV_CLIP (1<<21)
+#define m_DSP_X_MIR_EN (1<<22)
+#define m_DSP_Y_MIR_EN (1<<23)
+
+#define DSP_CTRL1 (0x0014)
+#define v_DSP_LUT_EN(x) (((x)&1)<<0)
+#define v_PRE_DITHER_DOWN_EN(x) (((x)&1)<<1)
+#define v_DITHER_DOWN_EN(x) (((x)&1)<<2)
+#define v_DITHER_DOWN_MODE(x) (((x)&1)<<3)
+#define v_DITHER_DOWN_SEL(x) (((x)&1)<<4)
+#define v_DITHER_UP_EN(x) (((x)&1)<<6)
+#define v_DSP_LAYER0_SEL(x) (((x)&3)<<8)
+#define v_DSP_LAYER1_SEL(x) (((x)&3)<<10)
+#define v_DSP_LAYER2_SEL(x) (((x)&3)<<12)
+#define v_DSP_LAYER3_SEL(x) (((x)&3)<<14)
+#define m_DSP_LUT_EN (1<<0)
+#define m_PRE_DITHER_DOWN_EN (1<<1)
+#define m_DITHER_DOWN_EN (1<<2)
+#define m_DITHER_DOWN_MODE (1<<3)
+#define m_DITHER_DOWN_SEL (1<<4)
+#define m_DITHER_UP_EN (1<<6)
+#define m_DSP_LAYER0_SEL (3<<8)
+#define m_DSP_LAYER1_SEL (3<<10)
+#define m_DSP_LAYER2_SEL (3<<12)
+#define m_DSP_LAYER3_SEL (3<<16)
+
+#define DSP_BG (0x0018)
+#define v_DSP_BG_BLUE(x) (((x)&0x3ff)<<0)
+#define v_DSP_BG_GREEN(x) (((x)&0x3ff)<<10)
+#define v_DSP_BG_RED(x) (((x)&0x3ff)<<20)
+#define m_DSP_BG_BLUE (0x3ff<<0)
+#define m_DSP_BG_GREEN (0x3ff<<10)
+#define m_DSP_BG_RED (0x3ff<<20)
+
+#define MCU_CTRL (0x001c)
+#define v_MCU_PIX_TOTAL(x) (((x)&0x3f)<<0)
+#define v_MCU_CS_PST(x) (((x)&0xf)<<6)
+#define v_MCU_CS_PEND(x) (((x)&0x3f)<<10)
+#define v_MCU_RW_PST(x) (((x)&0xf)<<16)
+#define v_MCU_RW_PEND(x) (((x)&0x3f)<<20)
+#define v_MCU_CLK_SEL(x) (((x)&1)<<26)
+#define v_MCU_HOLD_MODE(x) (((x)&1)<<27)
+#define v_MCU_FRAME_ST(x) (((x)&1)<<28)
+#define v_MCU_RS(x) (((x)&1)<<29)
+#define v_MCU_BYPASS(x) (((x)&1)<<30)
+#define v_MCU_TYPE(x) (((x)&1)<<31)
+#define m_MCU_PIX_TOTAL (0x3f<<0)
+#define m_MCU_CS_PST (0xf<<6)
+#define m_MCU_CS_PEND (0x3f<<10)
+#define m_MCU_RW_PST (0xf<<16)
+#define m_MCU_RW_PEND (0x3f<<20)
+#define m_MCU_CLK_SEL (1<<26)
+#define m_MCU_HOLD_MODE (1<<27)
+#define m_MCU_FRAME_ST (1<<28)
+#define m_MCU_RS (1<<29)
+#define m_MCU_BYPASS (1<<30)
+#define m_MCU_TYPE ((u32)1<<31)
+
+#define INTR_CTRL0 (0x0020)
+#define v_DSP_HOLD_VALID_INTR_STS(x) (((x)&1)<<0)
+#define v_FS_INTR_STS(x) (((x)&1)<<1)
+#define v_LINE_FLAG_INTR_STS(x) (((x)&1)<<2)
+#define v_BUS_ERROR_INTR_STS(x) (((x)&1)<<3)
+#define v_DSP_HOLD_VALID_INTR_EN(x) (((x)&1)<<4)
+#define v_FS_INTR_EN(x) (((x)&1)<<5)
+#define v_LINE_FLAG_INTR_EN(x) (((x)&1)<<6)
+#define v_BUS_ERROR_INTR_EN(x) (((x)&1)<<7)
+#define v_DSP_HOLD_VALID_INTR_CLR(x) (((x)&1)<<8)
+#define v_FS_INTR_CLR(x) (((x)&1)<<9)
+#define v_LINE_FLAG_INTR_CLR(x) (((x)&1)<<10)
+#define v_BUS_ERROR_INTR_CLR(x) (((x)&1)<<11)
+#define v_DSP_LINE_FLAG_NUM(x) (((x)&0xfff)<<12)
+
+#define m_DSP_HOLD_VALID_INTR_STS (1<<0)
+#define m_FS_INTR_STS (1<<1)
+#define m_LINE_FLAG_INTR_STS (1<<2)
+#define m_BUS_ERROR_INTR_STS (1<<3)
+#define m_DSP_HOLD_VALID_INTR_EN (1<<4)
+#define m_FS_INTR_EN (1<<5)
+#define m_LINE_FLAG_INTR_EN (1<<6)
+#define m_BUS_ERROR_INTR_EN (1<<7)
+#define m_DSP_HOLD_VALID_INTR_CLR (1<<8)
+#define m_FS_INTR_CLR (1<<9)
+#define m_LINE_FLAG_INTR_CLR (1<<10)
+#define m_BUS_ERROR_INTR_CLR (1<<11)
+#define m_DSP_LINE_FLAG_NUM (0xfff<<12)
+
+#define INTR_CTRL1 (0x0024)
+#define v_WIN0_EMPTY_INTR_STS(x) (((x)&1)<<0)
+#define v_WIN1_EMPTY_INTR_STS(x) (((x)&1)<<1)
+#define v_WIN2_EMPTY_INTR_STS(x) (((x)&1)<<2)
+#define v_WIN3_EMPTY_INTR_STS(x) (((x)&1)<<3)
+#define v_HWC_EMPTY_INTR_STS(x) (((x)&1)<<4)
+#define v_POST_BUF_EMPTY_INTR_STS(x) (((x)&1)<<5)
+#define v_PWM_GEN_INTR_STS(x) (((x)&1)<<6)
+#define v_WIN0_EMPTY_INTR_EN(x) (((x)&1)<<8)
+#define v_WIN1_EMPTY_INTR_EN(x) (((x)&1)<<9)
+#define v_WIN2_EMPTY_INTR_EN(x) (((x)&1)<<10)
+#define v_WIN3_EMPTY_INTR_EN(x) (((x)&1)<<11)
+#define v_HWC_EMPTY_INTR_EN(x) (((x)&1)<<12)
+#define v_POST_BUF_EMPTY_INTR_EN(x) (((x)&1)<<13)
+#define v_PWM_GEN_INTR_EN(x) (((x)&1)<<14)
+#define v_WIN0_EMPTY_INTR_CLR(x) (((x)&1)<<16)
+#define v_WIN1_EMPTY_INTR_CLR(x) (((x)&1)<<17)
+#define v_WIN2_EMPTY_INTR_CLR(x) (((x)&1)<<18)
+#define v_WIN3_EMPTY_INTR_CLR(x) (((x)&1)<<19)
+#define v_HWC_EMPTY_INTR_CLR(x) (((x)&1)<<20)
+#define v_POST_BUF_EMPTY_INTR_CLR(x) (((x)&1)<<21)
+#define v_PWM_GEN_INTR_CLR(x) (((x)&1)<<22)
+
+#define m_WIN0_EMPTY_INTR_STS (1<<0)
+#define m_WIN1_EMPTY_INTR_STS (1<<1)
+#define m_WIN2_EMPTY_INTR_STS (1<<2)
+#define m_WIN3_EMPTY_INTR_STS (1<<3)
+#define m_HWC_EMPTY_INTR_STS (1<<4)
+#define m_POST_BUF_EMPTY_INTR_STS (1<<5)
+#define m_PWM_GEN_INTR_STS (1<<6)
+#define m_WIN0_EMPTY_INTR_EN (1<<8)
+#define m_WIN1_EMPTY_INTR_EN (1<<9)
+#define m_WIN2_EMPTY_INTR_EN (1<<10)
+#define m_WIN3_EMPTY_INTR_EN (1<<11)
+#define m_HWC_EMPTY_INTR_EN (1<<12)
+#define m_POST_BUF_EMPTY_INTR_EN (1<<13)
+#define m_PWM_GEN_INTR_EN (1<<14)
+#define m_WIN0_EMPTY_INTR_CLR (1<<16)
+#define m_WIN1_EMPTY_INTR_CLR (1<<17)
+#define m_WIN2_EMPTY_INTR_CLR (1<<18)
+#define m_WIN3_EMPTY_INTR_CLR (1<<19)
+#define m_HWC_EMPTY_INTR_CLR (1<<20)
+#define m_POST_BUF_EMPTY_INTR_CLR (1<<21)
+#define m_PWM_GEN_INTR_CLR (1<<22)
+
+/*win0 register*/
+#define WIN0_CTRL0 (0x0030)
+#define v_WIN0_EN(x) (((x)&1)<<0)
+#define v_WIN0_DATA_FMT(x) (((x)&7)<<1)
+#define v_WIN0_FMT_10(x) (((x)&1)<<4)
+#define v_WIN0_LB_MODE(x) (((x)&7)<<5)
+#define v_WIN0_INTERLACE_READ(x) (((x)&1)<<8)
+#define v_WIN0_NO_OUTSTANDING(x) (((x)&1)<<9)
+#define v_WIN0_CSC_MODE(x) (((x)&3)<<10)
+#define v_WIN0_RB_SWAP(x) (((x)&1)<<12)
+#define v_WIN0_ALPHA_SWAP(x) (((x)&1)<<13)
+#define v_WIN0_MID_SWAP(x) (((x)&1)<<14)
+#define v_WIN0_UV_SWAP(x) (((x)&1)<<15)
+#define v_WIN0_PPAS_ZERO_EN(x) (((x)&1)<<16)
+#define v_WIN0_YRGB_DEFLICK(x) (((x)&1)<<18)
+#define v_WIN0_CBR_DEFLICK(x) (((x)&1)<<19)
+#define v_WIN0_YUV_CLIP(x) (((x)&1)<<20)
+
+#define m_WIN0_EN (1<<0)
+#define m_WIN0_DATA_FMT (7<<1)
+#define m_WIN0_FMT_10 (1<<4)
+#define m_WIN0_LB_MODE (3<<5)
+#define m_WIN0_INTERLACE_READ (1<<8)
+#define m_WIN0_NO_OUTSTANDING (1<<9)
+#define m_WIN0_CSC_MODE (3<<10)
+#define m_WIN0_RB_SWAP (1<<12)
+#define m_WIN0_ALPHA_SWAP (1<<13)
+#define m_WIN0_MID_SWAP (1<<14)
+#define m_WIN0_UV_SWAP (1<<15)
+#define m_WIN0_PPAS_ZERO_EN (1<<16)
+#define m_WIN0_YRGB_DEFLICK (1<<18)
+#define m_WIN0_CBR_DEFLICK (1<<19)
+#define m_WIN0_YUV_CLIP (1<<20)
+
+#define WIN0_CTRL1 (0x0034)
+#define v_WIN0_YRGB_AXI_GATHER_EN(x) (((x)&1)<<0)
+#define v_WIN0_CBR_AXI_GATHER_EN(x) (((x)&1)<<1)
+#define v_WIN0_BIC_COE_SEL(x) (((x)&3)<<2)
+#define v_WIN0_VSD_YRGB_GT4(x) (((x)&1)<<4)
+#define v_WIN0_VSD_YRGB_GT2(x) (((x)&1)<<5)
+#define v_WIN0_VSD_CBR_GT4(x) (((x)&1)<<6)
+#define v_WIN0_VSD_CBR_GT2(x) (((x)&1)<<7)
+#define v_WIN0_YRGB_AXI_GATHER_NUM(x) (((x)&0xf)<<8)
+#define v_WIN0_CBR_AXI_GATHER_NUM(x) (((x)&7)<<12)
+#define v_WIN0_LINE_LOAD_MODE(x) (((x)&1)<<15)
+#define v_WIN0_YRGB_HOR_SCL_MODE(x) (((x)&3)<<16)
+#define v_WIN0_YRGB_VER_SCL_MODE(x) (((x)&3)<<18)
+#define v_WIN0_YRGB_HSD_MODE(x) (((x)&3)<<20)
+#define v_WIN0_YRGB_VSU_MODE(x) (((x)&1)<<22)
+#define v_WIN0_YRGB_VSD_MODE(x) (((x)&1)<<23)
+#define v_WIN0_CBR_HOR_SCL_MODE(x) (((x)&3)<<24)
+#define v_WIN0_CBR_VER_SCL_MODE(x) (((x)&3)<<26)
+#define v_WIN0_CBR_HSD_MODE(x) (((x)&3)<<28)
+#define v_WIN0_CBR_VSU_MODE(x) (((x)&1)<<30)
+#define v_WIN0_CBR_VSD_MODE(x) (((x)&1)<<31)
+
+#define m_WIN0_YRGB_AXI_GATHER_EN (1<<0)
+#define m_WIN0_CBR_AXI_GATHER_EN (1<<1)
+#define m_WIN0_BIC_COE_SEL (3<<2)
+#define m_WIN0_VSD_YRGB_GT4 (1<<4)
+#define m_WIN0_VSD_YRGB_GT2 (1<<5)
+#define m_WIN0_VSD_CBR_GT4 (1<<6)
+#define m_WIN0_VSD_CBR_GT2 (1<<7)
+#define m_WIN0_YRGB_AXI_GATHER_NUM (0xf<<8)
+#define m_WIN0_CBR_AXI_GATHER_NUM (7<<12)
+#define m_WIN0_LINE_LOAD_MODE (1<<15)
+#define m_WIN0_YRGB_HOR_SCL_MODE (3<<16)
+#define m_WIN0_YRGB_VER_SCL_MODE (3<<18)
+#define m_WIN0_YRGB_HSD_MODE (3<<20)
+#define m_WIN0_YRGB_VSU_MODE (1<<22)
+#define m_WIN0_YRGB_VSD_MODE (1<<23)
+#define m_WIN0_CBR_HOR_SCL_MODE (3<<24)
+#define m_WIN0_CBR_VER_SCL_MODE (3<<26)
+#define m_WIN0_CBR_HSD_MODE (3<<28)
+#define m_WIN0_CBR_VSU_MODE ((u32)1<<30)
+#define m_WIN0_CBR_VSD_MODE ((u32)1<<31)
+
+#define WIN0_COLOR_KEY (0x0038)
+#define v_WIN0_COLOR_KEY(x) (((x)&0x3fffffff)<<0)
+#define v_WIN0_COLOR_KEY_EN(x) (((x)&1)<<31)
+#define m_WIN0_COLOR_KEY (0x3fffffff<<0)
+#define m_WIN0_COLOR_KEY_EN ((u32)1<<31)
+
+#define WIN0_VIR (0x003c)
+#define v_WIN0_VIR_STRIDE(x) (((x)&0x3fff)<<0)
+#define v_WIN0_VIR_STRIDE_UV(x) (((x)&0x3fff)<<16)
+#define m_WIN0_VIR_STRIDE (0x3fff<<0)
+#define m_WIN0_VIR_STRIDE_UV (0x3fff<<16)
+
+#define WIN0_YRGB_MST (0x0040)
+#define WIN0_CBR_MST (0x0044)
+#define WIN0_ACT_INFO (0x0048)
+#define v_WIN0_ACT_WIDTH(x) (((x-1)&0x1fff)<<0)
+#define v_WIN0_ACT_HEIGHT(x) (((x-1)&0x1fff)<<16)
+#define m_WIN0_ACT_WIDTH (0x1fff<<0)
+#define m_WIN0_ACT_HEIGHT (0x1fff<<16)
+
+#define WIN0_DSP_INFO (0x004c)
+#define v_WIN0_DSP_WIDTH(x) (((x-1)&0xfff)<<0)
+#define v_WIN0_DSP_HEIGHT(x) (((x-1)&0xfff)<<16)
+#define m_WIN0_DSP_WIDTH (0xfff<<0)
+#define m_WIN0_DSP_HEIGHT (0xfff<<16)
+
+#define WIN0_DSP_ST (0x0050)
+#define v_WIN0_DSP_XST(x) (((x)&0x1fff)<<0)
+#define v_WIN0_DSP_YST(x) (((x)&0x1fff)<<16)
+#define m_WIN0_DSP_XST (0x1fff<<0)
+#define m_WIN0_DSP_YST (0x1fff<<16)
+
+#define WIN0_SCL_FACTOR_YRGB (0x0054)
+#define v_WIN0_HS_FACTOR_YRGB(x) (((x)&0xffff)<<0)
+#define v_WIN0_VS_FACTOR_YRGB(x) (((x)&0xffff)<<16)
+#define m_WIN0_HS_FACTOR_YRGB (0xffff<<0)
+#define m_WIN0_VS_FACTOR_YRGB ((u32)0xffff<<16)
+
+#define WIN0_SCL_FACTOR_CBR (0x0058)
+#define v_WIN0_HS_FACTOR_CBR(x) (((x)&0xffff)<<0)
+#define v_WIN0_VS_FACTOR_CBR(x) (((x)&0xffff)<<16)
+#define m_WIN0_HS_FACTOR_CBR (0xffff<<0)
+#define m_WIN0_VS_FACTOR_CBR ((u32)0xffff<<16)
+
+#define WIN0_SCL_OFFSET (0x005c)
+#define v_WIN0_HS_OFFSET_YRGB(x) (((x)&0xff)<<0)
+#define v_WIN0_HS_OFFSET_CBR(x) (((x)&0xff)<<8)
+#define v_WIN0_VS_OFFSET_YRGB(x) (((x)&0xff)<<16)
+#define v_WIN0_VS_OFFSET_CBR(x) (((x)&0xff)<<24)
+
+#define m_WIN0_HS_OFFSET_YRGB (0xff<<0)
+#define m_WIN0_HS_OFFSET_CBR (0xff<<8)
+#define m_WIN0_VS_OFFSET_YRGB (0xff<<16)
+#define m_WIN0_VS_OFFSET_CBR ((u32)0xff<<24)
+
+#define WIN0_SRC_ALPHA_CTRL (0x0060)
+#define v_WIN0_SRC_ALPHA_EN(x) (((x)&1)<<0)
+#define v_WIN0_SRC_COLOR_M0(x) (((x)&1)<<1)
+#define v_WIN0_SRC_ALPHA_M0(x) (((x)&1)<<2)
+#define v_WIN0_SRC_BLEND_M0(x) (((x)&3)<<3)
+#define v_WIN0_SRC_ALPHA_CAL_M0(x) (((x)&1)<<5)
+#define v_WIN0_SRC_FACTOR_M0(x) (((x)&7)<<6)
+#define v_WIN0_SRC_GLOBAL_ALPHA(x) (((x)&0xff)<<16)
+#define v_WIN0_FADING_VALUE(x) (((x)&0xff)<<24)
+
+#define m_WIN0_SRC_ALPHA_EN (1<<0)
+#define m_WIN0_SRC_COLOR_M0 (1<<1)
+#define m_WIN0_SRC_ALPHA_M0 (1<<2)
+#define m_WIN0_SRC_BLEND_M0 (3<<3)
+#define m_WIN0_SRC_ALPHA_CAL_M0 (1<<5)
+#define m_WIN0_SRC_FACTOR_M0 (7<<6)
+#define m_WIN0_SRC_GLOBAL_ALPHA (0xff<<16)
+#define m_WIN0_FADING_VALUE (0xff<<24)
+
+#define WIN0_DST_ALPHA_CTRL (0x0064)
+#define v_WIN0_DST_FACTOR_M0(x) (((x)&7)<<6)
+#define m_WIN0_DST_FACTOR_M0 (7<<6)
+
+#define WIN0_FADING_CTRL (0x0068)
+#define v_WIN0_FADING_OFFSET_R(x) (((x)&0xff)<<0)
+#define v_WIN0_FADING_OFFSET_G(x) (((x)&0xff)<<8)
+#define v_WIN0_FADING_OFFSET_B(x) (((x)&0xff)<<16)
+#define v_WIN0_FADING_EN(x) (((x)&1)<<24)
+
+#define m_WIN0_FADING_OFFSET_R (0xff<<0)
+#define m_WIN0_FADING_OFFSET_G (0xff<<8)
+#define m_WIN0_FADING_OFFSET_B (0xff<<16)
+#define m_WIN0_FADING_EN (1<<24)
+
+/*win1 register*/
+#define WIN1_CTRL0 (0x0070)
+#define v_WIN1_EN(x) (((x)&1)<<0)
+#define v_WIN1_DATA_FMT(x) (((x)&7)<<1)
+#define v_WIN1_FMT_10(x) (((x)&1)<<4)
+#define v_WIN1_LB_MODE(x) (((x)&7)<<5)
+#define v_WIN1_INTERLACE_READ_MODE(x) (((x)&1)<<8)
+#define v_WIN1_NO_OUTSTANDING(x) (((x)&1)<<9)
+#define v_WIN1_CSC_MODE(x) (((x)&3)<<10)
+#define v_WIN1_RB_SWAP(x) (((x)&1)<<12)
+#define v_WIN1_ALPHA_SWAP(x) (((x)&1)<<13)
+#define v_WIN1_MID_SWAP(x) (((x)&1)<<14)
+#define v_WIN1_UV_SWAP(x) (((x)&1)<<15)
+#define v_WIN1_PPAS_ZERO_EN(x) (((x)&1)<<16)
+#define v_WIN1_YRGB_DEFLICK(x) (((x)&1)<<18)
+#define v_WIN1_CBR_DEFLICK(x) (((x)&1)<<19)
+#define v_WIN1_YUV_CLIP(x) (((x)&1)<<20)
+
+#define m_WIN1_EN (1<<0)
+#define m_WIN1_DATA_FMT (7<<1)
+#define m_WIN1_FMT_10 (1<<4)
+#define m_WIN1_LB_MODE (3<<5)
+#define m_WIN1_INTERLACE_READ_MODE (1<<8)
+#define m_WIN1_NO_OUTSTANDING (1<<9)
+#define m_WIN1_CSC_MODE (3<<10)
+#define m_WIN1_RB_SWAP (1<<12)
+#define m_WIN1_ALPHA_SWAP (1<<13)
+#define m_WIN1_MID_SWAP (1<<14)
+#define m_WIN1_UV_SWAP (1<<15)
+#define m_WIN1_PPAS_ZERO_EN (1<<16)
+#define m_WIN1_YRGB_DEFLICK (1<<18)
+#define m_WIN1_CBR_DEFLICK (1<<19)
+#define m_WIN1_YUV_CLIP (1<<20)
+
+#define WIN1_CTRL1 (0x0074)
+#define v_WIN1_YRGB_AXI_GATHER_EN(x) (((x)&1)<<0)
+#define v_WIN1_CBR_AXI_GATHER_EN(x) (((x)&1)<<1)
+#define v_WIN1_BIC_COE_SEL(x) (((x)&3)<<2)
+#define v_WIN1_VSD_YRGB_GT4(x) (((x)&1)<<4)
+#define v_WIN1_VSD_YRGB_GT2(x) (((x)&1)<<5)
+#define v_WIN1_VSD_CBR_GT4(x) (((x)&1)<<6)
+#define v_WIN1_VSD_CBR_GT2(x) (((x)&1)<<7)
+#define v_WIN1_YRGB_AXI_GATHER_NUM(x) (((x)&0xf)<<8)
+#define v_WIN1_CBR_AXI_GATHER_NUM(x) (((x)&7)<<12)
+#define v_WIN1_LINE_LOAD_MODE(x) (((x)&1)<<15)
+#define v_WIN1_YRGB_HOR_SCL_MODE(x) (((x)&3)<<16)
+#define v_WIN1_YRGB_VER_SCL_MODE(x) (((x)&3)<<18)
+#define v_WIN1_YRGB_HSD_MODE(x) (((x)&3)<<20)
+#define v_WIN1_YRGB_VSU_MODE(x) (((x)&1)<<22)
+#define v_WIN1_YRGB_VSD_MODE(x) (((x)&1)<<23)
+#define v_WIN1_CBR_HOR_SCL_MODE(x) (((x)&3)<<24)
+#define v_WIN1_CBR_VER_SCL_MODE(x) (((x)&3)<<26)
+#define v_WIN1_CBR_HSD_MODE(x) (((x)&3)<<28)
+#define v_WIN1_CBR_VSU_MODE(x) (((x)&1)<<30)
+#define v_WIN1_CBR_VSD_MODE(x) (((x)&1)<<31)
+
+#define m_WIN1_YRGB_AXI_GATHER_EN (1<<0)
+#define m_WIN1_CBR_AXI_GATHER_EN (1<<1)
+#define m_WIN1_BIC_COE_SEL (3<<2)
+#define m_WIN1_VSD_YRGB_GT4 (1<<4)
+#define m_WIN1_VSD_YRGB_GT2 (1<<5)
+#define m_WIN1_VSD_CBR_GT4 (1<<6)
+#define m_WIN1_VSD_CBR_GT2 (1<<7)
+#define m_WIN1_YRGB_AXI_GATHER_NUM (0xf<<8)
+#define m_WIN1_CBR_AXI_GATHER_NUM (7<<12)
+#define m_WIN1_LINE_LOAD_MODE (1<<15)
+#define m_WIN1_YRGB_HOR_SCL_MODE (3<<16)
+#define m_WIN1_YRGB_VER_SCL_MODE (3<<18)
+#define m_WIN1_YRGB_HSD_MODE (3<<20)
+#define m_WIN1_YRGB_VSU_MODE (1<<22)
+#define m_WIN1_YRGB_VSD_MODE (1<<23)
+#define m_WIN1_CBR_HOR_SCL_MODE (3<<24)
+#define m_WIN1_CBR_VER_SCL_MODE (3<<26)
+#define m_WIN1_CBR_HSD_MODE (3<<28)
+#define m_WIN1_CBR_VSU_MODE (1<<30)
+#define m_WIN1_CBR_VSD_MODE ((u32)1<<31)
+
+#define WIN1_COLOR_KEY (0x0078)
+#define v_WIN1_COLOR_KEY(x) (((x)&0x3fffffff)<<0)
+#define v_WIN1_COLOR_KEY_EN(x) (((x)&1)<<31)
+#define m_WIN1_COLOR_KEY (0x3fffffff<<0)
+#define m_WIN1_COLOR_KEY_EN ((u32)1<<31)
+
+#define WIN1_VIR (0x007c)
+#define v_WIN1_VIR_STRIDE(x) (((x)&0x3fff)<<0)
+#define v_WIN1_VIR_STRIDE_UV(x) (((x)&0x3fff)<<16)
+#define m_WIN1_VIR_STRIDE (0x3fff<<0)
+#define m_WIN1_VIR_STRIDE_UV (0x3fff<<16)
+
+#define WIN1_YRGB_MST (0x0080)
+#define WIN1_CBR_MST (0x0084)
+#define WIN1_ACT_INFO (0x0088)
+#define v_WIN1_ACT_WIDTH(x) (((x-1)&0x1fff)<<0)
+#define v_WIN1_ACT_HEIGHT(x) (((x-1)&0x1fff)<<16)
+#define m_WIN1_ACT_WIDTH (0x1fff<<0)
+#define m_WIN1_ACT_HEIGHT (0x1fff<<16)
+
+#define WIN1_DSP_INFO (0x008c)
+#define v_WIN1_DSP_WIDTH(x) (((x-1)&0xfff)<<0)
+#define v_WIN1_DSP_HEIGHT(x) (((x-1)&0xfff)<<16)
+#define m_WIN1_DSP_WIDTH (0xfff<<0)
+#define m_WIN1_DSP_HEIGHT (0xfff<<16)
+
+#define WIN1_DSP_ST (0x0090)
+#define v_WIN1_DSP_XST(x) (((x)&0x1fff)<<0)
+#define v_WIN1_DSP_YST(x) (((x)&0x1fff)<<16)
+#define m_WIN1_DSP_XST (0x1fff<<0)
+#define m_WIN1_DSP_YST (0x1fff<<16)
+
+#define WIN1_SCL_FACTOR_YRGB (0x0094)
+#define v_WIN1_HS_FACTOR_YRGB(x) (((x)&0xffff)<<0)
+#define v_WIN1_VS_FACTOR_YRGB(x) (((x)&0xffff)<<16)
+#define m_WIN1_HS_FACTOR_YRGB (0xffff<<0)
+#define m_WIN1_VS_FACTOR_YRGB ((u32)0xffff<<16)
+
+#define WIN1_SCL_FACTOR_CBR (0x0098)
+#define v_WIN1_HS_FACTOR_CBR(x) (((x)&0xffff)<<0)
+#define v_WIN1_VS_FACTOR_CBR(x) (((x)&0xffff)<<16)
+#define m_WIN1_HS_FACTOR_CBR (0xffff<<0)
+#define m_WIN1_VS_FACTOR_CBR ((u32)0xffff<<16)
+
+#define WIN1_SCL_OFFSET (0x009c)
+#define v_WIN1_HS_OFFSET_YRGB(x) (((x)&0xff)<<0)
+#define v_WIN1_HS_OFFSET_CBR(x) (((x)&0xff)<<8)
+#define v_WIN1_VS_OFFSET_YRGB(x) (((x)&0xff)<<16)
+#define v_WIN1_VS_OFFSET_CBR(x) (((x)&0xff)<<24)
+
+#define m_WIN1_HS_OFFSET_YRGB (0xff<<0)
+#define m_WIN1_HS_OFFSET_CBR (0xff<<8)
+#define m_WIN1_VS_OFFSET_YRGB (0xff<<16)
+#define m_WIN1_VS_OFFSET_CBR ((u32)0xff<<24)
+
+#define WIN1_SRC_ALPHA_CTRL (0x00a0)
+#define v_WIN1_SRC_ALPHA_EN(x) (((x)&1)<<0)
+#define v_WIN1_SRC_COLOR_M0(x) (((x)&1)<<1)
+#define v_WIN1_SRC_ALPHA_M0(x) (((x)&1)<<2)
+#define v_WIN1_SRC_BLEND_M0(x) (((x)&3)<<3)
+#define v_WIN1_SRC_ALPHA_CAL_M0(x) (((x)&1)<<5)
+#define v_WIN1_SRC_FACTOR_M0(x) (((x)&7)<<6)
+#define v_WIN1_SRC_GLOBAL_ALPHA(x) (((x)&0xff)<<16)
+#define v_WIN1_FADING_VALUE(x) (((x)&0xff)<<24)
+
+#define m_WIN1_SRC_ALPHA_EN (1<<0)
+#define m_WIN1_SRC_COLOR_M0 (1<<1)
+#define m_WIN1_SRC_ALPHA_M0 (1<<2)
+#define m_WIN1_SRC_BLEND_M0 (3<<3)
+#define m_WIN1_SRC_ALPHA_CAL_M0 (1<<5)
+#define m_WIN1_SRC_FACTOR_M0 (7<<6)
+#define m_WIN1_SRC_GLOBAL_ALPHA (0xff<<16)
+#define m_WIN1_FADING_VALUE (0xff<<24)
+
+#define WIN1_DST_ALPHA_CTRL (0x00a4)
+#define v_WIN1_DST_FACTOR_M0(x) (((x)&7)<<6)
+#define m_WIN1_DST_FACTOR_M0 (7<<6)
+
+#define WIN1_FADING_CTRL (0x00a8)
+#define v_WIN1_FADING_OFFSET_R(x) (((x)&0xff)<<0)
+#define v_WIN1_FADING_OFFSET_G(x) (((x)&0xff)<<8)
+#define v_WIN1_FADING_OFFSET_B(x) (((x)&0xff)<<16)
+#define v_WIN1_FADING_EN(x) (((x)&1)<<24)
+
+#define m_WIN1_FADING_OFFSET_R (0xff<<0)
+#define m_WIN1_FADING_OFFSET_G (0xff<<8)
+#define m_WIN1_FADING_OFFSET_B (0xff<<16)
+#define m_WIN1_FADING_EN (1<<24)
+
+/*win2 register*/
+#define WIN2_CTRL0 (0x00b0)
+#define v_WIN2_EN(x) (((x)&1)<<0)
+#define v_WIN2_DATA_FMT(x) (((x)&7)<<1)
+#define v_WIN2_MST0_EN(x) (((x)&1)<<4)
+#define v_WIN2_MST1_EN(x) (((x)&1)<<5)
+#define v_WIN2_MST2_EN(x) (((x)&1)<<6)
+#define v_WIN2_MST3_EN(x) (((x)&1)<<7)
+#define v_WIN2_INTERLACE_READ(x) (((x)&1)<<8)
+#define v_WIN2_NO_OUTSTANDING(x) (((x)&1)<<9)
+#define v_WIN2_CSC_MODE(x) (((x)&1)<<10)
+#define v_WIN2_RB_SWAP(x) (((x)&1)<<12)
+#define v_WIN2_ALPHA_SWAP(x) (((x)&1)<<13)
+#define v_WIN2_ENDIAN_MODE(x) (((x)&1)<<14)
+#define v_WIN2_LUT_EN(x) (((x)&1)<<18)
+
+#define m_WIN2_EN (1<<0)
+#define m_WIN2_DATA_FMT (7<<1)
+#define m_WIN2_MST0_EN (1<<4)
+#define m_WIN2_MST1_EN (1<<5)
+#define m_WIN2_MST2_EN (1<<6)
+#define m_WIN2_MST3_EN (1<<7)
+#define m_WIN2_INTERLACE_READ (1<<8)
+#define m_WIN2_NO_OUTSTANDING (1<<9)
+#define m_WIN2_CSC_MODE (1<<10)
+#define m_WIN2_RB_SWAP (1<<12)
+#define m_WIN2_ALPHA_SWAP (1<<13)
+#define m_WIN2_ENDIAN_MODE (1<<14)
+#define m_WIN2_LUT_EN (1<<18)
+
+#define WIN2_CTRL1 (0x00b4)
+#define v_WIN2_AXI_GATHER_EN(x) (((x)&1)<<0)
+#define v_WIN2_AXI_GATHER_NUM(x) (((x)&0xf)<<4)
+#define m_WIN2_AXI_GATHER_EN (1<<0)
+#define m_WIN2_AXI_GATHER_NUM (0xf<<4)
+
+#define WIN2_VIR0_1 (0x00b8)
+#define v_WIN2_VIR_STRIDE0(x) (((x)&0x1fff)<<0)
+#define v_WIN2_VIR_STRIDE1(x) (((x)&0x1fff)<<16)
+#define m_WIN2_VIR_STRIDE0 (0x1fff<<0)
+#define m_WIN2_VIR_STRIDE1 (0x1fff<<16)
+
+#define WIN2_VIR2_3 (0x00bc)
+#define v_WIN2_VIR_STRIDE2(x) (((x)&0x1fff)<<0)
+#define v_WIN2_VIR_STRIDE3(x) (((x)&0x1fff)<<16)
+#define m_WIN2_VIR_STRIDE2 (0x1fff<<0)
+#define m_WIN2_VIR_STRIDE3 (0x1fff<<16)
+
+#define WIN2_MST0 (0x00c0)
+#define WIN2_DSP_INFO0 (0x00c4)
+#define v_WIN2_DSP_WIDTH0(x) (((x-1)&0xfff)<<0)
+#define v_WIN2_DSP_HEIGHT0(x) (((x-1)&0xfff)<<16)
+#define m_WIN2_DSP_WIDTH0 (0xfff<<0)
+#define m_WIN2_DSP_HEIGHT0 (0xfff<<16)
+
+#define WIN2_DSP_ST0 (0x00c8)
+#define v_WIN2_DSP_XST0(x) (((x)&0x1fff)<<0)
+#define v_WIN2_DSP_YST0(x) (((x)&0x1fff)<<16)
+#define m_WIN2_DSP_XST0 (0x1fff<<0)
+#define m_WIN2_DSP_YST0 (0x1fff<<16)
+
+#define WIN2_COLOR_KEY (0x00cc)
+#define v_WIN2_COLOR_KEY(x) (((x)&0xffffff)<<0)
+#define v_WIN2_KEY_EN(x) (((x)&1)<<24)
+#define m_WIN2_COLOR_KEY (0xffffff<<0)
+#define m_WIN2_KEY_EN ((u32)1<<24)
+
+
+#define WIN2_MST1 (0x00d0)
+#define WIN2_DSP_INFO1 (0x00d4)
+#define v_WIN2_DSP_WIDTH1(x) (((x-1)&0xfff)<<0)
+#define v_WIN2_DSP_HEIGHT1(x) (((x-1)&0xfff)<<16)
+
+#define m_WIN2_DSP_WIDTH1 (0xfff<<0)
+#define m_WIN2_DSP_HEIGHT1 (0xfff<<16)
+
+#define WIN2_DSP_ST1 (0x00d8)
+#define v_WIN2_DSP_XST1(x) (((x)&0x1fff)<<0)
+#define v_WIN2_DSP_YST1(x) (((x)&0x1fff)<<16)
+
+#define m_WIN2_DSP_XST1 (0x1fff<<0)
+#define m_WIN2_DSP_YST1 (0x1fff<<16)
+
+#define WIN2_SRC_ALPHA_CTRL (0x00dc)
+#define v_WIN2_SRC_ALPHA_EN(x) (((x)&1)<<0)
+#define v_WIN2_SRC_COLOR_M0(x) (((x)&1)<<1)
+#define v_WIN2_SRC_ALPHA_M0(x) (((x)&1)<<2)
+#define v_WIN2_SRC_BLEND_M0(x) (((x)&3)<<3)
+#define v_WIN2_SRC_ALPHA_CAL_M0(x) (((x)&1)<<5)
+#define v_WIN2_SRC_FACTOR_M0(x) (((x)&7)<<5)
+#define v_WIN2_SRC_GLOBAL_ALPHA(x) (((x)&0xff)<<16)
+#define v_WIN2_FADING_VALUE(x) (((x)&0xff)<<24)
+
+
+#define m_WIN2_SRC_ALPHA_EN (1<<0)
+#define m_WIN2_SRC_COLOR_M0 (1<<1)
+#define m_WIN2_SRC_ALPHA_M0 (1<<2)
+#define m_WIN2_SRC_BLEND_M0 (3<<3)
+#define m_WIN2_SRC_ALPHA_CAL_M0 (1<<5)
+#define m_WIN2_SRC_FACTOR_M0 (7<<6)
+#define m_WIN2_SRC_GLOBAL_ALPHA (0xff<<16)
+#define m_WIN2_FADING_VALUE (0xff<<24)
+
+#define WIN2_MST2 (0x00e0)
+#define WIN2_DSP_INFO2 (0x00e4)
+#define v_WIN2_DSP_WIDTH2(x) (((x-1)&0xfff)<<0)
+#define v_WIN2_DSP_HEIGHT2(x) (((x-1)&0xfff)<<16)
+
+#define m_WIN2_DSP_WIDTH2 (0xfff<<0)
+#define m_WIN2_DSP_HEIGHT2 (0xfff<<16)
+
+
+#define WIN2_DSP_ST2 (0x00e8)
+#define v_WIN2_DSP_XST2(x) (((x)&0x1fff)<<0)
+#define v_WIN2_DSP_YST2(x) (((x)&0x1fff)<<16)
+#define m_WIN2_DSP_XST2 (0x1fff<<0)
+#define m_WIN2_DSP_YST2 (0x1fff<<16)
+
+#define WIN2_DST_ALPHA_CTRL (0x00ec)
+#define v_WIN2_DST_FACTOR_M0(x) (((x)&7)<<6)
+#define m_WIN2_DST_FACTOR_M0 (7<<6)
+
+#define WIN2_MST3 (0x00f0)
+#define WIN2_DSP_INFO3 (0x00f4)
+#define v_WIN2_DSP_WIDTH3(x) (((x-1)&0xfff)<<0)
+#define v_WIN2_DSP_HEIGHT3(x) (((x-1)&0xfff)<<16)
+#define m_WIN2_DSP_WIDTH3 (0xfff<<0)
+#define m_WIN2_DSP_HEIGHT3 (0xfff<<16)
+
+#define WIN2_DSP_ST3 (0x00f8)
+#define v_WIN2_DSP_XST3(x) (((x)&0x1fff)<<0)
+#define v_WIN2_DSP_YST3(x) (((x)&0x1fff)<<16)
+#define m_WIN2_DSP_XST3 (0x1fff<<0)
+#define m_WIN2_DSP_YST3 (0x1fff<<16)
+
+#define WIN2_FADING_CTRL (0x00fc)
+#define v_WIN2_FADING_OFFSET_R(x) (((x)&0xff)<<0)
+#define v_WIN2_FADING_OFFSET_G(x) (((x)&0xff)<<8)
+#define v_WIN2_FADING_OFFSET_B(x) (((x)&0xff)<<16)
+#define v_WIN2_FADING_EN(x) (((x)&1)<<24)
+
+#define m_WIN2_FADING_OFFSET_R (0xff<<0)
+#define m_WIN2_FADING_OFFSET_G (0xff<<8)
+#define m_WIN2_FADING_OFFSET_B (0xff<<16)
+#define m_WIN2_FADING_EN (1<<24)
+
+/*win3 register*/
+#define WIN3_CTRL0 (0x0100)
+#define v_WIN3_EN(x) (((x)&1)<<0)
+#define v_WIN3_DATA_FMT(x) (((x)&7)<<1)
+#define v_WIN3_MST0_EN(x) (((x)&1)<<4)
+#define v_WIN3_MST1_EN(x) (((x)&1)<<5)
+#define v_WIN3_MST2_EN(x) (((x)&1)<<6)
+#define v_WIN3_MST3_EN(x) (((x)&1)<<7)
+#define v_WIN3_INTERLACE_READ(x) (((x)&1)<<8)
+#define v_WIN3_NO_OUTSTANDING(x) (((x)&1)<<9)
+#define v_WIN3_CSC_MODE(x) (((x)&1)<<10)
+#define v_WIN3_RB_SWAP(x) (((x)&1)<<12)
+#define v_WIN3_ALPHA_SWAP(x) (((x)&1)<<13)
+#define v_WIN3_ENDIAN_MODE(x) (((x)&1)<<14)
+#define v_WIN3_LUT_EN(x) (((x)&1)<<18)
+
+#define m_WIN3_EN (1<<0)
+#define m_WIN3_DATA_FMT (7<<1)
+#define m_WIN3_MST0_EN (1<<4)
+#define m_WIN3_MST1_EN (1<<5)
+#define m_WIN3_MST2_EN (1<<6)
+#define m_WIN3_MST3_EN (1<<7)
+#define m_WIN3_INTERLACE_READ (1<<8)
+#define m_WIN3_NO_OUTSTANDING (1<<9)
+#define m_WIN3_CSC_MODE (1<<10)
+#define m_WIN3_RB_SWAP (1<<12)
+#define m_WIN3_ALPHA_SWAP (1<<13)
+#define m_WIN3_ENDIAN_MODE (1<<14)
+#define m_WIN3_LUT_EN (1<<18)
+
+
+#define WIN3_CTRL1 (0x0104)
+#define v_WIN3_AXI_GATHER_EN(x) (((x)&1)<<0)
+#define v_WIN3_AXI_GATHER_NUM(x) (((x)&0xf)<<4)
+#define m_WIN3_AXI_GATHER_EN (1<<0)
+#define m_WIN3_AXI_GATHER_NUM (0xf<<4)
+
+#define WIN3_VIR0_1 (0x0108)
+#define v_WIN3_VIR_STRIDE0(x) (((x)&0x1fff)<<0)
+#define v_WIN3_VIR_STRIDE1(x) (((x)&0x1fff)<<16)
+#define m_WIN3_VIR_STRIDE0 (0x1fff<<0)
+#define m_WIN3_VIR_STRIDE1 (0x1fff<<16)
+
+#define WIN3_VIR2_3 (0x010c)
+#define v_WIN3_VIR_STRIDE2(x) (((x)&0x1fff)<<0)
+#define v_WIN3_VIR_STRIDE3(x) (((x)&0x1fff)<<16)
+#define m_WIN3_VIR_STRIDE2 (0x1fff<<0)
+#define m_WIN3_VIR_STRIDE3 (0x1fff<<16)
+
+#define WIN3_MST0 (0x0110)
+#define WIN3_DSP_INFO0 (0x0114)
+#define v_WIN3_DSP_WIDTH0(x) (((x-1)&0xfff)<<0)
+#define v_WIN3_DSP_HEIGHT0(x) (((x-1)&0xfff)<<16)
+#define m_WIN3_DSP_WIDTH0 (0xfff<<0)
+#define m_WIN3_DSP_HEIGHT0 (0xfff<<16)
+
+#define WIN3_DSP_ST0 (0x0118)
+#define v_WIN3_DSP_XST0(x) (((x)&0x1fff)<<0)
+#define v_WIN3_DSP_YST0(x) (((x)&0x1fff)<<16)
+#define m_WIN3_DSP_XST0 (0x1fff<<0)
+#define m_WIN3_DSP_YST0 (0x1fff<<16)
+
+#define WIN3_COLOR_KEY (0x011c)
+#define v_WIN3_COLOR_KEY(x) (((x)&0xffffff)<<0)
+#define v_WIN3_KEY_EN(x) (((x)&1)<<24)
+#define m_WIN3_COLOR_KEY (0xffffff<<0)
+#define m_WIN3_KEY_EN ((u32)1<<24)
+
+#define WIN3_MST1 (0x0120)
+#define WIN3_DSP_INFO1 (0x0124)
+#define v_WIN3_DSP_WIDTH1(x) (((x-1)&0xfff)<<0)
+#define v_WIN3_DSP_HEIGHT1(x) (((x-1)&0xfff)<<16)
+#define m_WIN3_DSP_WIDTH1 (0xfff<<0)
+#define m_WIN3_DSP_HEIGHT1 (0xfff<<16)
+
+#define WIN3_DSP_ST1 (0x0128)
+#define v_WIN3_DSP_XST1(x) (((x)&0x1fff)<<0)
+#define v_WIN3_DSP_YST1(x) (((x)&0x1fff)<<16)
+#define m_WIN3_DSP_XST1 (0x1fff<<0)
+#define m_WIN3_DSP_YST1 (0x1fff<<16)
+
+#define WIN3_SRC_ALPHA_CTRL (0x012c)
+#define v_WIN3_SRC_ALPHA_EN(x) (((x)&1)<<0)
+#define v_WIN3_SRC_COLOR_M0(x) (((x)&1)<<1)
+#define v_WIN3_SRC_ALPHA_M0(x) (((x)&1)<<2)
+#define v_WIN3_SRC_BLEND_M0(x) (((x)&3)<<3)
+#define v_WIN3_SRC_ALPHA_CAL_M0(x) (((x)&1)<<5)
+#define v_WIN3_SRC_FACTOR_M0(x) (((x)&7)<<6)
+#define v_WIN3_SRC_GLOBAL_ALPHA(x) (((x)&0xff)<<16)
+#define v_WIN3_FADING_VALUE(x) (((x)&0xff)<<24)
+
+#define m_WIN3_SRC_ALPHA_EN (1<<0)
+#define m_WIN3_SRC_COLOR_M0 (1<<1)
+#define m_WIN3_SRC_ALPHA_M0 (1<<2)
+#define m_WIN3_SRC_BLEND_M0 (3<<3)
+#define m_WIN3_SRC_ALPHA_CAL_M0 (1<<5)
+#define m_WIN3_SRC_FACTOR_M0 (7<<6)
+#define m_WIN3_SRC_GLOBAL_ALPHA (0xff<<16)
+#define m_WIN3_FADING_VALUE (0xff<<24)
+
+#define WIN3_MST2 (0x0130)
+#define WIN3_DSP_INFO2 (0x0134)
+#define v_WIN3_DSP_WIDTH2(x) (((x-1)&0xfff)<<0)
+#define v_WIN3_DSP_HEIGHT2(x) (((x-1)&0xfff)<<16)
+#define m_WIN3_DSP_WIDTH2 (0xfff<<0)
+#define m_WIN3_DSP_HEIGHT2 (0xfff<<16)
+
+#define WIN3_DSP_ST2 (0x0138)
+#define v_WIN3_DSP_XST2(x) (((x)&0x1fff)<<0)
+#define v_WIN3_DSP_YST2(x) (((x)&0x1fff)<<16)
+#define m_WIN3_DSP_XST2 (0x1fff<<0)
+#define m_WIN3_DSP_YST2 (0x1fff<<16)
+
+#define WIN3_DST_ALPHA_CTRL (0x013c)
+#define v_WIN3_DST_FACTOR_M0(x) (((x)&7)<<6)
+#define m_WIN3_DST_FACTOR_M0 (7<<6)
+
+
+#define WIN3_MST3 (0x0140)
+#define WIN3_DSP_INFO3 (0x0144)
+#define v_WIN3_DSP_WIDTH3(x) (((x-1)&0xfff)<<0)
+#define v_WIN3_DSP_HEIGHT3(x) (((x-1)&0xfff)<<16)
+#define m_WIN3_DSP_WIDTH3 (0xfff<<0)
+#define m_WIN3_DSP_HEIGHT3 (0xfff<<16)
+
+#define WIN3_DSP_ST3 (0x0148)
+#define v_WIN3_DSP_XST3(x) (((x)&0x1fff)<<0)
+#define v_WIN3_DSP_YST3(x) (((x)&0x1fff)<<16)
+#define m_WIN3_DSP_XST3 (0x1fff<<0)
+#define m_WIN3_DSP_YST3 (0x1fff<<16)
+
+#define WIN3_FADING_CTRL (0x014c)
+#define v_WIN3_FADING_OFFSET_R(x) (((x)&0xff)<<0)
+#define v_WIN3_FADING_OFFSET_G(x) (((x)&0xff)<<8)
+#define v_WIN3_FADING_OFFSET_B(x) (((x)&0xff)<<16)
+#define v_WIN3_FADING_EN(x) (((x)&1)<<24)
+
+#define m_WIN3_FADING_OFFSET_R (0xff<<0)
+#define m_WIN3_FADING_OFFSET_G (0xff<<8)
+#define m_WIN3_FADING_OFFSET_B (0xff<<16)
+#define m_WIN3_FADING_EN (1<<24)
+
+
+/*hwc register*/
+#define HWC_CTRL0 (0x0150)
+#define v_HWC_EN(x) (((x)&1)<<0)
+#define v_HWC_DATA_FMT(x) (((x)&7)<<1)
+#define v_HWC_MODE(x) (((x)&1)<<4)
+#define v_HWC_SIZE(x) (((x)&3)<<5)
+#define v_HWC_INTERLACE_READ(x) (((x)&1)<<8)
+#define v_HWC_NO_OUTSTANDING(x) (((x)&1)<<9)
+#define v_HWC_CSC_MODE(x) (((x)&1)<<10)
+#define v_HWC_RB_SWAP(x) (((x)&1)<<12)
+#define v_HWC_ALPHA_SWAP(x) (((x)&1)<<13)
+#define v_HWC_ENDIAN_MODE(x) (((x)&1)<<14)
+#define v_HWC_LUT_EN(x) (((x)&1)<<18)
+
+#define m_HWC_EN (1<<0)
+#define m_HWC_DATA_FMT (7<<1)
+#define m_HWC_MODE (1<<4)
+#define m_HWC_SIZE (3<<5)
+#define m_HWC_INTERLACE_READ (1<<8)
+#define m_HWC_NO_OUTSTANDING (1<<9)
+#define m_HWC_CSC_MODE (1<<10)
+#define m_HWC_RB_SWAP (1<<12)
+#define m_HWC_ALPHA_SWAP (1<<13)
+#define m_HWC_ENDIAN_MODE (1<<14)
+#define m_HWC_LUT_EN (1<<18)
+
+
+#define HWC_CTRL1 (0x0154)
+#define v_HWC_AXI_GATHER_EN(x) (((x)&1)<<0)
+#define v_HWC_AXI_GATHER_NUM(x) (((x)&7)<<4)
+#define m_HWC_AXI_GATHER_EN (1<<0)
+#define m_HWC_AXI_GATHER_NUM (7<<4)
+
+#define HWC_MST (0x0158)
+#define HWC_DSP_ST (0x015c)
+#define v_HWC_DSP_XST3(x) (((x)&0x1fff)<<0)
+#define v_HWC_DSP_YST3(x) (((x)&0x1fff)<<16)
+#define m_HWC_DSP_XST3 (0x1fff<<0)
+#define m_HWC_DSP_YST3 (0x1fff<<16)
+
+#define HWC_SRC_ALPHA_CTRL (0x0160)
+#define v_HWC_SRC_ALPHA_EN(x) (((x)&1)<<0)
+#define v_HWC_SRC_COLOR_M0(x) (((x)&1)<<1)
+#define v_HWC_SRC_ALPHA_M0(x) (((x)&1)<<2)
+#define v_HWC_SRC_BLEND_M0(x) (((x)&3)<<3)
+#define v_HWC_SRC_ALPHA_CAL_M0(x) (((x)&1)<<5)
+#define v_HWC_SRC_FACTOR_M0(x) (((x)&7)<<6)
+#define v_HWC_SRC_GLOBAL_ALPHA(x) (((x)&0xff)<<16)
+#define v_HWC_FADING_VALUE(x) (((x)&0xff)<<24)
+
+#define m_HWC_SRC_ALPHA_EN (1<<0)
+#define m_HWC_SRC_COLOR_M0 (1<<1)
+#define m_HWC_SRC_ALPHA_M0 (1<<2)
+#define m_HWC_SRC_BLEND_M0 (3<<3)
+#define m_HWC_SRC_ALPHA_CAL_M0 (1<<5)
+#define m_HWC_SRC_FACTOR_M0 (7<<6)
+#define m_HWC_SRC_GLOBAL_ALPHA (0xff<<16)
+#define m_HWC_FADING_VALUE (0xff<<24)
+
+#define HWC_DST_ALPHA_CTRL (0x0164)
+#define v_HWC_DST_FACTOR_M0(x) (((x)&7)<<6)
+#define m_HWC_DST_FACTOR_M0 (7<<6)
+
+
+#define HWC_FADING_CTRL (0x0168)
+#define v_HWC_FADING_OFFSET_R(x) (((x)&0xff)<<0)
+#define v_HWC_FADING_OFFSET_G(x) (((x)&0xff)<<8)
+#define v_HWC_FADING_OFFSET_B(x) (((x)&0xff)<<16)
+#define v_HWC_FADING_EN(x) (((x)&1)<<24)
+
+#define m_HWC_FADING_OFFSET_R (0xff<<0)
+#define m_HWC_FADING_OFFSET_G (0xff<<8)
+#define m_HWC_FADING_OFFSET_B (0xff<<16)
+#define m_HWC_FADING_EN (1<<24)
+
+/*post process register*/
+#define POST_DSP_HACT_INFO (0x0170)
+#define v_DSP_HACT_END_POST(x) (((x)&0x1fff)<<0)
+#define v_DSP_HACT_ST_POST(x) (((x)&0x1fff)<<16)
+#define m_DSP_HACT_END_POST (0x1fff<<0)
+#define m_DSP_HACT_ST_POST (0x1fff<<16)
+
+#define POST_DSP_VACT_INFO (0x0174)
+#define v_DSP_VACT_END_POST(x) (((x)&0x1fff)<<0)
+#define v_DSP_VACT_ST_POST(x) (((x)&0x1fff)<<16)
+#define m_DSP_VACT_END_POST (0x1fff<<0)
+#define m_DSP_VACT_ST_POST (0x1fff<<16)
+
+#define POST_SCL_FACTOR_YRGB (0x0178)
+#define v_POST_HS_FACTOR_YRGB(x) (((x)&0xffff)<<0)
+#define v_POST_VS_FACTOR_YRGB(x) (((x)&0xffff)<<16)
+#define m_POST_HS_FACTOR_YRGB (0xffff<<0)
+#define m_POST_VS_FACTOR_YRGB (0xffff<<16)
+
+#define POST_SCL_CTRL (0x0180)
+#define v_POST_HOR_SD_EN(x) (((x)&1)<<0)
+#define v_POST_VER_SD_EN(x) (((x)&1)<<1)
+
+#define m_POST_HOR_SD_EN (0x1<<0)
+#define m_POST_VER_SD_EN (0x1<<1)
+
+#define POST_DSP_VACT_INFO_F1 (0x0184)
+#define v_DSP_VACT_END_POST_F1(x) (((x)&0x1fff)<<0)
+#define v_DSP_VACT_ST_POST_F1(x) (((x)&0x1fff)<<16)
+
+#define m_DSP_VACT_END_POST_F1 (0x1fff<<0)
+#define m_DSP_VACT_ST_POST_F1 (0x1fff<<16)
+
+#define DSP_HTOTAL_HS_END (0x0188)
+#define v_DSP_HS_PW(x) (((x)&0x1fff)<<0)
+#define v_DSP_HTOTAL(x) (((x)&0x1fff)<<16)
+#define m_DSP_HS_PW (0x1fff<<0)
+#define m_DSP_HTOTAL (0x1fff<<16)
+
+#define DSP_HACT_ST_END (0x018c)
+#define v_DSP_HACT_END(x) (((x)&0x1fff)<<0)
+#define v_DSP_HACT_ST(x) (((x)&0x1fff)<<16)
+#define m_DSP_HACT_END (0x1fff<<0)
+#define m_DSP_HACT_ST (0x1fff<<16)
+
+#define DSP_VTOTAL_VS_END (0x0190)
+#define v_DSP_VS_PW(x) (((x)&0x1fff)<<0)
+#define v_DSP_VTOTAL(x) (((x)&0x1fff)<<16)
+#define m_DSP_VS_PW (0x1fff<<0)
+#define m_DSP_VTOTAL (0x1fff<<16)
+
+#define DSP_VACT_ST_END (0x0194)
+#define v_DSP_VACT_END(x) (((x)&0x1fff)<<0)
+#define v_DSP_VACT_ST(x) (((x)&0x1fff)<<16)
+#define m_DSP_VACT_END (0x1fff<<0)
+#define m_DSP_VACT_ST (0x1fff<<16)
+
+#define DSP_VS_ST_END_F1 (0x0198)
+#define v_DSP_VS_END_F1(x) (((x)&0x1fff)<<0)
+#define v_DSP_VS_ST_F1(x) (((x)&0x1fff)<<16)
+#define m_DSP_VS_END_F1 (0x1fff<<0)
+#define m_DSP_VS_ST_F1 (0x1fff<<16)
+
+#define DSP_VACT_ST_END_F1 (0x019c)
+#define v_DSP_VACT_END_F1(x) (((x)&0x1fff)<<0)
+#define v_DSP_VAC_ST_F1(x) (((x)&0x1fff)<<16)
+#define m_DSP_VACT_END_F1 (0x1fff<<0)
+#define m_DSP_VAC_ST_F1 (0x1fff<<16)
+
+
+/*pwm register*/
+#define PWM_CTRL (0x01a0)
+#define v_PWM_EN(x) (((x)&1)<<0)
+#define v_PWM_MODE(x) (((x)&3)<<1)
+
+#define v_DUTY_POL(x) (((x)&1)<<3)
+#define v_INACTIVE_POL(x) (((x)&1)<<4)
+#define v_OUTPUT_MODE(x) (((x)&1)<<5)
+#define v_BL_EN(x) (((x)&1)<<8)
+#define v_CLK_SEL(x) (((x)&1)<<9)
+#define v_PRESCALE(x) (((x)&7)<<12)
+#define v_SCALE(x) (((x)&0xff)<<16)
+#define v_RPT(x) (((x)&0xff)<<24)
+
+#define m_PWM_EN (1<<0)
+#define m_PWM_MODE (3<<1)
+
+#define m_DUTY_POL (1<<3)
+#define m_INACTIVE_POL (1<<4)
+#define m_OUTPUT_MODE (1<<5)
+#define m_BL_EN (1<<8)
+#define m_CLK_SEL (1<<9)
+#define m_PRESCALE (7<<12)
+#define m_SCALE (0xff<<16)
+#define m_RPT ((u32)0xff<<24)
+
+#define PWM_PERIOD_HPR (0x01a4)
+#define PWM_DUTY_LPR (0x01a8)
+#define PWM_CNT (0x01ac)
+
+/*BCSH register*/
+#define BCSH_COLOR_BAR (0x01b0)
+#define v_BCSH_EN(x) (((x)&1)<<0)
+#define v_BCSH_COLOR_BAR_Y(x) (((x)&0x3ff)<<2)
+#define v_BCSH_COLOR_BAR_U(x) (((x)&0x3ff)<<12)
+#define v_BCSH_COLOR_BAR_V(x) (((x)&0x3ff)<<22)
+
+#define m_BCSH_EN (1<<0)
+#define m_BCSH_COLOR_BAR_Y (0x3ff<<2)
+#define m_BCSH_COLOR_BAR_U (0x3ff<<12)
+#define m_BCSH_COLOR_BAR_V ((u32)0x3ff<<22)
+
+#define BCSH_BCS (0x01b4)
+#define v_BCSH_BRIGHTNESS(x) (((x)&0xff)<<0)
+#define v_BCSH_CONTRAST(x) (((x)&0x1ff)<<8)
+#define v_BCSH_SAT_CON(x) (((x)&0x3ff)<<20)
+#define v_BCSH_OUT_MODE(x) (((x)&0x3)<<30)
+
+#define m_BCSH_BRIGHTNESS (0xff<<0)
+#define m_BCSH_CONTRAST (0x1ff<<8)
+#define m_BCSH_SAT_CON (0x3ff<<20)
+#define m_BCSH_OUT_MODE ((u32)0x3<<30)
+
+
+#define BCSH_H (0x01b8)
+#define v_BCSH_SIN_HUE(x) (((x)&0x1ff)<<0)
+#define v_BCSH_COS_HUE(x) (((x)&0x1ff)<<16)
+
+#define m_BCSH_SIN_HUE (0x1ff<<0)
+#define m_BCSH_COS_HUE (0x1ff<<16)
+
+#define CABC_CTRL0 (0x01c0)
+#define v_CABC_EN(x) (((x)&1)<<0)
+#define v_CABC_CALC_PIXEL_NUM(x) (((x)&0x7fffff)<<1)
+#define v_CABC_STAGE_UP(x) (((x)&0xff)<<24)
+#define m_CABC_EN (1<<0)
+#define m_CABC_CALC_PIXEL_NUM (0x7fffff<<1)
+#define m_CABC_STAGE_UP (0xff<<24)
+
+
+#define CABC_CTRL1 (0x01c4)
+#define v_CABC_TOTAL_NUM(x) (((x)&0x7fffff)<<1)
+#define v_CABC_STAGE_DOWN(x) (((x)&0xff)<<24)
+#define m_CABC_TOTAL_NUM (0x7fffff<<1)
+#define m_CABC_STAGE_DOWN (0xff<<24)
+
+#define CABC_GAUSS_LINE0_0 (0x01c8)
+#define v_CABC_T_LINE0_0(x) (((x)&0xff)<<0)
+#define v_CABC_T_LINE0_1(x) (((x)&0xff)<<8)
+#define v_CABC_T_LINE0_2(x) (((x)&0xff)<<16)
+#define v_CABC_T_LINE0_3(x) (((x)&0xff)<<24)
+#define m_CABC_T_LINE0_0 (0xff<<0)
+#define m_CABC_T_LINE0_1 (0xff<<8)
+#define m_CABC_T_LINE0_2 (0xff<<16)
+#define m_CABC_T_LINE0_3 ((u32)0xff<<24)
+
+#define CABC_GAUSS_LINE0_1 (0x01cc)
+#define v_CABC_T_LINE0_4(x) (((x)&0xff)<<0)
+#define v_CABC_T_LINE0_5(x) (((x)&0xff)<<8)
+#define v_CABC_T_LINE0_6(x) (((x)&0xff)<<16)
+#define m_CABC_T_LINE0_4 (0xff<<0)
+#define m_CABC_T_LINE0_5 (0xff<<8)
+#define m_CABC_T_LINE0_6 (0xff<<16)
+
+
+#define CABC_GAUSS_LINE1_0 (0x01d0)
+#define v_CABC_T_LINE1_0(x) (((x)&0xff)<<0)
+#define v_CABC_T_LINE1_1(x) (((x)&0xff)<<8)
+#define v_CABC_T_LINE1_2(x) (((x)&0xff)<<16)
+#define v_CABC_T_LINE1_3(x) (((x)&0xff)<<24)
+#define m_CABC_T_LINE1_0 (0xff<<0)
+#define m_CABC_T_LINE1_1 (0xff<<8)
+#define m_CABC_T_LINE1_2 (0xff<<16)
+#define m_CABC_T_LINE1_3 ((u32)0xff<<24)
+
+#define CABC_GAUSS_LINE1_1 (0x01d4)
+#define v_CABC_T_LINE1_4(x) (((x)&0xff)<<0)
+#define v_CABC_T_LINE1_5(x) (((x)&0xff)<<8)
+#define v_CABC_T_LINE1_6(x) (((x)&0xff)<<16)
+#define m_CABC_T_LINE1_4 (0xff<<0)
+#define m_CABC_T_LINE1_5 (0xff<<8)
+#define m_CABC_T_LINE1_6 (0xff<<16)
+
+#define CABC_GAUSS_LINE2_0 (0x01d8)
+#define v_CABC_T_LINE2_0(x) (((x)&0xff)<<0)
+#define v_CABC_T_LINE2_1(x) (((x)&0xff)<<8)
+#define v_CABC_T_LINE2_2(x) (((x)&0xff)<<16)
+#define v_CABC_T_LINE2_3(x) (((x)&0xff)<<24)
+#define m_CABC_T_LINE2_0 (0xff<<0)
+#define m_CABC_T_LINE2_1 (0xff<<8)
+#define m_CABC_T_LINE2_2 (0xff<<16)
+#define m_CABC_T_LINE2_3 ((u32)0xff<<24)
+
+#define CABC_GAUSS_LINE2_1 (0x01dc)
+#define v_CABC_T_LINE2_4(x) (((x)&0xff)<<0)
+#define v_CABC_T_LINE2_5(x) (((x)&0xff)<<8)
+#define v_CABC_T_LINE2_6(x) (((x)&0xff)<<16)
+#define m_CABC_T_LINE2_4 (0xff<<0)
+#define m_CABC_T_LINE2_5 (0xff<<8)
+#define m_CABC_T_LINE2_6 (0xff<<16)
+
+/*FRC register*/
+#define FRC_LOWER01_0 (0x01e0)
+#define v_FRC_LOWER01_FRM0(x) (((x)&0xffff)<<0)
+#define v_FRC_LOWER01_FRM1(x) (((x)&0xffff)<<16)
+#define m_FRC_LOWER01_FRM0 (0xffff<<0)
+#define m_FRC_LOWER01_FRM1 ((u32)0xffff<<16)
+
+#define FRC_LOWER01_1 (0x01e4)
+#define v_FRC_LOWER01_FRM2(x) (((x)&0xffff)<<0)
+#define v_FRC_LOWER01_FRM3(x) (((x)&0xffff)<<16)
+#define m_FRC_LOWER01_FRM2 (0xffff<<0)
+#define m_FRC_LOWER01_FRM3 ((u32)0xffff<<16)
+
+#define FRC_LOWER10_0 (0x01e8)
+#define v_FRC_LOWER10_FRM0(x) (((x)&0xffff)<<0)
+#define v_FRC_LOWER10_FRM1(x) (((x)&0xffff)<<16)
+#define m_FRC_LOWER10_FRM0 (0xffff<<0)
+#define m_FRC_LOWER10_FRM1 ((u32)0xffff<<16)
+
+#define FRC_LOWER10_1 (0x01ec)
+#define v_FRC_LOWER10_FRM2(x) (((x)&0xffff)<<0)
+#define v_FRC_LOWER10_FRM3(x) (((x)&0xffff)<<16)
+#define m_FRC_LOWER10_FRM2 (0xffff<<0)
+#define m_FRC_LOWER10_FRM3 ((u32)0xffff<<16)
+
+#define FRC_LOWER11_0 (0x01f0)
+#define v_FRC_LOWER11_FRM0(x) (((x)&0xffff)<<0)
+#define v_FRC_LOWER11_FRM1(x) (((x)&0xffff)<<16)
+#define m_FRC_LOWER11_FRM0 (0xffff<<0)
+#define m_FRC_LOWER11_FRM1 ((u32)0xffff<<16)
+
+#define FRC_LOWER11_1 (0x01f4)
+#define v_FRC_LOWER11_FRM2(x) (((x)&0xffff)<<0)
+#define v_FRC_LOWER11_FRM3(x) (((x)&0xffff)<<16)
+#define m_FRC_LOWER11_FRM2 (0xffff<<0)
+#define m_FRC_LOWER11_FRM3 ((u32)0xffff<<16)
+
+#define MMU_DTE_ADDR (0x0300)
+#define v_MMU_DTE_ADDR(x) (((x)&0xffffffff)<<0)
+#define m_MMU_DTE_ADDR (0xffffffff<<0)
+
+#define MMU_STATUS (0x0304)
+#define v_PAGING_ENABLED(x) (((x)&1)<<0)
+#define v_PAGE_FAULT_ACTIVE(x) (((x)&1)<<1)
+#define v_STAIL_ACTIVE(x) (((x)&1)<<2)
+#define v_MMU_IDLE(x) (((x)&1)<<3)
+#define v_REPLAY_BUFFER_EMPTY(x) (((x)&1)<<4)
+#define v_PAGE_FAULT_IS_WRITE(x) (((x)&1)<<5)
+#define v_PAGE_FAULT_BUS_ID(x) (((x)&0x1f)<<6)
+#define m_PAGING_ENABLED (1<<0)
+#define m_PAGE_FAULT_ACTIVE (1<<1)
+#define m_STAIL_ACTIVE (1<<2)
+#define m_MMU_IDLE (1<<3)
+#define m_REPLAY_BUFFER_EMPTY (1<<4)
+#define m_PAGE_FAULT_IS_WRITE (1<<5)
+#define m_PAGE_FAULT_BUS_ID (0x1f<<6)
+
+#define MMU_COMMAND (0x0308)
+#define v_MMU_CMD(x) (((x)&0x3)<<0)
+#define m_MMU_CMD (0x3<<0)
+
+#define MMU_PAGE_FAULT_ADDR (0x030c)
+#define v_PAGE_FAULT_ADDR(x) (((x)&0xffffffff)<<0)
+#define m_PAGE_FAULT_ADDR (0xffffffff<<0)
+
+#define MMU_ZAP_ONE_LINE (0x0310)
+#define v_MMU_ZAP_ONE_LINE(x) (((x)&0xffffffff)<<0)
+#define m_MMU_ZAP_ONE_LINE (0xffffffff<<0)
+
+#define MMU_INT_RAWSTAT (0x0314)
+#define v_PAGE_FAULT_RAWSTAT(x) (((x)&1)<<0)
+#define v_READ_BUS_ERROR_RAWSTAT(x) (((x)&1)<<1)
+#define m_PAGE_FAULT_RAWSTAT (1<<0)
+#define m_READ_BUS_ERROR_RAWSTAT (1<<1)
+
+#define MMU_INT_CLEAR (0x0318)
+#define v_PAGE_FAULT_CLEAR(x) (((x)&1)<<0)
+#define v_READ_BUS_ERROR_CLEAR(x) (((x)&1)<<1)
+#define m_PAGE_FAULT_CLEAR (1<<0)
+#define m_READ_BUS_ERROR_CLEAR (1<<1)
+
+#define MMU_INT_MASK (0x031c)
+#define v_PAGE_FAULT_MASK(x) (((x)&1)<<0)
+#define v_READ_BUS_ERROR_MASK(x) (((x)&1)<<1)
+#define m_PAGE_FAULT_MASK (1<<0)
+#define m_READ_BUS_ERROR_MASK (1<<1)
+
+#define MMU_INT_STATUS (0x0320)
+#define v_PAGE_FAULT_STATUS(x) (((x)&1)<<0)
+#define v_READ_BUS_ERROR_STATUS(x) (((x)&1)<<1)
+#define m_PAGE_FAULT_STATUS (1<<0)
+#define m_READ_BUS_ERROR_STATUS (1<<1)
+
+#define MMU_AUTO_GATING (0x0324)
+#define v_MMU_AUTO_GATING(x) (((x)&1)<<0)
+#define m_MMU_AUTO_GATING (1<<0)
+
+#define WIN2_LUT_ADDR (0x0400)
+#define WIN3_LUT_ADDR (0x0800)
+#define HWC_LUT_ADDR (0x0c00)
+#define GAMMA_LUT_ADDR (0x1000)
+#define MCU_BYPASS_WPORT (0x2200)
+#define MCU_BYPASS_RPORT (0x2300)
+
+#define PWM_MODE_ONE_SHOT (0x0)
+#define PWM_MODE_CONTINUOUS (0x1)
+#define PWM_MODE_CAPTURE (0x2)
+//#endif
+
+
+#define CalScale(x, y) ((((u32)(x-1))*0x1000)/(y-1))
+
+
+
+/*ALPHA BLENDING MODE*/
+enum alpha_mode { /* Fs Fd */
+ AB_USER_DEFINE = 0x0,
+ AB_CLEAR = 0x1,/* 0 0*/
+ AB_SRC = 0x2,/* 1 0*/
+ AB_DST = 0x3,/* 0 1 */
+ AB_SRC_OVER = 0x4,/* 1 1-As''*/
+ AB_DST_OVER = 0x5,/* 1-Ad'' 1*/
+ AB_SRC_IN = 0x6,
+ AB_DST_IN = 0x7,
+ AB_SRC_OUT = 0x8,
+ AB_DST_OUT = 0x9,
+ AB_SRC_ATOP = 0xa,
+ AB_DST_ATOP = 0xb,
+ XOR = 0xc,
+ AB_SRC_OVER_GLOBAL = 0xd
+}; /*alpha_blending_mode*/
+
+enum src_alpha_mode {
+ AA_STRAIGHT = 0x0,
+ AA_INVERSE = 0x1
+};/*src_alpha_mode*/
+
+enum global_alpha_mode {
+ AA_GLOBAL = 0x0,
+ AA_PER_PIX = 0x1,
+ AA_PER_PIX_GLOBAL = 0x2
+};/*src_global_alpha_mode*/
+
+enum src_alpha_sel {
+ AA_SAT = 0x0,
+ AA_NO_SAT = 0x1
+};/*src_alpha_sel*/
+
+enum src_color_mode {
+ AA_SRC_PRE_MUL = 0x0,
+ AA_SRC_NO_PRE_MUL = 0x1
+};/*src_color_mode*/
+
+enum factor_mode {
+ AA_ZERO = 0x0,
+ AA_ONE = 0x1,
+ AA_SRC = 0x2,
+ AA_SRC_INVERSE = 0x3,
+ AA_SRC_GLOBAL = 0x4
+};/*src_factor_mode && dst_factor_mode*/
+struct alpha_config{
+ enum src_alpha_mode src_alpha_mode; /*win0_src_alpha_m0*/
+ u32 src_global_alpha_val; /*win0_src_global_alpha*/
+ enum global_alpha_mode src_global_alpha_mode;/*win0_src_blend_m0*/
+ enum src_alpha_sel src_alpha_cal_m0; /*win0_src_alpha_cal_m0*/
+ enum src_color_mode src_color_mode; /*win0_src_color_m0*/
+ enum factor_mode src_factor_mode; /*win0_src_factor_m0*/
+ enum factor_mode dst_factor_mode; /*win0_dst_factor_m0*/
+};
+
+
+static inline void lcdc_writel(struct fimd_context *ctx,u32 offset,u32 v)
+{
+ u32 *_pv = (u32*)ctx->regsbak;
+ _pv += (offset >> 2);
+ *_pv = v;
+ writel_relaxed(v,ctx->regs+offset);
+}
+
+static inline u32 lcdc_readl(struct fimd_context *ctx,u32 offset)
+{
+ u32 v;
+ u32 *_pv = (u32*)ctx->regsbak;
+ _pv += (offset >> 2);
+ v = readl_relaxed(ctx->regs+offset);
+ *_pv = v;
+ return v;
+}
+
+static inline u32 lcdc_read_bit(struct fimd_context *ctx,u32 offset,u32 msk)
+{
+ u32 _v = readl_relaxed(ctx->regs+offset);
+ _v &= msk;
+ return (_v >> msk);
+}
+
+static inline void lcdc_set_bit(struct fimd_context *ctx,u32 offset,u32 msk)
+{
+ u32* _pv = (u32*)ctx->regsbak;
+ _pv += (offset >> 2);
+ (*_pv) |= msk;
+ writel_relaxed(*_pv,ctx->regs + offset);
+}
+
+static inline void lcdc_clr_bit(struct fimd_context *ctx,u32 offset,u32 msk)
+{
+ u32* _pv = (u32*)ctx->regsbak;
+ _pv += (offset >> 2);
+ (*_pv) &= (~msk);
+ writel_relaxed(*_pv,ctx->regs + offset);
+}
+
+static inline void lcdc_msk_reg(struct fimd_context *ctx,u32 offset,u32 msk,u32 v)
+{
+ u32 *_pv = (u32*)ctx->regsbak;
+ _pv += (offset >> 2);
+ (*_pv) &= (~msk);
+ (*_pv) |= v;
+ writel_relaxed(*_pv,ctx->regs+offset);
+}
+
+static inline void lcdc_cfg_done(struct fimd_context *ctx)
+{
+ writel_relaxed(0x01,ctx->regs+REG_CFG_DONE);
+ dsb();
+}
--- /dev/null
+/* rockchip_drm_buf.c
+ *
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_buf.h"
+#include "rockchip_drm_iommu.h"
+
+static int lowlevel_buffer_allocate(struct drm_device *dev,
+ unsigned int flags, struct rockchip_drm_gem_buf *buf)
+{
+ int ret = 0;
+ enum dma_attr attr;
+ unsigned int nr_pages;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (buf->dma_addr) {
+ DRM_DEBUG_KMS("already allocated.\n");
+ return 0;
+ }
+
+ init_dma_attrs(&buf->dma_attrs);
+
+ /*
+ * if ROCKCHIP_BO_CONTIG, fully physically contiguous memory
+ * region will be allocated else physically contiguous
+ * as possible.
+ */
+ if (!(flags & ROCKCHIP_BO_NONCONTIG))
+ dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &buf->dma_attrs);
+
+ /*
+ * if ROCKCHIP_BO_WC or ROCKCHIP_BO_NONCACHABLE, writecombine mapping
+ * else cachable mapping.
+ */
+ if (flags & ROCKCHIP_BO_WC || !(flags & ROCKCHIP_BO_CACHABLE))
+ attr = DMA_ATTR_WRITE_COMBINE;
+ else
+ attr = DMA_ATTR_NON_CONSISTENT;
+
+ dma_set_attr(attr, &buf->dma_attrs);
+ dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buf->dma_attrs);
+
+ nr_pages = buf->size >> PAGE_SHIFT;
+
+ if (!is_drm_iommu_supported(dev)) {
+ dma_addr_t start_addr;
+ unsigned int i = 0;
+
+ buf->pages = kzalloc(sizeof(struct page) * nr_pages,
+ GFP_KERNEL);
+ if (!buf->pages) {
+ DRM_ERROR("failed to allocate pages.\n");
+ return -ENOMEM;
+ }
+
+ buf->kvaddr = dma_alloc_attrs(dev->dev, buf->size,
+ &buf->dma_addr, GFP_KERNEL,
+ &buf->dma_attrs);
+ if (!buf->kvaddr) {
+ DRM_ERROR("failed to allocate buffer.\n");
+ kfree(buf->pages);
+ return -ENOMEM;
+ }
+
+ start_addr = buf->dma_addr;
+ while (i < nr_pages) {
+ buf->pages[i] = phys_to_page(start_addr);
+ start_addr += PAGE_SIZE;
+ i++;
+ }
+ } else {
+
+ buf->pages = dma_alloc_attrs(dev->dev, buf->size,
+ &buf->dma_addr, GFP_KERNEL,
+ &buf->dma_attrs);
+ if (!buf->pages) {
+ DRM_ERROR("failed to allocate buffer.\n");
+ return -ENOMEM;
+ }
+ }
+
+ buf->sgt = drm_prime_pages_to_sg(buf->pages, nr_pages);
+ if (!buf->sgt) {
+ DRM_ERROR("failed to get sg table.\n");
+ ret = -ENOMEM;
+ goto err_free_attrs;
+ }
+
+ DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n",
+ (unsigned long)buf->dma_addr,
+ buf->size);
+
+ return ret;
+
+err_free_attrs:
+ dma_free_attrs(dev->dev, buf->size, buf->pages,
+ (dma_addr_t)buf->dma_addr, &buf->dma_attrs);
+ buf->dma_addr = (dma_addr_t)NULL;
+
+ if (!is_drm_iommu_supported(dev))
+ kfree(buf->pages);
+
+ return ret;
+}
+
+static void lowlevel_buffer_deallocate(struct drm_device *dev,
+ unsigned int flags, struct rockchip_drm_gem_buf *buf)
+{
+ DRM_DEBUG_KMS("%s.\n", __FILE__);
+
+ if (!buf->dma_addr) {
+ DRM_DEBUG_KMS("dma_addr is invalid.\n");
+ return;
+ }
+
+ DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n",
+ (unsigned long)buf->dma_addr,
+ buf->size);
+
+ sg_free_table(buf->sgt);
+
+ kfree(buf->sgt);
+ buf->sgt = NULL;
+
+ if (!is_drm_iommu_supported(dev)) {
+ dma_free_attrs(dev->dev, buf->size, buf->kvaddr,
+ (dma_addr_t)buf->dma_addr, &buf->dma_attrs);
+ kfree(buf->pages);
+ } else
+ dma_free_attrs(dev->dev, buf->size, buf->pages,
+ (dma_addr_t)buf->dma_addr, &buf->dma_attrs);
+
+ buf->dma_addr = (dma_addr_t)NULL;
+}
+
+struct rockchip_drm_gem_buf *rockchip_drm_init_buf(struct drm_device *dev,
+ unsigned int size)
+{
+ struct rockchip_drm_gem_buf *buffer;
+
+ DRM_DEBUG_KMS("%s.\n", __FILE__);
+ DRM_DEBUG_KMS("desired size = 0x%x\n", size);
+
+ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+ if (!buffer) {
+ DRM_ERROR("failed to allocate rockchip_drm_gem_buf.\n");
+ return NULL;
+ }
+
+ buffer->size = size;
+ return buffer;
+}
+
+void rockchip_drm_fini_buf(struct drm_device *dev,
+ struct rockchip_drm_gem_buf *buffer)
+{
+ DRM_DEBUG_KMS("%s.\n", __FILE__);
+
+ if (!buffer) {
+ DRM_DEBUG_KMS("buffer is null.\n");
+ return;
+ }
+
+ kfree(buffer);
+ buffer = NULL;
+}
+
+int rockchip_drm_alloc_buf(struct drm_device *dev,
+ struct rockchip_drm_gem_buf *buf, unsigned int flags)
+{
+
+ /*
+ * allocate memory region and set the memory information
+ * to vaddr and dma_addr of a buffer object.
+ */
+ if (lowlevel_buffer_allocate(dev, flags, buf) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void rockchip_drm_free_buf(struct drm_device *dev,
+ unsigned int flags, struct rockchip_drm_gem_buf *buffer)
+{
+
+ lowlevel_buffer_deallocate(dev, flags, buffer);
+}
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_BUF_H_
+#define _ROCKCHIP_DRM_BUF_H_
+
+/* create and initialize buffer object. */
+struct rockchip_drm_gem_buf *rockchip_drm_init_buf(struct drm_device *dev,
+ unsigned int size);
+
+/* destroy buffer object. */
+void rockchip_drm_fini_buf(struct drm_device *dev,
+ struct rockchip_drm_gem_buf *buffer);
+
+/* allocate physical memory region and setup sgt. */
+int rockchip_drm_alloc_buf(struct drm_device *dev,
+ struct rockchip_drm_gem_buf *buf,
+ unsigned int flags);
+
+/* release physical memory region, and sgt. */
+void rockchip_drm_free_buf(struct drm_device *dev,
+ unsigned int flags,
+ struct rockchip_drm_gem_buf *buffer);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include <drm/rockchip_drm.h>
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_encoder.h"
+
+#define to_rockchip_connector(x) container_of(x, struct rockchip_drm_connector,\
+ drm_connector)
+
+struct rockchip_drm_connector {
+ struct drm_connector drm_connector;
+ uint32_t encoder_id;
+ struct rockchip_drm_manager *manager;
+ uint32_t dpms;
+};
+
+/* convert rockchip_video_timings to drm_display_mode */
+static inline void
+convert_to_display_mode(struct drm_display_mode *mode,
+ struct rockchip_drm_panel_info *panel)
+{
+ struct fb_videomode *timing = &panel->timing;
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ mode->clock = timing->pixclock / 1000;
+ mode->vrefresh = timing->refresh;
+
+ mode->hdisplay = timing->xres;
+ mode->hsync_start = mode->hdisplay + timing->right_margin;
+ mode->hsync_end = mode->hsync_start + timing->hsync_len;
+ mode->htotal = mode->hsync_end + timing->left_margin;
+
+ mode->vdisplay = timing->yres;
+ mode->vsync_start = mode->vdisplay + timing->lower_margin;
+ mode->vsync_end = mode->vsync_start + timing->vsync_len;
+ mode->vtotal = mode->vsync_end + timing->upper_margin;
+ mode->width_mm = panel->width_mm;
+ mode->height_mm = panel->height_mm;
+
+ if (timing->vmode & FB_VMODE_INTERLACED)
+ mode->flags |= DRM_MODE_FLAG_INTERLACE;
+
+ if (timing->vmode & FB_VMODE_DOUBLE)
+ mode->flags |= DRM_MODE_FLAG_DBLSCAN;
+}
+
+/* convert drm_display_mode to rockchip_video_timings */
+static inline void
+convert_to_video_timing(struct fb_videomode *timing,
+ struct drm_display_mode *mode)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ memset(timing, 0, sizeof(*timing));
+
+ timing->pixclock = mode->clock * 1000;
+ timing->refresh = drm_mode_vrefresh(mode);
+
+ timing->xres = mode->hdisplay;
+ timing->right_margin = mode->hsync_start - mode->hdisplay;
+ timing->hsync_len = mode->hsync_end - mode->hsync_start;
+ timing->left_margin = mode->htotal - mode->hsync_end;
+
+ timing->yres = mode->vdisplay;
+ timing->lower_margin = mode->vsync_start - mode->vdisplay;
+ timing->vsync_len = mode->vsync_end - mode->vsync_start;
+ timing->upper_margin = mode->vtotal - mode->vsync_end;
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ timing->vmode = FB_VMODE_INTERLACED;
+ else
+ timing->vmode = FB_VMODE_NONINTERLACED;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ timing->vmode |= FB_VMODE_DOUBLE;
+}
+
+static int rockchip_drm_connector_get_modes(struct drm_connector *connector)
+{
+ struct rockchip_drm_connector *rockchip_connector =
+ to_rockchip_connector(connector);
+ struct rockchip_drm_manager *manager = rockchip_connector->manager;
+ struct rockchip_drm_display_ops *display_ops = manager->display_ops;
+ struct edid *edid = NULL;
+ unsigned int count = 0;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!display_ops) {
+ DRM_DEBUG_KMS("display_ops is null.\n");
+ return 0;
+ }
+
+ /*
+ * if get_edid() exists then get_edid() callback of hdmi side
+ * is called to get edid data through i2c interface else
+ * get timing from the FIMD driver(display controller).
+ *
+ * P.S. in case of lcd panel, count is always 1 if success
+ * because lcd panel has only one mode.
+ */
+ if (display_ops->get_edid) {
+ edid = display_ops->get_edid(manager->dev, connector);
+ if (IS_ERR_OR_NULL(edid)) {
+ ret = PTR_ERR(edid);
+ edid = NULL;
+ DRM_ERROR("Panel operation get_edid failed %d\n", ret);
+ goto out;
+ }
+
+ count = drm_add_edid_modes(connector, edid);
+ if (!count) {
+ DRM_ERROR("Add edid modes failed %d\n", count);
+ goto out;
+ }
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ } else {
+ struct rockchip_drm_panel_info *panel;
+ struct drm_display_mode *mode = drm_mode_create(connector->dev);
+ if (!mode) {
+ DRM_ERROR("failed to create a new display mode.\n");
+ return 0;
+ }
+
+ if (display_ops->get_panel)
+ panel = display_ops->get_panel(manager->dev);
+ else {
+ drm_mode_destroy(connector->dev, mode);
+ return 0;
+ }
+
+ convert_to_display_mode(mode, panel);
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+
+ count = 1;
+ }
+
+out:
+ kfree(edid);
+ return count;
+}
+
+static int rockchip_drm_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct rockchip_drm_connector *rockchip_connector =
+ to_rockchip_connector(connector);
+ struct rockchip_drm_manager *manager = rockchip_connector->manager;
+ struct rockchip_drm_display_ops *display_ops = manager->display_ops;
+ struct fb_videomode timing;
+ int ret = MODE_BAD;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ convert_to_video_timing(&timing, mode);
+
+ if (display_ops && display_ops->check_timing)
+ if (!display_ops->check_timing(manager->dev, (void *)&timing))
+ ret = MODE_OK;
+
+ return ret;
+}
+
+struct drm_encoder *rockchip_drm_best_encoder(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct rockchip_drm_connector *rockchip_connector =
+ to_rockchip_connector(connector);
+ struct drm_mode_object *obj;
+ struct drm_encoder *encoder;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ obj = drm_mode_object_find(dev, rockchip_connector->encoder_id,
+ DRM_MODE_OBJECT_ENCODER);
+ if (!obj) {
+ DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
+ rockchip_connector->encoder_id);
+ return NULL;
+ }
+
+ encoder = obj_to_encoder(obj);
+
+ return encoder;
+}
+
+static struct drm_connector_helper_funcs rockchip_connector_helper_funcs = {
+ .get_modes = rockchip_drm_connector_get_modes,
+ .mode_valid = rockchip_drm_connector_mode_valid,
+ .best_encoder = rockchip_drm_best_encoder,
+};
+
+void rockchip_drm_display_power(struct drm_connector *connector, int mode)
+{
+ struct drm_encoder *encoder = rockchip_drm_best_encoder(connector);
+ struct rockchip_drm_connector *rockchip_connector;
+ struct rockchip_drm_manager *manager = rockchip_drm_get_manager(encoder);
+ struct rockchip_drm_display_ops *display_ops = manager->display_ops;
+
+ rockchip_connector = to_rockchip_connector(connector);
+
+ if (rockchip_connector->dpms == mode) {
+ DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
+ return;
+ }
+
+ if (display_ops && display_ops->power_on)
+ display_ops->power_on(manager->dev, mode);
+
+ rockchip_connector->dpms = mode;
+}
+
+static void rockchip_drm_connector_dpms(struct drm_connector *connector,
+ int mode)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * in case that drm_crtc_helper_set_mode() is called,
+ * encoder/crtc->funcs->dpms() will be just returned
+ * because they already were DRM_MODE_DPMS_ON so only
+ * rockchip_drm_display_power() will be called.
+ */
+ drm_helper_connector_dpms(connector, mode);
+
+ rockchip_drm_display_power(connector, mode);
+
+}
+
+static int rockchip_drm_connector_fill_modes(struct drm_connector *connector,
+ unsigned int max_width, unsigned int max_height)
+{
+ struct rockchip_drm_connector *rockchip_connector =
+ to_rockchip_connector(connector);
+ struct rockchip_drm_manager *manager = rockchip_connector->manager;
+ struct rockchip_drm_manager_ops *ops = manager->ops;
+ unsigned int width, height;
+
+ width = max_width;
+ height = max_height;
+
+ /*
+ * if specific driver want to find desired_mode using maxmum
+ * resolution then get max width and height from that driver.
+ */
+ if (ops && ops->get_max_resol)
+ ops->get_max_resol(manager->dev, &width, &height);
+
+ return drm_helper_probe_single_connector_modes(connector, width,
+ height);
+}
+
+/* get detection status of display device. */
+static enum drm_connector_status
+rockchip_drm_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct rockchip_drm_connector *rockchip_connector =
+ to_rockchip_connector(connector);
+ struct rockchip_drm_manager *manager = rockchip_connector->manager;
+ struct rockchip_drm_display_ops *display_ops =
+ manager->display_ops;
+ enum drm_connector_status status = connector_status_disconnected;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (display_ops && display_ops->is_connected) {
+ if (display_ops->is_connected(manager->dev))
+ status = connector_status_connected;
+ else
+ status = connector_status_disconnected;
+ }
+
+ return status;
+}
+
+static void rockchip_drm_connector_destroy(struct drm_connector *connector)
+{
+ struct rockchip_drm_connector *rockchip_connector =
+ to_rockchip_connector(connector);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(rockchip_connector);
+}
+
+static struct drm_connector_funcs rockchip_connector_funcs = {
+ .dpms = rockchip_drm_connector_dpms,
+ .fill_modes = rockchip_drm_connector_fill_modes,
+ .detect = rockchip_drm_connector_detect,
+ .destroy = rockchip_drm_connector_destroy,
+};
+
+struct drm_connector *rockchip_drm_connector_create(struct drm_device *dev,
+ struct drm_encoder *encoder)
+{
+ struct rockchip_drm_connector *rockchip_connector;
+ struct rockchip_drm_manager *manager = rockchip_drm_get_manager(encoder);
+ struct drm_connector *connector;
+ int type;
+ int err;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ rockchip_connector = kzalloc(sizeof(*rockchip_connector), GFP_KERNEL);
+ if (!rockchip_connector) {
+ DRM_ERROR("failed to allocate connector\n");
+ return NULL;
+ }
+
+ connector = &rockchip_connector->drm_connector;
+
+ switch (manager->display_ops->type) {
+ case ROCKCHIP_DISPLAY_TYPE_HDMI:
+ type = DRM_MODE_CONNECTOR_HDMIA;
+ connector->interlace_allowed = true;
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+ break;
+ case ROCKCHIP_DISPLAY_TYPE_VIDI:
+ type = DRM_MODE_CONNECTOR_VIRTUAL;
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+ break;
+ case ROCKCHIP_DISPLAY_TYPE_LCD:
+ type = DRM_MODE_CONNECTOR_LVDS;
+ break;
+ default:
+ type = DRM_MODE_CONNECTOR_Unknown;
+ break;
+ }
+
+ drm_connector_init(dev, connector, &rockchip_connector_funcs, type);
+ drm_connector_helper_add(connector, &rockchip_connector_helper_funcs);
+
+ err = drm_sysfs_connector_add(connector);
+ if (err)
+ goto err_connector;
+
+ rockchip_connector->encoder_id = encoder->base.id;
+ rockchip_connector->manager = manager;
+ rockchip_connector->dpms = DRM_MODE_DPMS_OFF;
+ connector->dpms = DRM_MODE_DPMS_OFF;
+ connector->encoder = encoder;
+
+ err = drm_mode_connector_attach_encoder(connector, encoder);
+ if (err) {
+ DRM_ERROR("failed to attach a connector to a encoder\n");
+ goto err_sysfs;
+ }
+
+ DRM_DEBUG_KMS("connector has been created\n");
+
+ return connector;
+
+err_sysfs:
+ drm_sysfs_connector_remove(connector);
+err_connector:
+ drm_connector_cleanup(connector);
+ kfree(rockchip_connector);
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_CONNECTOR_H_
+#define _ROCKCHIP_DRM_CONNECTOR_H_
+
+struct drm_connector *rockchip_drm_connector_create(struct drm_device *dev,
+ struct drm_encoder *encoder);
+
+struct drm_encoder *rockchip_drm_best_encoder(struct drm_connector *connector);
+
+void rockchip_drm_display_power(struct drm_connector *connector, int mode);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_encoder.h"
+#include "rockchip_drm_connector.h"
+#include "rockchip_drm_fbdev.h"
+
+static LIST_HEAD(rockchip_drm_subdrv_list);
+
+static int rockchip_drm_create_enc_conn(struct drm_device *dev,
+ struct rockchip_drm_subdrv *subdrv)
+{
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ int ret;
+
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ subdrv->manager->dev = subdrv->dev;
+
+ /* create and initialize a encoder for this sub driver. */
+ encoder = rockchip_drm_encoder_create(dev, subdrv->manager,
+ (1 << MAX_CRTC) - 1);
+ if (!encoder) {
+ DRM_ERROR("failed to create encoder\n");
+ return -EFAULT;
+ }
+
+ /*
+ * create and initialize a connector for this sub driver and
+ * attach the encoder created above to the connector.
+ */
+ connector = rockchip_drm_connector_create(dev, encoder);
+ if (!connector) {
+ DRM_ERROR("failed to create connector\n");
+ ret = -EFAULT;
+ goto err_destroy_encoder;
+ }
+
+ subdrv->encoder = encoder;
+ subdrv->connector = connector;
+
+ return 0;
+
+err_destroy_encoder:
+ encoder->funcs->destroy(encoder);
+ return ret;
+}
+
+static void rockchip_drm_destroy_enc_conn(struct rockchip_drm_subdrv *subdrv)
+{
+ if (subdrv->encoder) {
+ struct drm_encoder *encoder = subdrv->encoder;
+ encoder->funcs->destroy(encoder);
+ subdrv->encoder = NULL;
+ }
+
+ if (subdrv->connector) {
+ struct drm_connector *connector = subdrv->connector;
+ connector->funcs->destroy(connector);
+ subdrv->connector = NULL;
+ }
+}
+
+static int rockchip_drm_subdrv_probe(struct drm_device *dev,
+ struct rockchip_drm_subdrv *subdrv)
+{
+ if (subdrv->probe) {
+ int ret;
+
+ subdrv->drm_dev = dev;
+
+ /*
+ * this probe callback would be called by sub driver
+ * after setting of all resources to this sub driver,
+ * such as clock, irq and register map are done or by load()
+ * of rockchip drm driver.
+ *
+ * P.S. note that this driver is considered for modularization.
+ */
+ ret = subdrv->probe(dev, subdrv->dev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rockchip_drm_subdrv_remove(struct drm_device *dev,
+ struct rockchip_drm_subdrv *subdrv)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ if (subdrv->remove)
+ subdrv->remove(dev, subdrv->dev);
+}
+
+int rockchip_drm_device_register(struct drm_device *dev)
+{
+ struct rockchip_drm_subdrv *subdrv, *n;
+ unsigned int fine_cnt = 0;
+ int err;
+
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ if (!dev)
+ return -EINVAL;
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ list_for_each_entry_safe(subdrv, n, &rockchip_drm_subdrv_list, list) {
+ err = rockchip_drm_subdrv_probe(dev, subdrv);
+ if (err) {
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ DRM_DEBUG("rockchip drm subdrv probe failed.\n");
+ list_del(&subdrv->list);
+ continue;
+ }
+
+ /*
+ * if manager is null then it means that this sub driver
+ * doesn't need encoder and connector.
+ */
+ if (!subdrv->manager) {
+ fine_cnt++;
+ continue;
+ }
+
+ err = rockchip_drm_create_enc_conn(dev, subdrv);
+ if (err) {
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ DRM_DEBUG("failed to create encoder and connector.\n");
+ rockchip_drm_subdrv_remove(dev, subdrv);
+ list_del(&subdrv->list);
+ continue;
+ }
+
+ fine_cnt++;
+ }
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ if (!fine_cnt)
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rockchip_drm_device_register);
+
+int rockchip_drm_device_unregister(struct drm_device *dev)
+{
+ struct rockchip_drm_subdrv *subdrv;
+
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ if (!dev) {
+ WARN(1, "Unexpected drm device unregister!\n");
+ return -EINVAL;
+ }
+
+ list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list) {
+ rockchip_drm_subdrv_remove(dev, subdrv);
+ rockchip_drm_destroy_enc_conn(subdrv);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rockchip_drm_device_unregister);
+
+int rockchip_drm_subdrv_register(struct rockchip_drm_subdrv *subdrv)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ if (!subdrv)
+ return -EINVAL;
+
+ list_add_tail(&subdrv->list, &rockchip_drm_subdrv_list);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rockchip_drm_subdrv_register);
+
+int rockchip_drm_subdrv_unregister(struct rockchip_drm_subdrv *subdrv)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ if (!subdrv)
+ return -EINVAL;
+
+ list_del(&subdrv->list);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rockchip_drm_subdrv_unregister);
+
+int rockchip_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
+{
+ struct rockchip_drm_subdrv *subdrv;
+ int ret;
+
+ list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list) {
+ if (subdrv->open) {
+ ret = subdrv->open(dev, subdrv->dev, file);
+ if (ret)
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ list_for_each_entry_reverse(subdrv, &subdrv->list, list) {
+ if (subdrv->close)
+ subdrv->close(dev, subdrv->dev, file);
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rockchip_drm_subdrv_open);
+
+void rockchip_drm_subdrv_close(struct drm_device *dev, struct drm_file *file)
+{
+ struct rockchip_drm_subdrv *subdrv;
+
+ list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list) {
+ if (subdrv->close)
+ subdrv->close(dev, subdrv->dev, file);
+ }
+}
+EXPORT_SYMBOL_GPL(rockchip_drm_subdrv_close);
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_encoder.h"
+#include "rockchip_drm_plane.h"
+
+#define to_rockchip_crtc(x) container_of(x, struct rockchip_drm_crtc,\
+ drm_crtc)
+
+enum rockchip_crtc_mode {
+ CRTC_MODE_NORMAL, /* normal mode */
+ CRTC_MODE_BLANK, /* The private plane of crtc is blank */
+};
+
+/*
+ * Exynos specific crtc structure.
+ *
+ * @drm_crtc: crtc object.
+ * @drm_plane: pointer of private plane object for this crtc
+ * @pipe: a crtc index created at load() with a new crtc object creation
+ * and the crtc object would be set to private->crtc array
+ * to get a crtc object corresponding to this pipe from private->crtc
+ * array when irq interrupt occured. the reason of using this pipe is that
+ * drm framework doesn't support multiple irq yet.
+ * we can refer to the crtc to current hardware interrupt occured through
+ * this pipe value.
+ * @dpms: store the crtc dpms value
+ * @mode: store the crtc mode value
+ */
+struct rockchip_drm_crtc {
+ struct drm_crtc drm_crtc;
+ struct drm_plane *plane;
+ unsigned int pipe;
+ unsigned int dpms;
+ enum rockchip_crtc_mode mode;
+ wait_queue_head_t pending_flip_queue;
+ atomic_t pending_flip;
+};
+
+static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
+
+ DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
+
+ if (rockchip_crtc->dpms == mode) {
+ DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
+ return;
+ }
+
+ if (mode > DRM_MODE_DPMS_ON) {
+ /* wait for the completion of page flip. */
+ wait_event(rockchip_crtc->pending_flip_queue,
+ atomic_read(&rockchip_crtc->pending_flip) == 0);
+ drm_vblank_off(crtc->dev, rockchip_crtc->pipe);
+ }
+
+ rockchip_drm_fn_encoder(crtc, &mode, rockchip_drm_encoder_crtc_dpms);
+ rockchip_crtc->dpms = mode;
+}
+
+static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* drm framework doesn't check NULL. */
+}
+
+static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
+{
+ struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+ rockchip_plane_commit(rockchip_crtc->plane);
+ rockchip_plane_dpms(rockchip_crtc->plane, DRM_MODE_DPMS_ON);
+}
+
+static bool
+rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* drm framework doesn't check NULL */
+ return true;
+}
+
+static int
+rockchip_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
+ struct drm_plane *plane = rockchip_crtc->plane;
+ unsigned int crtc_w;
+ unsigned int crtc_h;
+ int pipe = rockchip_crtc->pipe;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * copy the mode data adjusted by mode_fixup() into crtc->mode
+ * so that hardware can be seet to proper mode.
+ */
+ memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
+
+ crtc_w = crtc->fb->width - x;
+ crtc_h = crtc->fb->height - y;
+
+ ret = rockchip_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
+ x, y, crtc_w, crtc_h);
+ if (ret)
+ return ret;
+
+ plane->crtc = crtc;
+ plane->fb = crtc->fb;
+
+ rockchip_drm_fn_encoder(crtc, &pipe, rockchip_drm_encoder_crtc_pipe);
+
+ return 0;
+}
+
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
+ struct drm_plane *plane = rockchip_crtc->plane;
+ unsigned int crtc_w;
+ unsigned int crtc_h;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* when framebuffer changing is requested, crtc's dpms should be on */
+ if (rockchip_crtc->dpms > DRM_MODE_DPMS_ON) {
+ DRM_ERROR("failed framebuffer changing request.\n");
+ return -EPERM;
+ }
+
+ crtc_w = crtc->fb->width - x;
+ crtc_h = crtc->fb->height - y;
+
+ ret = rockchip_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
+ x, y, crtc_w, crtc_h);
+ if (ret)
+ return ret;
+
+ rockchip_drm_crtc_commit(crtc);
+
+ return 0;
+}
+
+static void rockchip_drm_crtc_load_lut(struct drm_crtc *crtc)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+ /* drm framework doesn't check NULL */
+}
+
+static void rockchip_drm_crtc_disable(struct drm_crtc *crtc)
+{
+ struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ rockchip_plane_dpms(rockchip_crtc->plane, DRM_MODE_DPMS_OFF);
+ rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
+ .dpms = rockchip_drm_crtc_dpms,
+ .prepare = rockchip_drm_crtc_prepare,
+ .commit = rockchip_drm_crtc_commit,
+ .mode_fixup = rockchip_drm_crtc_mode_fixup,
+ .mode_set = rockchip_drm_crtc_mode_set,
+ .mode_set_base = rockchip_drm_crtc_mode_set_base,
+ .load_lut = rockchip_drm_crtc_load_lut,
+ .disable = rockchip_drm_crtc_disable,
+};
+
+static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event)
+{
+ struct drm_device *dev = crtc->dev;
+ struct rockchip_drm_private *dev_priv = dev->dev_private;
+ struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
+ struct drm_framebuffer *old_fb = crtc->fb;
+ int ret = -EINVAL;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* when the page flip is requested, crtc's dpms should be on */
+ if (rockchip_crtc->dpms > DRM_MODE_DPMS_ON) {
+ DRM_ERROR("failed page flip request.\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->struct_mutex);
+
+ if (event) {
+ /*
+ * the pipe from user always is 0 so we can set pipe number
+ * of current owner to event.
+ */
+ event->pipe = rockchip_crtc->pipe;
+
+ ret = drm_vblank_get(dev, rockchip_crtc->pipe);
+ if (ret) {
+ DRM_DEBUG("failed to acquire vblank counter\n");
+
+ goto out;
+ }
+
+ spin_lock_irq(&dev->event_lock);
+ list_add_tail(&event->base.link,
+ &dev_priv->pageflip_event_list);
+ atomic_set(&rockchip_crtc->pending_flip, 1);
+ spin_unlock_irq(&dev->event_lock);
+
+ crtc->fb = fb;
+ ret = rockchip_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y,
+ NULL);
+ if (ret) {
+ crtc->fb = old_fb;
+
+ spin_lock_irq(&dev->event_lock);
+ drm_vblank_put(dev, rockchip_crtc->pipe);
+ list_del(&event->base.link);
+ spin_unlock_irq(&dev->event_lock);
+
+ goto out;
+ }
+ }
+out:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
+ struct rockchip_drm_private *private = crtc->dev->dev_private;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ private->crtc[rockchip_crtc->pipe] = NULL;
+
+ drm_crtc_cleanup(crtc);
+ kfree(rockchip_crtc);
+}
+
+static int rockchip_drm_crtc_set_property(struct drm_crtc *crtc,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct drm_device *dev = crtc->dev;
+ struct rockchip_drm_private *dev_priv = dev->dev_private;
+ struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
+
+ DRM_DEBUG_KMS("%s\n", __func__);
+
+ if (property == dev_priv->crtc_mode_property) {
+ enum rockchip_crtc_mode mode = val;
+
+ if (mode == rockchip_crtc->mode)
+ return 0;
+
+ rockchip_crtc->mode = mode;
+
+ switch (mode) {
+ case CRTC_MODE_NORMAL:
+ rockchip_drm_crtc_commit(crtc);
+ break;
+ case CRTC_MODE_BLANK:
+ rockchip_plane_dpms(rockchip_crtc->plane,
+ DRM_MODE_DPMS_OFF);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static struct drm_crtc_funcs rockchip_crtc_funcs = {
+ .set_config = drm_crtc_helper_set_config,
+ .page_flip = rockchip_drm_crtc_page_flip,
+ .destroy = rockchip_drm_crtc_destroy,
+ .set_property = rockchip_drm_crtc_set_property,
+};
+
+static const struct drm_prop_enum_list mode_names[] = {
+ { CRTC_MODE_NORMAL, "normal" },
+ { CRTC_MODE_BLANK, "blank" },
+};
+
+static void rockchip_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct rockchip_drm_private *dev_priv = dev->dev_private;
+ struct drm_property *prop;
+
+ DRM_DEBUG_KMS("%s\n", __func__);
+
+ prop = dev_priv->crtc_mode_property;
+ if (!prop) {
+ prop = drm_property_create_enum(dev, 0, "mode", mode_names,
+ ARRAY_SIZE(mode_names));
+ if (!prop)
+ return;
+
+ dev_priv->crtc_mode_property = prop;
+ }
+
+ drm_object_attach_property(&crtc->base, prop, 0);
+}
+
+int rockchip_drm_crtc_create(struct drm_device *dev, unsigned int nr)
+{
+ struct rockchip_drm_crtc *rockchip_crtc;
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct drm_crtc *crtc;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ rockchip_crtc = kzalloc(sizeof(*rockchip_crtc), GFP_KERNEL);
+ if (!rockchip_crtc) {
+ DRM_ERROR("failed to allocate rockchip crtc\n");
+ return -ENOMEM;
+ }
+
+ rockchip_crtc->pipe = nr;
+ rockchip_crtc->dpms = DRM_MODE_DPMS_OFF;
+ init_waitqueue_head(&rockchip_crtc->pending_flip_queue);
+ atomic_set(&rockchip_crtc->pending_flip, 0);
+ rockchip_crtc->plane = rockchip_plane_init(dev, 1 << nr, true);
+ if (!rockchip_crtc->plane) {
+ kfree(rockchip_crtc);
+ return -ENOMEM;
+ }
+
+ crtc = &rockchip_crtc->drm_crtc;
+
+ private->crtc[nr] = crtc;
+
+ drm_crtc_init(dev, crtc, &rockchip_crtc_funcs);
+ drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
+
+ rockchip_drm_crtc_attach_mode_property(crtc);
+
+ return 0;
+}
+
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct rockchip_drm_crtc *rockchip_crtc =
+ to_rockchip_crtc(private->crtc[crtc]);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (rockchip_crtc->dpms != DRM_MODE_DPMS_ON)
+ return -EPERM;
+
+ rockchip_drm_fn_encoder(private->crtc[crtc], &crtc,
+ rockchip_drm_enable_vblank);
+
+ return 0;
+}
+
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct rockchip_drm_crtc *rockchip_crtc =
+ to_rockchip_crtc(private->crtc[crtc]);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (rockchip_crtc->dpms != DRM_MODE_DPMS_ON)
+ return;
+
+ rockchip_drm_fn_encoder(private->crtc[crtc], &crtc,
+ rockchip_drm_disable_vblank);
+}
+
+void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc)
+{
+ struct rockchip_drm_private *dev_priv = dev->dev_private;
+ struct drm_pending_vblank_event *e, *t;
+ struct drm_crtc *drm_crtc = dev_priv->crtc[crtc];
+ struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(drm_crtc);
+ unsigned long flags;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+
+ list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
+ base.link) {
+ /* if event's pipe isn't same as crtc then ignore it. */
+ if (crtc != e->pipe)
+ continue;
+
+ list_del(&e->base.link);
+ drm_send_vblank_event(dev, -1, e);
+ drm_vblank_put(dev, crtc);
+ atomic_set(&rockchip_crtc->pending_flip, 0);
+ wake_up(&rockchip_crtc->pending_flip_queue);
+ }
+
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+}
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_CRTC_H_
+#define _ROCKCHIP_DRM_CRTC_H_
+
+int rockchip_drm_crtc_create(struct drm_device *dev, unsigned int nr);
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
+void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/rockchip_drm.h>
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+#include <linux/dma-buf.h>
+
+struct rockchip_drm_dmabuf_attachment {
+ struct sg_table sgt;
+ enum dma_data_direction dir;
+ bool is_mapped;
+};
+
+static int rockchip_gem_attach_dma_buf(struct dma_buf *dmabuf,
+ struct device *dev,
+ struct dma_buf_attachment *attach)
+{
+ struct rockchip_drm_dmabuf_attachment *rockchip_attach;
+
+ rockchip_attach = kzalloc(sizeof(*rockchip_attach), GFP_KERNEL);
+ if (!rockchip_attach)
+ return -ENOMEM;
+
+ rockchip_attach->dir = DMA_NONE;
+ attach->priv = rockchip_attach;
+
+ return 0;
+}
+
+static void rockchip_gem_detach_dma_buf(struct dma_buf *dmabuf,
+ struct dma_buf_attachment *attach)
+{
+ struct rockchip_drm_dmabuf_attachment *rockchip_attach = attach->priv;
+ struct sg_table *sgt;
+
+ if (!rockchip_attach)
+ return;
+
+ sgt = &rockchip_attach->sgt;
+
+ if (rockchip_attach->dir != DMA_NONE)
+ dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents,
+ rockchip_attach->dir);
+
+ sg_free_table(sgt);
+ kfree(rockchip_attach);
+ attach->priv = NULL;
+}
+
+static struct sg_table *
+ rockchip_gem_map_dma_buf(struct dma_buf_attachment *attach,
+ enum dma_data_direction dir)
+{
+ struct rockchip_drm_dmabuf_attachment *rockchip_attach = attach->priv;
+ struct rockchip_drm_gem_obj *gem_obj = attach->dmabuf->priv;
+ struct drm_device *dev = gem_obj->base.dev;
+ struct rockchip_drm_gem_buf *buf;
+ struct scatterlist *rd, *wr;
+ struct sg_table *sgt = NULL;
+ unsigned int i;
+ int nents, ret;
+
+ DRM_DEBUG_PRIME("%s\n", __FILE__);
+
+ /* just return current sgt if already requested. */
+ if (rockchip_attach->dir == dir && rockchip_attach->is_mapped)
+ return &rockchip_attach->sgt;
+
+ buf = gem_obj->buffer;
+ if (!buf) {
+ DRM_ERROR("buffer is null.\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sgt = &rockchip_attach->sgt;
+
+ ret = sg_alloc_table(sgt, buf->sgt->orig_nents, GFP_KERNEL);
+ if (ret) {
+ DRM_ERROR("failed to alloc sgt.\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ mutex_lock(&dev->struct_mutex);
+
+ rd = buf->sgt->sgl;
+ wr = sgt->sgl;
+ for (i = 0; i < sgt->orig_nents; ++i) {
+ sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
+ rd = sg_next(rd);
+ wr = sg_next(wr);
+ }
+
+ if (dir != DMA_NONE) {
+ nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir);
+ if (!nents) {
+ DRM_ERROR("failed to map sgl with iommu.\n");
+ sg_free_table(sgt);
+ sgt = ERR_PTR(-EIO);
+ goto err_unlock;
+ }
+ }
+
+ rockchip_attach->is_mapped = true;
+ rockchip_attach->dir = dir;
+ attach->priv = rockchip_attach;
+
+ DRM_DEBUG_PRIME("buffer size = 0x%lx\n", buf->size);
+
+err_unlock:
+ mutex_unlock(&dev->struct_mutex);
+ return sgt;
+}
+
+static void rockchip_gem_unmap_dma_buf(struct dma_buf_attachment *attach,
+ struct sg_table *sgt,
+ enum dma_data_direction dir)
+{
+ /* Nothing to do. */
+}
+
+static void rockchip_dmabuf_release(struct dma_buf *dmabuf)
+{
+ struct rockchip_drm_gem_obj *rockchip_gem_obj = dmabuf->priv;
+
+ DRM_DEBUG_PRIME("%s\n", __FILE__);
+
+ /*
+ * rockchip_dmabuf_release() call means that file object's
+ * f_count is 0 and it calls drm_gem_object_handle_unreference()
+ * to drop the references that these values had been increased
+ * at drm_prime_handle_to_fd()
+ */
+ if (rockchip_gem_obj->base.export_dma_buf == dmabuf) {
+ rockchip_gem_obj->base.export_dma_buf = NULL;
+
+ /*
+ * drop this gem object refcount to release allocated buffer
+ * and resources.
+ */
+ drm_gem_object_unreference_unlocked(&rockchip_gem_obj->base);
+ }
+}
+
+static void *rockchip_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf,
+ unsigned long page_num)
+{
+ /* TODO */
+
+ return NULL;
+}
+
+static void rockchip_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf,
+ unsigned long page_num,
+ void *addr)
+{
+ /* TODO */
+}
+
+static void *rockchip_gem_dmabuf_kmap(struct dma_buf *dma_buf,
+ unsigned long page_num)
+{
+ /* TODO */
+
+ return NULL;
+}
+
+static void rockchip_gem_dmabuf_kunmap(struct dma_buf *dma_buf,
+ unsigned long page_num, void *addr)
+{
+ /* TODO */
+}
+
+static int rockchip_gem_dmabuf_mmap(struct dma_buf *dma_buf,
+ struct vm_area_struct *vma)
+{
+ return -ENOTTY;
+}
+
+static struct dma_buf_ops rockchip_dmabuf_ops = {
+ .attach = rockchip_gem_attach_dma_buf,
+ .detach = rockchip_gem_detach_dma_buf,
+ .map_dma_buf = rockchip_gem_map_dma_buf,
+ .unmap_dma_buf = rockchip_gem_unmap_dma_buf,
+ .kmap = rockchip_gem_dmabuf_kmap,
+ .kmap_atomic = rockchip_gem_dmabuf_kmap_atomic,
+ .kunmap = rockchip_gem_dmabuf_kunmap,
+ .kunmap_atomic = rockchip_gem_dmabuf_kunmap_atomic,
+ .mmap = rockchip_gem_dmabuf_mmap,
+ .release = rockchip_dmabuf_release,
+};
+
+struct dma_buf *rockchip_dmabuf_prime_export(struct drm_device *drm_dev,
+ struct drm_gem_object *obj, int flags)
+{
+ struct rockchip_drm_gem_obj *rockchip_gem_obj = to_rockchip_gem_obj(obj);
+
+ return dma_buf_export(rockchip_gem_obj, &rockchip_dmabuf_ops,
+ rockchip_gem_obj->base.size, flags);
+}
+
+struct drm_gem_object *rockchip_dmabuf_prime_import(struct drm_device *drm_dev,
+ struct dma_buf *dma_buf)
+{
+ struct dma_buf_attachment *attach;
+ struct sg_table *sgt;
+ struct scatterlist *sgl;
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ struct rockchip_drm_gem_buf *buffer;
+ int ret;
+
+ DRM_DEBUG_PRIME("%s\n", __FILE__);
+
+ /* is this one of own objects? */
+ if (dma_buf->ops == &rockchip_dmabuf_ops) {
+ struct drm_gem_object *obj;
+
+ rockchip_gem_obj = dma_buf->priv;
+ obj = &rockchip_gem_obj->base;
+
+ /* is it from our device? */
+ if (obj->dev == drm_dev) {
+ /*
+ * Importing dmabuf exported from out own gem increases
+ * refcount on gem itself instead of f_count of dmabuf.
+ */
+ drm_gem_object_reference(obj);
+ return obj;
+ }
+ }
+
+ attach = dma_buf_attach(dma_buf, drm_dev->dev);
+ if (IS_ERR(attach))
+ return ERR_PTR(-EINVAL);
+
+ get_dma_buf(dma_buf);
+
+ sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+ if (IS_ERR_OR_NULL(sgt)) {
+ ret = PTR_ERR(sgt);
+ goto err_buf_detach;
+ }
+
+ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+ if (!buffer) {
+ DRM_ERROR("failed to allocate rockchip_drm_gem_buf.\n");
+ ret = -ENOMEM;
+ goto err_unmap_attach;
+ }
+
+ rockchip_gem_obj = rockchip_drm_gem_init(drm_dev, dma_buf->size);
+ if (!rockchip_gem_obj) {
+ ret = -ENOMEM;
+ goto err_free_buffer;
+ }
+
+ sgl = sgt->sgl;
+
+ buffer->size = dma_buf->size;
+ buffer->dma_addr = sg_dma_address(sgl);
+
+ if (sgt->nents == 1) {
+ /* always physically continuous memory if sgt->nents is 1. */
+ rockchip_gem_obj->flags |= ROCKCHIP_BO_CONTIG;
+ } else {
+ /*
+ * this case could be CONTIG or NONCONTIG type but for now
+ * sets NONCONTIG.
+ * TODO. we have to find a way that exporter can notify
+ * the type of its own buffer to importer.
+ */
+ rockchip_gem_obj->flags |= ROCKCHIP_BO_NONCONTIG;
+ }
+
+ rockchip_gem_obj->buffer = buffer;
+ buffer->sgt = sgt;
+ rockchip_gem_obj->base.import_attach = attach;
+
+ DRM_DEBUG_PRIME("dma_addr = 0x%x, size = 0x%lx\n", buffer->dma_addr,
+ buffer->size);
+
+ return &rockchip_gem_obj->base;
+
+err_free_buffer:
+ kfree(buffer);
+ buffer = NULL;
+err_unmap_attach:
+ dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
+err_buf_detach:
+ dma_buf_detach(dma_buf, attach);
+ dma_buf_put(dma_buf);
+
+ return ERR_PTR(ret);
+}
+
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_DMABUF_H_
+#define _ROCKCHIP_DRM_DMABUF_H_
+
+#ifdef CONFIG_DRM_ROCKCHIP_DMABUF
+struct dma_buf *rockchip_dmabuf_prime_export(struct drm_device *drm_dev,
+ struct drm_gem_object *obj, int flags);
+
+struct drm_gem_object *rockchip_dmabuf_prime_import(struct drm_device *drm_dev,
+ struct dma_buf *dma_buf);
+#else
+#define rockchip_dmabuf_prime_export NULL
+#define rockchip_dmabuf_prime_import NULL
+#endif
+#endif
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_crtc.h"
+#include "rockchip_drm_encoder.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_plane.h"
+#include "rockchip_drm_dmabuf.h"
+#include "rockchip_drm_iommu.h"
+
+#define DRIVER_NAME "rockchip"
+#define DRIVER_DESC "rockchip Soc DRM"
+#define DRIVER_DATE "20140318"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+#define VBLANK_OFF_DELAY 50000
+
+/* platform device pointer for eynos drm device. */
+static struct platform_device *rockchip_drm_pdev;
+
+static int rockchip_drm_load(struct drm_device *dev, unsigned long flags)
+{
+ struct rockchip_drm_private *private;
+ int ret;
+ int nr;
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ private = kzalloc(sizeof(struct rockchip_drm_private), GFP_KERNEL);
+ if (!private) {
+ DRM_ERROR("failed to allocate private\n");
+ return -ENOMEM;
+ }
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ INIT_LIST_HEAD(&private->pageflip_event_list);
+ dev->dev_private = (void *)private;
+
+ /*
+ * create mapping to manage iommu table and set a pointer to iommu
+ * mapping structure to iommu_mapping of private data.
+ * also this iommu_mapping can be used to check if iommu is supported
+ * or not.
+ */
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ ret = drm_create_iommu_mapping(dev);
+ if (ret < 0) {
+ DRM_ERROR("failed to create iommu mapping.\n");
+ goto err_crtc;
+ }
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ drm_mode_config_init(dev);
+
+ /* init kms poll for handling hpd */
+ drm_kms_helper_poll_init(dev);
+
+ rockchip_drm_mode_config_init(dev);
+
+ /*
+ * ROCKCHIP4 is enough to have two CRTCs and each crtc would be used
+ * without dependency of hardware.
+ */
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ for (nr = 0; nr < MAX_CRTC; nr++) {
+ ret = rockchip_drm_crtc_create(dev, nr);
+ if (ret)
+ goto err_release_iommu_mapping;
+ }
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ for (nr = 0; nr < MAX_PLANE; nr++) {
+ struct drm_plane *plane;
+ unsigned int possible_crtcs = (1 << MAX_CRTC) - 1;
+
+ plane = rockchip_plane_init(dev, possible_crtcs, false);
+ if (!plane)
+ goto err_release_iommu_mapping;
+ }
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ ret = drm_vblank_init(dev, MAX_CRTC);
+ if (ret)
+ goto err_release_iommu_mapping;
+
+ /*
+ * probe sub drivers such as display controller and hdmi driver,
+ * that were registered at probe() of platform driver
+ * to the sub driver and create encoder and connector for them.
+ */
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ ret = rockchip_drm_device_register(dev);
+ if (ret)
+ goto err_vblank;
+
+ /* setup possible_clones. */
+ rockchip_drm_encoder_setup(dev);
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ /*
+ * create and configure fb helper and also rockchip specific
+ * fbdev object.
+ */
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ ret = rockchip_drm_fbdev_init(dev);
+ if (ret) {
+ DRM_ERROR("failed to initialize drm fbdev\n");
+ goto err_drm_device;
+ }
+
+ drm_vblank_offdelay = VBLANK_OFF_DELAY;
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ return 0;
+
+err_drm_device:
+ rockchip_drm_device_unregister(dev);
+err_vblank:
+ drm_vblank_cleanup(dev);
+err_release_iommu_mapping:
+ drm_release_iommu_mapping(dev);
+err_crtc:
+ drm_mode_config_cleanup(dev);
+ kfree(private);
+
+ return ret;
+}
+
+static int rockchip_drm_unload(struct drm_device *dev)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ rockchip_drm_fbdev_fini(dev);
+ rockchip_drm_device_unregister(dev);
+ drm_vblank_cleanup(dev);
+ drm_kms_helper_poll_fini(dev);
+ drm_mode_config_cleanup(dev);
+
+ drm_release_iommu_mapping(dev);
+ kfree(dev->dev_private);
+
+ dev->dev_private = NULL;
+
+ return 0;
+}
+
+static int rockchip_drm_open(struct drm_device *dev, struct drm_file *file)
+{
+ struct drm_rockchip_file_private *file_priv;
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
+ if (!file_priv)
+ return -ENOMEM;
+
+ file->driver_priv = file_priv;
+
+ return rockchip_drm_subdrv_open(dev, file);
+}
+
+static void rockchip_drm_preclose(struct drm_device *dev,
+ struct drm_file *file)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct drm_pending_vblank_event *e, *t;
+ unsigned long flags;
+
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ /* release events of current file */
+ spin_lock_irqsave(&dev->event_lock, flags);
+ list_for_each_entry_safe(e, t, &private->pageflip_event_list,
+ base.link) {
+ if (e->base.file_priv == file) {
+ list_del(&e->base.link);
+ e->base.destroy(&e->base);
+ }
+ }
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ rockchip_drm_subdrv_close(dev, file);
+}
+
+static void rockchip_drm_postclose(struct drm_device *dev, struct drm_file *file)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ if (!file->driver_priv)
+ return;
+
+ kfree(file->driver_priv);
+ file->driver_priv = NULL;
+}
+
+static void rockchip_drm_lastclose(struct drm_device *dev)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ rockchip_drm_fbdev_restore_mode(dev);
+}
+
+static const struct vm_operations_struct rockchip_drm_gem_vm_ops = {
+ .fault = rockchip_drm_gem_fault,
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+static struct drm_ioctl_desc rockchip_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_drm_gem_create_ioctl,
+ DRM_UNLOCKED | DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
+ rockchip_drm_gem_map_offset_ioctl, DRM_UNLOCKED |
+ DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MMAP,
+ rockchip_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET,
+ rockchip_drm_gem_get_ioctl, DRM_UNLOCKED),
+};
+
+static const struct file_operations rockchip_drm_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .mmap = rockchip_drm_gem_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .release = drm_release,
+};
+
+static struct drm_driver rockchip_drm_driver = {
+ .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET |
+ DRIVER_GEM | DRIVER_PRIME,
+ .load = rockchip_drm_load,
+ .unload = rockchip_drm_unload,
+ .open = rockchip_drm_open,
+ .preclose = rockchip_drm_preclose,
+ .lastclose = rockchip_drm_lastclose,
+ .postclose = rockchip_drm_postclose,
+ .get_vblank_counter = drm_vblank_count,
+ .enable_vblank = rockchip_drm_crtc_enable_vblank,
+ .disable_vblank = rockchip_drm_crtc_disable_vblank,
+ .gem_init_object = rockchip_drm_gem_init_object,
+ .gem_free_object = rockchip_drm_gem_free_object,
+ .gem_vm_ops = &rockchip_drm_gem_vm_ops,
+ .dumb_create = rockchip_drm_gem_dumb_create,
+ .dumb_map_offset = rockchip_drm_gem_dumb_map_offset,
+ .dumb_destroy = rockchip_drm_gem_dumb_destroy,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_export = rockchip_dmabuf_prime_export,
+ .gem_prime_import = rockchip_dmabuf_prime_import,
+ .ioctls = rockchip_ioctls,
+ .fops = &rockchip_drm_driver_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+};
+
+static int rockchip_drm_platform_probe(struct platform_device *pdev)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ rockchip_drm_driver.num_ioctls = DRM_ARRAY_SIZE(rockchip_ioctls);
+
+ return drm_platform_init(&rockchip_drm_driver, pdev);
+}
+
+static int rockchip_drm_platform_remove(struct platform_device *pdev)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+
+ drm_platform_exit(&rockchip_drm_driver, pdev);
+
+ return 0;
+}
+
+static struct platform_driver rockchip_drm_platform_driver = {
+ .probe = rockchip_drm_platform_probe,
+ .remove = rockchip_drm_platform_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rockchip-drm",
+ },
+};
+
+static int __init rockchip_drm_init(void)
+{
+ int ret;
+
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+#ifdef CONFIG_DRM_RK3288_FIMD
+ ret = platform_driver_register(&fimd_driver);
+ if (ret < 0)
+ goto out_fimd;
+#endif
+#ifdef CONFIG_DRM_RK3188_FIMD
+ ret = platform_driver_register(&fimd_driver);
+ if (ret < 0)
+ goto out_fimd;
+#endif
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ ret = platform_driver_register(&rockchip_drm_platform_driver);
+ if (ret < 0)
+ goto out_drm;
+
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ rockchip_drm_pdev = platform_device_register_simple("rockchip-drm", -1,
+ NULL, 0);
+ if (IS_ERR(rockchip_drm_pdev)) {
+ ret = PTR_ERR(rockchip_drm_pdev);
+ goto out;
+ }
+
+ printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
+ return 0;
+
+out:
+ platform_driver_unregister(&rockchip_drm_platform_driver);
+out_drm:
+#ifdef CONFIG_DRM_RK3188_FIMD
+ platform_driver_unregister(&fimd_driver);
+out_fimd:
+#endif
+#ifdef CONFIG_DRM_RK3288_FIMD
+ platform_driver_unregister(&fimd_driver);
+out_fimd:
+#endif
+ return ret;
+}
+
+static void __exit rockchip_drm_exit(void)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ platform_device_unregister(rockchip_drm_pdev);
+
+ platform_driver_unregister(&rockchip_drm_platform_driver);
+}
+
+module_init(rockchip_drm_init);
+module_exit(rockchip_drm_exit);
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_DRV_H_
+#define _ROCKCHIP_DRM_DRV_H_
+
+#include <linux/module.h>
+
+#define MAX_CRTC 3
+#define MAX_PLANE 5
+#define MAX_FB_BUFFER 4
+#define DEFAULT_ZPOS -1
+
+#define _wait_for(COND, MS) ({ \
+ unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \
+ int ret__ = 0; \
+ while (!(COND)) { \
+ if (time_after(jiffies, timeout__)) { \
+ ret__ = -ETIMEDOUT; \
+ break; \
+ } \
+ } \
+ ret__; \
+})
+
+#define wait_for(COND, MS) _wait_for(COND, MS)
+
+struct drm_device;
+struct rockchip_drm_overlay;
+struct drm_connector;
+
+extern unsigned int drm_vblank_offdelay;
+
+/* this enumerates display type. */
+enum rockchip_drm_output_type {
+ ROCKCHIP_DISPLAY_TYPE_NONE,
+ /* RGB or CPU Interface. */
+ ROCKCHIP_DISPLAY_TYPE_LCD,
+ /* HDMI Interface. */
+ ROCKCHIP_DISPLAY_TYPE_HDMI,
+ /* Virtual Display Interface. */
+ ROCKCHIP_DISPLAY_TYPE_VIDI,
+};
+
+/*
+ * Exynos drm overlay ops structure.
+ *
+ * @mode_set: copy drm overlay info to hw specific overlay info.
+ * @commit: apply hardware specific overlay data to registers.
+ * @enable: enable hardware specific overlay.
+ * @disable: disable hardware specific overlay.
+ */
+struct rockchip_drm_overlay_ops {
+ void (*mode_set)(struct device *subdrv_dev,
+ struct rockchip_drm_overlay *overlay);
+ void (*commit)(struct device *subdrv_dev, int zpos);
+ void (*enable)(struct device *subdrv_dev, int zpos);
+ void (*disable)(struct device *subdrv_dev, int zpos);
+};
+
+/*
+ * Exynos drm common overlay structure.
+ *
+ * @fb_x: offset x on a framebuffer to be displayed.
+ * - the unit is screen coordinates.
+ * @fb_y: offset y on a framebuffer to be displayed.
+ * - the unit is screen coordinates.
+ * @fb_width: width of a framebuffer.
+ * @fb_height: height of a framebuffer.
+ * @src_width: width of a partial image to be displayed from framebuffer.
+ * @src_height: height of a partial image to be displayed from framebuffer.
+ * @crtc_x: offset x on hardware screen.
+ * @crtc_y: offset y on hardware screen.
+ * @crtc_width: window width to be displayed (hardware screen).
+ * @crtc_height: window height to be displayed (hardware screen).
+ * @mode_width: width of screen mode.
+ * @mode_height: height of screen mode.
+ * @refresh: refresh rate.
+ * @scan_flag: interlace or progressive way.
+ * (it could be DRM_MODE_FLAG_*)
+ * @bpp: pixel size.(in bit)
+ * @pixel_format: fourcc pixel format of this overlay
+ * @dma_addr: array of bus(accessed by dma) address to the memory region
+ * allocated for a overlay.
+ * @zpos: order of overlay layer(z position).
+ * @default_win: a window to be enabled.
+ * @color_key: color key on or off.
+ * @index_color: if using color key feature then this value would be used
+ * as index color.
+ * @local_path: in case of lcd type, local path mode on or off.
+ * @transparency: transparency on or off.
+ * @activated: activated or not.
+ *
+ * this structure is common to rockchip SoC and its contents would be copied
+ * to hardware specific overlay info.
+ */
+struct rockchip_drm_overlay {
+ unsigned int fb_x;
+ unsigned int fb_y;
+ unsigned int fb_width;
+ unsigned int fb_height;
+ unsigned int src_width;
+ unsigned int src_height;
+ unsigned int crtc_x;
+ unsigned int crtc_y;
+ unsigned int crtc_width;
+ unsigned int crtc_height;
+ unsigned int mode_width;
+ unsigned int mode_height;
+ unsigned int refresh;
+ unsigned int scan_flag;
+ unsigned int bpp;
+ unsigned int pitch;
+ uint32_t pixel_format;
+ dma_addr_t dma_addr[MAX_FB_BUFFER];
+ int zpos;
+
+ bool default_win;
+ bool color_key;
+ unsigned int index_color;
+ bool local_path;
+ bool transparency;
+ bool activated;
+};
+
+/*
+ * Exynos DRM Display Structure.
+ * - this structure is common to analog tv, digital tv and lcd panel.
+ *
+ * @type: one of ROCKCHIP_DISPLAY_TYPE_LCD and HDMI.
+ * @is_connected: check for that display is connected or not.
+ * @get_edid: get edid modes from display driver.
+ * @get_panel: get panel object from display driver.
+ * @check_timing: check if timing is valid or not.
+ * @power_on: display device on or off.
+ */
+struct rockchip_drm_display_ops {
+ enum rockchip_drm_output_type type;
+ bool (*is_connected)(struct device *dev);
+ struct edid *(*get_edid)(struct device *dev,
+ struct drm_connector *connector);
+ void *(*get_panel)(struct device *dev);
+ int (*check_timing)(struct device *dev, void *timing);
+ int (*power_on)(struct device *dev, int mode);
+};
+
+/*
+ * Exynos drm manager ops
+ *
+ * @dpms: control device power.
+ * @apply: set timing, vblank and overlay data to registers.
+ * @mode_fixup: fix mode data comparing to hw specific display mode.
+ * @mode_set: convert drm_display_mode to hw specific display mode and
+ * would be called by encoder->mode_set().
+ * @get_max_resol: get maximum resolution to specific hardware.
+ * @commit: set current hw specific display mode to hw.
+ * @enable_vblank: specific driver callback for enabling vblank interrupt.
+ * @disable_vblank: specific driver callback for disabling vblank interrupt.
+ * @wait_for_vblank: wait for vblank interrupt to make sure that
+ * hardware overlay is updated.
+ */
+struct rockchip_drm_manager_ops {
+ void (*dpms)(struct device *subdrv_dev, int mode);
+ void (*apply)(struct device *subdrv_dev);
+ void (*mode_fixup)(struct device *subdrv_dev,
+ struct drm_connector *connector,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+ void (*mode_set)(struct device *subdrv_dev, void *mode);
+ void (*get_max_resol)(struct device *subdrv_dev, unsigned int *width,
+ unsigned int *height);
+ void (*commit)(struct device *subdrv_dev);
+ int (*enable_vblank)(struct device *subdrv_dev);
+ void (*disable_vblank)(struct device *subdrv_dev);
+ void (*wait_for_vblank)(struct device *subdrv_dev);
+};
+
+/*
+ * Exynos drm common manager structure.
+ *
+ * @dev: pointer to device object for subdrv device driver.
+ * sub drivers such as display controller or hdmi driver,
+ * have their own device object.
+ * @ops: pointer to callbacks for rockchip drm specific framebuffer.
+ * these callbacks should be set by specific drivers such fimd
+ * or hdmi driver and are used to control hardware global registers.
+ * @overlay_ops: pointer to callbacks for rockchip drm specific framebuffer.
+ * these callbacks should be set by specific drivers such fimd
+ * or hdmi driver and are used to control hardware overlay reigsters.
+ * @display: pointer to callbacks for rockchip drm specific framebuffer.
+ * these callbacks should be set by specific drivers such fimd
+ * or hdmi driver and are used to control display devices such as
+ * analog tv, digital tv and lcd panel and also get timing data for them.
+ */
+struct rockchip_drm_manager {
+ struct device *dev;
+ int pipe;
+ struct rockchip_drm_manager_ops *ops;
+ struct rockchip_drm_overlay_ops *overlay_ops;
+ struct rockchip_drm_display_ops *display_ops;
+};
+
+struct rockchip_drm_g2d_private {
+ struct device *dev;
+ struct list_head inuse_cmdlist;
+ struct list_head event_list;
+ struct list_head userptr_list;
+};
+
+struct rockchip_drm_ipp_private {
+ struct device *dev;
+ struct list_head event_list;
+};
+
+struct drm_rockchip_file_private {
+ struct rockchip_drm_g2d_private *g2d_priv;
+ struct rockchip_drm_ipp_private *ipp_priv;
+};
+
+/*
+ * Exynos drm private structure.
+ *
+ * @da_start: start address to device address space.
+ * with iommu, device address space starts from this address
+ * otherwise default one.
+ * @da_space_size: size of device address space.
+ * if 0 then default value is used for it.
+ * @da_space_order: order to device address space.
+ */
+struct rockchip_drm_private {
+ struct drm_fb_helper *fb_helper;
+
+ /* list head for new event to be added. */
+ struct list_head pageflip_event_list;
+
+ /*
+ * created crtc object would be contained at this array and
+ * this array is used to be aware of which crtc did it request vblank.
+ */
+ struct drm_crtc *crtc[MAX_CRTC];
+ struct drm_property *plane_zpos_property;
+ struct drm_property *crtc_mode_property;
+
+ unsigned long da_start;
+ unsigned long da_space_size;
+ unsigned long da_space_order;
+};
+
+/*
+ * Exynos drm sub driver structure.
+ *
+ * @list: sub driver has its own list object to register to rockchip drm driver.
+ * @dev: pointer to device object for subdrv device driver.
+ * @drm_dev: pointer to drm_device and this pointer would be set
+ * when sub driver calls rockchip_drm_subdrv_register().
+ * @manager: subdrv has its own manager to control a hardware appropriately
+ * and we can access a hardware drawing on this manager.
+ * @probe: this callback would be called by rockchip drm driver after
+ * subdrv is registered to it.
+ * @remove: this callback is used to release resources created
+ * by probe callback.
+ * @open: this would be called with drm device file open.
+ * @close: this would be called with drm device file close.
+ * @encoder: encoder object owned by this sub driver.
+ * @connector: connector object owned by this sub driver.
+ */
+struct rockchip_drm_subdrv {
+ struct list_head list;
+ struct device *dev;
+ struct drm_device *drm_dev;
+ struct rockchip_drm_manager *manager;
+
+ int (*probe)(struct drm_device *drm_dev, struct device *dev);
+ void (*remove)(struct drm_device *drm_dev, struct device *dev);
+ int (*open)(struct drm_device *drm_dev, struct device *dev,
+ struct drm_file *file);
+ void (*close)(struct drm_device *drm_dev, struct device *dev,
+ struct drm_file *file);
+
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+};
+
+/*
+ * this function calls a probe callback registered to sub driver list and
+ * create its own encoder and connector and then set drm_device object
+ * to global one.
+ */
+int rockchip_drm_device_register(struct drm_device *dev);
+/*
+ * this function calls a remove callback registered to sub driver list and
+ * destroy its own encoder and connetor.
+ */
+int rockchip_drm_device_unregister(struct drm_device *dev);
+
+/*
+ * this function would be called by sub drivers such as display controller
+ * or hdmi driver to register this sub driver object to rockchip drm driver
+ * and when a sub driver is registered to rockchip drm driver a probe callback
+ * of the sub driver is called and creates its own encoder and connector.
+ */
+int rockchip_drm_subdrv_register(struct rockchip_drm_subdrv *drm_subdrv);
+
+/* this function removes subdrv list from rockchip drm driver */
+int rockchip_drm_subdrv_unregister(struct rockchip_drm_subdrv *drm_subdrv);
+
+int rockchip_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
+void rockchip_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
+
+/*
+ * this function registers rockchip drm hdmi platform device. It ensures only one
+ * instance of the device is created.
+ */
+int rockchip_platform_device_hdmi_register(void);
+
+/*
+ * this function unregisters rockchip drm hdmi platform device if it exists.
+ */
+void rockchip_platform_device_hdmi_unregister(void);
+
+/*
+ * this function registers rockchip drm ipp platform device.
+ */
+int rockchip_platform_device_ipp_register(void);
+
+/*
+ * this function unregisters rockchip drm ipp platform device if it exists.
+ */
+void rockchip_platform_device_ipp_unregister(void);
+
+extern struct platform_driver fimd_driver;
+extern struct platform_driver hdmi_driver;
+extern struct platform_driver mixer_driver;
+extern struct platform_driver rockchip_drm_common_hdmi_driver;
+extern struct platform_driver vidi_driver;
+extern struct platform_driver g2d_driver;
+extern struct platform_driver fimc_driver;
+extern struct platform_driver rotator_driver;
+extern struct platform_driver gsc_driver;
+extern struct platform_driver ipp_driver;
+#endif
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_encoder.h"
+#include "rockchip_drm_connector.h"
+
+#define to_rockchip_encoder(x) container_of(x, struct rockchip_drm_encoder,\
+ drm_encoder)
+
+/*
+ * rockchip specific encoder structure.
+ *
+ * @drm_encoder: encoder object.
+ * @manager: specific encoder has its own manager to control a hardware
+ * appropriately and we can access a hardware drawing on this manager.
+ * @dpms: store the encoder dpms value.
+ * @updated: indicate whether overlay data updating is needed or not.
+ */
+struct rockchip_drm_encoder {
+ struct drm_crtc *old_crtc;
+ struct drm_encoder drm_encoder;
+ struct rockchip_drm_manager *manager;
+ int dpms;
+ bool updated;
+};
+
+static void rockchip_drm_connector_power(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_connector *connector;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (rockchip_drm_best_encoder(connector) == encoder) {
+ DRM_DEBUG_KMS("connector[%d] dpms[%d]\n",
+ connector->base.id, mode);
+
+ rockchip_drm_display_power(connector, mode);
+ }
+ }
+}
+
+static void rockchip_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct rockchip_drm_manager *manager = rockchip_drm_get_manager(encoder);
+ struct rockchip_drm_manager_ops *manager_ops = manager->ops;
+ struct rockchip_drm_encoder *rockchip_encoder = to_rockchip_encoder(encoder);
+
+ DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode);
+
+ if (rockchip_encoder->dpms == mode) {
+ DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
+ return;
+ }
+
+ mutex_lock(&dev->struct_mutex);
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ if (manager_ops && manager_ops->apply)
+ if (!rockchip_encoder->updated)
+ manager_ops->apply(manager->dev);
+
+ rockchip_drm_connector_power(encoder, mode);
+ rockchip_encoder->dpms = mode;
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ rockchip_drm_connector_power(encoder, mode);
+ rockchip_encoder->dpms = mode;
+ rockchip_encoder->updated = false;
+ break;
+ default:
+ DRM_ERROR("unspecified mode %d\n", mode);
+ break;
+ }
+
+ mutex_unlock(&dev->struct_mutex);
+}
+
+static bool
+rockchip_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_connector *connector;
+ struct rockchip_drm_manager *manager = rockchip_drm_get_manager(encoder);
+ struct rockchip_drm_manager_ops *manager_ops = manager->ops;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder == encoder)
+ if (manager_ops && manager_ops->mode_fixup)
+ manager_ops->mode_fixup(manager->dev, connector,
+ mode, adjusted_mode);
+ }
+
+ return true;
+}
+
+static void disable_plane_to_crtc(struct drm_device *dev,
+ struct drm_crtc *old_crtc,
+ struct drm_crtc *new_crtc)
+{
+ struct drm_plane *plane;
+
+ /*
+ * if old_crtc isn't same as encoder->crtc then it means that
+ * user changed crtc id to another one so the plane to old_crtc
+ * should be disabled and plane->crtc should be set to new_crtc
+ * (encoder->crtc)
+ */
+ list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+ if (plane->crtc == old_crtc) {
+ /*
+ * do not change below call order.
+ *
+ * plane->funcs->disable_plane call checks
+ * if encoder->crtc is same as plane->crtc and if same
+ * then overlay_ops->disable callback will be called
+ * to diasble current hw overlay so plane->crtc should
+ * have new_crtc because new_crtc was set to
+ * encoder->crtc in advance.
+ */
+ plane->crtc = new_crtc;
+ plane->funcs->disable_plane(plane);
+ }
+ }
+}
+
+static void rockchip_drm_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_connector *connector;
+ struct rockchip_drm_manager *manager;
+ struct rockchip_drm_manager_ops *manager_ops;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder == encoder) {
+ struct rockchip_drm_encoder *rockchip_encoder;
+
+ rockchip_encoder = to_rockchip_encoder(encoder);
+
+ if (rockchip_encoder->old_crtc != encoder->crtc &&
+ rockchip_encoder->old_crtc) {
+
+ /*
+ * disable a plane to old crtc and change
+ * crtc of the plane to new one.
+ */
+ disable_plane_to_crtc(dev,
+ rockchip_encoder->old_crtc,
+ encoder->crtc);
+ }
+
+ manager = rockchip_drm_get_manager(encoder);
+ manager_ops = manager->ops;
+
+ if (manager_ops && manager_ops->mode_set)
+ manager_ops->mode_set(manager->dev,
+ adjusted_mode);
+
+ rockchip_encoder->old_crtc = encoder->crtc;
+ }
+ }
+}
+
+static void rockchip_drm_encoder_prepare(struct drm_encoder *encoder)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* drm framework doesn't check NULL. */
+}
+
+static void rockchip_drm_encoder_commit(struct drm_encoder *encoder)
+{
+ struct rockchip_drm_encoder *rockchip_encoder = to_rockchip_encoder(encoder);
+ struct rockchip_drm_manager *manager = rockchip_encoder->manager;
+ struct rockchip_drm_manager_ops *manager_ops = manager->ops;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (manager_ops && manager_ops->commit)
+ manager_ops->commit(manager->dev);
+
+ /*
+ * this will avoid one issue that overlay data is updated to
+ * real hardware two times.
+ * And this variable will be used to check if the data was
+ * already updated or not by rockchip_drm_encoder_dpms function.
+ */
+ rockchip_encoder->updated = true;
+
+ /*
+ * In case of setcrtc, there is no way to update encoder's dpms
+ * so update it here.
+ */
+ rockchip_encoder->dpms = DRM_MODE_DPMS_ON;
+}
+
+void rockchip_drm_encoder_complete_scanout(struct drm_framebuffer *fb)
+{
+ struct rockchip_drm_encoder *rockchip_encoder;
+ struct rockchip_drm_manager_ops *ops;
+ struct drm_device *dev = fb->dev;
+ struct drm_encoder *encoder;
+
+ /*
+ * make sure that overlay data are updated to real hardware
+ * for all encoders.
+ */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ rockchip_encoder = to_rockchip_encoder(encoder);
+ ops = rockchip_encoder->manager->ops;
+
+ /*
+ * wait for vblank interrupt
+ * - this makes sure that overlay data are updated to
+ * real hardware.
+ */
+ if (ops->wait_for_vblank)
+ ops->wait_for_vblank(rockchip_encoder->manager->dev);
+ }
+}
+
+
+static void rockchip_drm_encoder_disable(struct drm_encoder *encoder)
+{
+ struct drm_plane *plane;
+ struct drm_device *dev = encoder->dev;
+
+ rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+ /* all planes connected to this encoder should be also disabled. */
+ list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+ if (plane->crtc == encoder->crtc)
+ plane->funcs->disable_plane(plane);
+ }
+}
+
+static struct drm_encoder_helper_funcs rockchip_encoder_helper_funcs = {
+ .dpms = rockchip_drm_encoder_dpms,
+ .mode_fixup = rockchip_drm_encoder_mode_fixup,
+ .mode_set = rockchip_drm_encoder_mode_set,
+ .prepare = rockchip_drm_encoder_prepare,
+ .commit = rockchip_drm_encoder_commit,
+ .disable = rockchip_drm_encoder_disable,
+};
+
+static void rockchip_drm_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct rockchip_drm_encoder *rockchip_encoder =
+ to_rockchip_encoder(encoder);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ rockchip_encoder->manager->pipe = -1;
+
+ drm_encoder_cleanup(encoder);
+ kfree(rockchip_encoder);
+}
+
+static struct drm_encoder_funcs rockchip_encoder_funcs = {
+ .destroy = rockchip_drm_encoder_destroy,
+};
+
+static unsigned int rockchip_drm_encoder_clones(struct drm_encoder *encoder)
+{
+ struct drm_encoder *clone;
+ struct drm_device *dev = encoder->dev;
+ struct rockchip_drm_encoder *rockchip_encoder = to_rockchip_encoder(encoder);
+ struct rockchip_drm_display_ops *display_ops =
+ rockchip_encoder->manager->display_ops;
+ unsigned int clone_mask = 0;
+ int cnt = 0;
+
+ list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
+ switch (display_ops->type) {
+ case ROCKCHIP_DISPLAY_TYPE_LCD:
+ case ROCKCHIP_DISPLAY_TYPE_HDMI:
+ case ROCKCHIP_DISPLAY_TYPE_VIDI:
+ clone_mask |= (1 << (cnt++));
+ break;
+ default:
+ continue;
+ }
+ }
+
+ return clone_mask;
+}
+
+void rockchip_drm_encoder_setup(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ encoder->possible_clones = rockchip_drm_encoder_clones(encoder);
+}
+
+struct drm_encoder *
+rockchip_drm_encoder_create(struct drm_device *dev,
+ struct rockchip_drm_manager *manager,
+ unsigned int possible_crtcs)
+{
+ struct drm_encoder *encoder;
+ struct rockchip_drm_encoder *rockchip_encoder;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!manager || !possible_crtcs)
+ return NULL;
+
+ if (!manager->dev)
+ return NULL;
+
+ rockchip_encoder = kzalloc(sizeof(*rockchip_encoder), GFP_KERNEL);
+ if (!rockchip_encoder) {
+ DRM_ERROR("failed to allocate encoder\n");
+ return NULL;
+ }
+
+ rockchip_encoder->dpms = DRM_MODE_DPMS_OFF;
+ rockchip_encoder->manager = manager;
+ encoder = &rockchip_encoder->drm_encoder;
+ encoder->possible_crtcs = possible_crtcs;
+
+ DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+ drm_encoder_init(dev, encoder, &rockchip_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS);
+
+ drm_encoder_helper_add(encoder, &rockchip_encoder_helper_funcs);
+
+ DRM_DEBUG_KMS("encoder has been created\n");
+
+ return encoder;
+}
+
+struct rockchip_drm_manager *rockchip_drm_get_manager(struct drm_encoder *encoder)
+{
+ return to_rockchip_encoder(encoder)->manager;
+}
+
+void rockchip_drm_fn_encoder(struct drm_crtc *crtc, void *data,
+ void (*fn)(struct drm_encoder *, void *))
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_encoder *encoder;
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct rockchip_drm_manager *manager;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ /*
+ * if crtc is detached from encoder, check pipe,
+ * otherwise check crtc attached to encoder
+ */
+ if (!encoder->crtc) {
+ manager = to_rockchip_encoder(encoder)->manager;
+ if (manager->pipe < 0 ||
+ private->crtc[manager->pipe] != crtc)
+ continue;
+ } else {
+ if (encoder->crtc != crtc)
+ continue;
+ }
+
+ fn(encoder, data);
+ }
+}
+
+void rockchip_drm_enable_vblank(struct drm_encoder *encoder, void *data)
+{
+ struct rockchip_drm_manager *manager =
+ to_rockchip_encoder(encoder)->manager;
+ struct rockchip_drm_manager_ops *manager_ops = manager->ops;
+ int crtc = *(int *)data;
+
+ if (manager->pipe != crtc)
+ return;
+
+ if (manager_ops->enable_vblank)
+ manager_ops->enable_vblank(manager->dev);
+}
+
+void rockchip_drm_disable_vblank(struct drm_encoder *encoder, void *data)
+{
+ struct rockchip_drm_manager *manager =
+ to_rockchip_encoder(encoder)->manager;
+ struct rockchip_drm_manager_ops *manager_ops = manager->ops;
+ int crtc = *(int *)data;
+
+ if (manager->pipe != crtc)
+ return;
+
+ if (manager_ops->disable_vblank)
+ manager_ops->disable_vblank(manager->dev);
+}
+
+void rockchip_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data)
+{
+ struct rockchip_drm_encoder *rockchip_encoder = to_rockchip_encoder(encoder);
+ struct rockchip_drm_manager *manager = rockchip_encoder->manager;
+ struct rockchip_drm_manager_ops *manager_ops = manager->ops;
+ int mode = *(int *)data;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (manager_ops && manager_ops->dpms)
+ manager_ops->dpms(manager->dev, mode);
+
+ /*
+ * if this condition is ok then it means that the crtc is already
+ * detached from encoder and last function for detaching is properly
+ * done, so clear pipe from manager to prevent repeated call.
+ */
+ if (mode > DRM_MODE_DPMS_ON) {
+ if (!encoder->crtc)
+ manager->pipe = -1;
+ }
+}
+
+void rockchip_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data)
+{
+ struct rockchip_drm_manager *manager =
+ to_rockchip_encoder(encoder)->manager;
+ int pipe = *(int *)data;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * when crtc is detached from encoder, this pipe is used
+ * to select manager operation
+ */
+ manager->pipe = pipe;
+}
+
+void rockchip_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data)
+{
+ struct rockchip_drm_manager *manager =
+ to_rockchip_encoder(encoder)->manager;
+ struct rockchip_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ struct rockchip_drm_overlay *overlay = data;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (overlay_ops && overlay_ops->mode_set)
+ overlay_ops->mode_set(manager->dev, overlay);
+}
+
+void rockchip_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data)
+{
+ struct rockchip_drm_manager *manager =
+ to_rockchip_encoder(encoder)->manager;
+ struct rockchip_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ int zpos = DEFAULT_ZPOS;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (data)
+ zpos = *(int *)data;
+
+ if (overlay_ops && overlay_ops->commit)
+ overlay_ops->commit(manager->dev, zpos);
+}
+
+void rockchip_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data)
+{
+ struct rockchip_drm_manager *manager =
+ to_rockchip_encoder(encoder)->manager;
+ struct rockchip_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ int zpos = DEFAULT_ZPOS;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (data)
+ zpos = *(int *)data;
+
+ if (overlay_ops && overlay_ops->enable)
+ overlay_ops->enable(manager->dev, zpos);
+}
+
+void rockchip_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data)
+{
+ struct rockchip_drm_manager *manager =
+ to_rockchip_encoder(encoder)->manager;
+ struct rockchip_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ int zpos = DEFAULT_ZPOS;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (data)
+ zpos = *(int *)data;
+
+ if (overlay_ops && overlay_ops->disable)
+ overlay_ops->disable(manager->dev, zpos);
+}
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_ENCODER_H_
+#define _ROCKCHIP_DRM_ENCODER_H_
+
+struct rockchip_drm_manager;
+
+void rockchip_drm_encoder_setup(struct drm_device *dev);
+struct drm_encoder *rockchip_drm_encoder_create(struct drm_device *dev,
+ struct rockchip_drm_manager *mgr,
+ unsigned int possible_crtcs);
+struct rockchip_drm_manager *
+rockchip_drm_get_manager(struct drm_encoder *encoder);
+void rockchip_drm_fn_encoder(struct drm_crtc *crtc, void *data,
+ void (*fn)(struct drm_encoder *, void *));
+void rockchip_drm_enable_vblank(struct drm_encoder *encoder, void *data);
+void rockchip_drm_disable_vblank(struct drm_encoder *encoder, void *data);
+void rockchip_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data);
+void rockchip_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data);
+void rockchip_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data);
+void rockchip_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data);
+void rockchip_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data);
+void rockchip_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data);
+void rockchip_drm_encoder_complete_scanout(struct drm_framebuffer *fb);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <uapi/drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_iommu.h"
+#include "rockchip_drm_encoder.h"
+
+#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
+
+/*
+ * rockchip specific framebuffer structure.
+ *
+ * @fb: drm framebuffer obejct.
+ * @buf_cnt: a buffer count to drm framebuffer.
+ * @rockchip_gem_obj: array of rockchip specific gem object containing a gem object.
+ */
+struct rockchip_drm_fb {
+ struct drm_framebuffer fb;
+ unsigned int buf_cnt;
+ struct rockchip_drm_gem_obj *rockchip_gem_obj[MAX_FB_BUFFER];
+};
+
+static int check_fb_gem_memory_type(struct drm_device *drm_dev,
+ struct rockchip_drm_gem_obj *rockchip_gem_obj)
+{
+ unsigned int flags;
+
+ /*
+ * if rockchip drm driver supports iommu then framebuffer can use
+ * all the buffer types.
+ */
+ if (is_drm_iommu_supported(drm_dev))
+ return 0;
+
+ flags = rockchip_gem_obj->flags;
+
+ /*
+ * without iommu support, not support physically non-continuous memory
+ * for framebuffer.
+ */
+ if (IS_NONCONTIG_BUFFER(flags)) {
+ DRM_ERROR("cannot use this gem memory type for fb.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+ struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+ unsigned int i;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* make sure that overlay data are updated before relesing fb. */
+ rockchip_drm_encoder_complete_scanout(fb);
+
+ drm_framebuffer_cleanup(fb);
+
+ for (i = 0; i < ARRAY_SIZE(rockchip_fb->rockchip_gem_obj); i++) {
+ struct drm_gem_object *obj;
+
+ if (rockchip_fb->rockchip_gem_obj[i] == NULL)
+ continue;
+
+ obj = &rockchip_fb->rockchip_gem_obj[i]->base;
+ drm_gem_object_unreference_unlocked(obj);
+ }
+
+ kfree(rockchip_fb);
+ rockchip_fb = NULL;
+}
+
+static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int *handle)
+{
+ struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* This fb should have only one gem object. */
+ if (WARN_ON(rockchip_fb->buf_cnt != 1))
+ return -EINVAL;
+
+ return drm_gem_handle_create(file_priv,
+ &rockchip_fb->rockchip_gem_obj[0]->base, handle);
+}
+
+static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file_priv, unsigned flags,
+ unsigned color, struct drm_clip_rect *clips,
+ unsigned num_clips)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO */
+
+ return 0;
+}
+
+static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
+ .destroy = rockchip_drm_fb_destroy,
+ .create_handle = rockchip_drm_fb_create_handle,
+ .dirty = rockchip_drm_fb_dirty,
+};
+
+void rockchip_drm_fb_set_buf_cnt(struct drm_framebuffer *fb,
+ unsigned int cnt)
+{
+ struct rockchip_drm_fb *rockchip_fb;
+
+ rockchip_fb = to_rockchip_fb(fb);
+
+ rockchip_fb->buf_cnt = cnt;
+}
+
+unsigned int rockchip_drm_fb_get_buf_cnt(struct drm_framebuffer *fb)
+{
+ struct rockchip_drm_fb *rockchip_fb;
+
+ rockchip_fb = to_rockchip_fb(fb);
+
+ return rockchip_fb->buf_cnt;
+}
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj)
+{
+ struct rockchip_drm_fb *rockchip_fb;
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ int ret;
+
+ rockchip_gem_obj = to_rockchip_gem_obj(obj);
+
+ ret = check_fb_gem_memory_type(dev, rockchip_gem_obj);
+ if (ret < 0) {
+ DRM_ERROR("cannot use this gem memory type for fb.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
+ if (!rockchip_fb) {
+ DRM_ERROR("failed to allocate rockchip drm framebuffer\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
+ rockchip_fb->rockchip_gem_obj[0] = rockchip_gem_obj;
+
+ ret = drm_framebuffer_init(dev, &rockchip_fb->fb, &rockchip_drm_fb_funcs);
+ if (ret) {
+ DRM_ERROR("failed to initialize framebuffer\n");
+ return ERR_PTR(ret);
+ }
+
+ return &rockchip_fb->fb;
+}
+
+static u32 rockchip_drm_format_num_buffers(struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ unsigned int cnt = 0;
+
+ if (mode_cmd->pixel_format != DRM_FORMAT_NV12)
+ return drm_format_num_planes(mode_cmd->pixel_format);
+
+ while (cnt != MAX_FB_BUFFER) {
+ if (!mode_cmd->handles[cnt])
+ break;
+ cnt++;
+ }
+
+ /*
+ * check if NV12 or NV12M.
+ *
+ * NV12
+ * handles[0] = base1, offsets[0] = 0
+ * handles[1] = base1, offsets[1] = Y_size
+ *
+ * NV12M
+ * handles[0] = base1, offsets[0] = 0
+ * handles[1] = base2, offsets[1] = 0
+ */
+ if (cnt == 2) {
+ /*
+ * in case of NV12 format, offsets[1] is not 0 and
+ * handles[0] is same as handles[1].
+ */
+ if (mode_cmd->offsets[1] &&
+ mode_cmd->handles[0] == mode_cmd->handles[1])
+ cnt = 1;
+ }
+
+ return cnt;
+}
+
+static struct drm_framebuffer *
+rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+ struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ struct drm_gem_object *obj;
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ struct rockchip_drm_fb *rockchip_fb;
+ int i, ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
+ if (!rockchip_fb) {
+ DRM_ERROR("failed to allocate rockchip drm framebuffer\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object\n");
+ ret = -ENOENT;
+ goto err_free;
+ }
+
+ drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
+ rockchip_fb->rockchip_gem_obj[0] = to_rockchip_gem_obj(obj);
+ rockchip_fb->buf_cnt = rockchip_drm_format_num_buffers(mode_cmd);
+
+ DRM_DEBUG_KMS("buf_cnt = %d\n", rockchip_fb->buf_cnt);
+
+ for (i = 1; i < rockchip_fb->buf_cnt; i++) {
+ obj = drm_gem_object_lookup(dev, file_priv,
+ mode_cmd->handles[i]);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object\n");
+ ret = -ENOENT;
+ rockchip_fb->buf_cnt = i;
+ goto err_unreference;
+ }
+
+ rockchip_gem_obj = to_rockchip_gem_obj(obj);
+ rockchip_fb->rockchip_gem_obj[i] = rockchip_gem_obj;
+
+ ret = check_fb_gem_memory_type(dev, rockchip_gem_obj);
+ if (ret < 0) {
+ DRM_ERROR("cannot use this gem memory type for fb.\n");
+ goto err_unreference;
+ }
+ }
+
+ ret = drm_framebuffer_init(dev, &rockchip_fb->fb, &rockchip_drm_fb_funcs);
+ if (ret) {
+ DRM_ERROR("failed to init framebuffer.\n");
+ goto err_unreference;
+ }
+
+ return &rockchip_fb->fb;
+
+err_unreference:
+ for (i = 0; i < rockchip_fb->buf_cnt; i++) {
+ struct drm_gem_object *obj;
+
+ obj = &rockchip_fb->rockchip_gem_obj[i]->base;
+ if (obj)
+ drm_gem_object_unreference_unlocked(obj);
+ }
+err_free:
+ kfree(rockchip_fb);
+ return ERR_PTR(ret);
+}
+
+struct rockchip_drm_gem_buf *rockchip_drm_fb_buffer(struct drm_framebuffer *fb,
+ int index)
+{
+ struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+ struct rockchip_drm_gem_buf *buffer;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (index >= MAX_FB_BUFFER)
+ return NULL;
+
+ buffer = rockchip_fb->rockchip_gem_obj[index]->buffer;
+ if (!buffer)
+ return NULL;
+
+ DRM_DEBUG_KMS("dma_addr = 0x%lx\n", (unsigned long)buffer->dma_addr);
+
+ return buffer;
+}
+
+static void rockchip_drm_output_poll_changed(struct drm_device *dev)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct drm_fb_helper *fb_helper = private->fb_helper;
+
+ if (fb_helper)
+ drm_fb_helper_hotplug_event(fb_helper);
+}
+
+static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
+ .fb_create = rockchip_user_fb_create,
+ .output_poll_changed = rockchip_drm_output_poll_changed,
+};
+
+void rockchip_drm_mode_config_init(struct drm_device *dev)
+{
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+
+ /*
+ * set max width and height as default value(4096x4096).
+ * this value would be used to check framebuffer size limitation
+ * at drm_mode_addfb().
+ */
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
+
+ dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
+}
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_FB_H_
+#define _ROCKCHIP_DRM_FB_H
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj);
+
+/* get memory information of a drm framebuffer */
+struct rockchip_drm_gem_buf *rockchip_drm_fb_buffer(struct drm_framebuffer *fb,
+ int index);
+
+void rockchip_drm_mode_config_init(struct drm_device *dev);
+
+/* set a buffer count to drm framebuffer. */
+void rockchip_drm_fb_set_buf_cnt(struct drm_framebuffer *fb,
+ unsigned int cnt);
+
+/* get a buffer count to drm framebuffer. */
+unsigned int rockchip_drm_fb_get_buf_cnt(struct drm_framebuffer *fb);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_iommu.h"
+
+#define MAX_CONNECTOR 4
+#define PREFERRED_BPP 32
+
+#define to_rockchip_fbdev(x) container_of(x, struct rockchip_drm_fbdev,\
+ drm_fb_helper)
+
+struct rockchip_drm_fbdev {
+ struct drm_fb_helper drm_fb_helper;
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+};
+
+static int rockchip_drm_fb_mmap(struct fb_info *info,
+ struct vm_area_struct *vma)
+{
+ struct drm_fb_helper *helper = info->par;
+ struct rockchip_drm_fbdev *rockchip_fbd = to_rockchip_fbdev(helper);
+ struct rockchip_drm_gem_obj *rockchip_gem_obj = rockchip_fbd->rockchip_gem_obj;
+ struct rockchip_drm_gem_buf *buffer = rockchip_gem_obj->buffer;
+ unsigned long vm_size;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __func__);
+
+ vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
+
+ vm_size = vma->vm_end - vma->vm_start;
+
+ if (vm_size > buffer->size)
+ return -EINVAL;
+
+ ret = dma_mmap_attrs(helper->dev->dev, vma, buffer->pages,
+ buffer->dma_addr, buffer->size, &buffer->dma_attrs);
+ if (ret < 0) {
+ DRM_ERROR("failed to mmap.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct fb_ops rockchip_drm_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_mmap = rockchip_drm_fb_mmap,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_check_var = drm_fb_helper_check_var,
+ .fb_set_par = drm_fb_helper_set_par,
+ .fb_blank = drm_fb_helper_blank,
+ .fb_pan_display = drm_fb_helper_pan_display,
+ .fb_setcmap = drm_fb_helper_setcmap,
+};
+
+static int rockchip_drm_fbdev_update(struct drm_fb_helper *helper,
+ struct drm_framebuffer *fb)
+{
+ struct fb_info *fbi = helper->fbdev;
+ struct drm_device *dev = helper->dev;
+ struct rockchip_drm_gem_buf *buffer;
+ unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
+ unsigned long offset;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
+ drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
+
+ /* RGB formats use only one buffer */
+ buffer = rockchip_drm_fb_buffer(fb, 0);
+ if (!buffer) {
+ DRM_LOG_KMS("buffer is null.\n");
+ return -EFAULT;
+ }
+
+ /* map pages with kernel virtual space. */
+ if (!buffer->kvaddr) {
+ if (is_drm_iommu_supported(dev)) {
+ unsigned int nr_pages = buffer->size >> PAGE_SHIFT;
+
+ buffer->kvaddr = vmap(buffer->pages, nr_pages, VM_MAP,
+ pgprot_writecombine(PAGE_KERNEL));
+ } else {
+ phys_addr_t dma_addr = buffer->dma_addr;
+ if (dma_addr)
+ buffer->kvaddr = phys_to_virt(dma_addr);
+ else
+ buffer->kvaddr = (void __iomem *)NULL;
+ }
+ if (!buffer->kvaddr) {
+ DRM_ERROR("failed to map pages to kernel space.\n");
+ return -EIO;
+ }
+ }
+
+ /* buffer count to framebuffer always is 1 at booting time. */
+ rockchip_drm_fb_set_buf_cnt(fb, 1);
+
+ offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
+ offset += fbi->var.yoffset * fb->pitches[0];
+
+ dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
+ fbi->screen_base = buffer->kvaddr + offset;
+ if (is_drm_iommu_supported(dev))
+ fbi->fix.smem_start = (unsigned long)
+ (page_to_phys(sg_page(buffer->sgt->sgl)) + offset);
+ else
+ fbi->fix.smem_start = (unsigned long)buffer->dma_addr;
+
+ fbi->screen_size = size;
+ fbi->fix.smem_len = size;
+
+ return 0;
+}
+
+static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct rockchip_drm_fbdev *rockchip_fbdev = to_rockchip_fbdev(helper);
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ struct drm_device *dev = helper->dev;
+ struct fb_info *fbi;
+ struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+ struct platform_device *pdev = dev->platformdev;
+ unsigned long size;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
+ sizes->surface_width, sizes->surface_height,
+ sizes->surface_bpp);
+
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+
+ mutex_lock(&dev->struct_mutex);
+
+ fbi = framebuffer_alloc(0, &pdev->dev);
+ if (!fbi) {
+ DRM_ERROR("failed to allocate fb info.\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ size = mode_cmd.pitches[0] * mode_cmd.height;
+
+ /* 0 means to allocate physically continuous memory */
+ rockchip_gem_obj = rockchip_drm_gem_create(dev, 0, size);
+ if (IS_ERR(rockchip_gem_obj)) {
+ ret = PTR_ERR(rockchip_gem_obj);
+ goto err_release_framebuffer;
+ }
+
+ rockchip_fbdev->rockchip_gem_obj = rockchip_gem_obj;
+
+ helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd,
+ &rockchip_gem_obj->base);
+ if (IS_ERR(helper->fb)) {
+ DRM_ERROR("failed to create drm framebuffer.\n");
+ ret = PTR_ERR(helper->fb);
+ goto err_destroy_gem;
+ }
+
+ helper->fbdev = fbi;
+
+ fbi->par = helper;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->fbops = &rockchip_drm_fb_ops;
+
+ ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+ if (ret) {
+ DRM_ERROR("failed to allocate cmap.\n");
+ goto err_destroy_framebuffer;
+ }
+
+ ret = rockchip_drm_fbdev_update(helper, helper->fb);
+ if (ret < 0)
+ goto err_dealloc_cmap;
+
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+
+err_dealloc_cmap:
+ fb_dealloc_cmap(&fbi->cmap);
+err_destroy_framebuffer:
+ drm_framebuffer_cleanup(helper->fb);
+err_destroy_gem:
+ rockchip_drm_gem_destroy(rockchip_gem_obj);
+err_release_framebuffer:
+ framebuffer_release(fbi);
+
+/*
+ * if failed, all resources allocated above would be released by
+ * drm_mode_config_cleanup() when drm_load() had been called prior
+ * to any specific driver such as fimd or hdmi driver.
+ */
+out:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
+ .fb_probe = rockchip_drm_fbdev_create,
+};
+
+int rockchip_drm_fbdev_init(struct drm_device *dev)
+{
+ struct rockchip_drm_fbdev *fbdev;
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct drm_fb_helper *helper;
+ unsigned int num_crtc;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
+ return 0;
+
+ fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
+ if (!fbdev) {
+ DRM_ERROR("failed to allocate drm fbdev.\n");
+ return -ENOMEM;
+ }
+
+ private->fb_helper = helper = &fbdev->drm_fb_helper;
+ helper->funcs = &rockchip_drm_fb_helper_funcs;
+
+ num_crtc = dev->mode_config.num_crtc;
+
+ ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR);
+ if (ret < 0) {
+ DRM_ERROR("failed to initialize drm fb helper.\n");
+ goto err_init;
+ }
+
+ ret = drm_fb_helper_single_add_all_connectors(helper);
+ if (ret < 0) {
+ DRM_ERROR("failed to register drm_fb_helper_connector.\n");
+ goto err_setup;
+
+ }
+
+ /* disable all the possible outputs/crtcs before entering KMS mode */
+ drm_helper_disable_unused_functions(dev);
+
+ ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
+ if (ret < 0) {
+ DRM_ERROR("failed to set up hw configuration.\n");
+ goto err_setup;
+ }
+
+ return 0;
+
+err_setup:
+ drm_fb_helper_fini(helper);
+
+err_init:
+ private->fb_helper = NULL;
+ kfree(fbdev);
+
+ return ret;
+}
+
+static void rockchip_drm_fbdev_destroy(struct drm_device *dev,
+ struct drm_fb_helper *fb_helper)
+{
+ struct rockchip_drm_fbdev *rockchip_fbd = to_rockchip_fbdev(fb_helper);
+ struct rockchip_drm_gem_obj *rockchip_gem_obj = rockchip_fbd->rockchip_gem_obj;
+ struct drm_framebuffer *fb;
+
+ if (is_drm_iommu_supported(dev) && rockchip_gem_obj->buffer->kvaddr)
+ vunmap(rockchip_gem_obj->buffer->kvaddr);
+
+ /* release drm framebuffer and real buffer */
+ if (fb_helper->fb && fb_helper->fb->funcs) {
+ fb = fb_helper->fb;
+ if (fb) {
+ drm_framebuffer_unregister_private(fb);
+ drm_framebuffer_remove(fb);
+ }
+ }
+
+ /* release linux framebuffer */
+ if (fb_helper->fbdev) {
+ struct fb_info *info;
+ int ret;
+
+ info = fb_helper->fbdev;
+ ret = unregister_framebuffer(info);
+ if (ret < 0)
+ DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
+
+ if (info->cmap.len)
+ fb_dealloc_cmap(&info->cmap);
+
+ framebuffer_release(info);
+ }
+
+ drm_fb_helper_fini(fb_helper);
+}
+
+void rockchip_drm_fbdev_fini(struct drm_device *dev)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct rockchip_drm_fbdev *fbdev;
+
+ if (!private || !private->fb_helper)
+ return;
+
+ fbdev = to_rockchip_fbdev(private->fb_helper);
+
+ if (fbdev->rockchip_gem_obj)
+ rockchip_drm_gem_destroy(fbdev->rockchip_gem_obj);
+
+ rockchip_drm_fbdev_destroy(dev, private->fb_helper);
+ kfree(fbdev);
+ private->fb_helper = NULL;
+}
+
+void rockchip_drm_fbdev_restore_mode(struct drm_device *dev)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+
+ if (!private || !private->fb_helper)
+ return;
+
+ drm_modeset_lock_all(dev);
+ drm_fb_helper_restore_fbdev_mode(private->fb_helper);
+ drm_modeset_unlock_all(dev);
+}
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_FBDEV_H_
+#define _ROCKCHIP_DRM_FBDEV_H_
+
+int rockchip_drm_fbdev_init(struct drm_device *dev);
+int rockchip_drm_fbdev_reinit(struct drm_device *dev);
+void rockchip_drm_fbdev_fini(struct drm_device *dev);
+void rockchip_drm_fbdev_restore_mode(struct drm_device *dev);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <drm/drmP.h>
+
+#include <linux/shmem_fs.h>
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_buf.h"
+
+static unsigned int convert_to_vm_err_msg(int msg)
+{
+ unsigned int out_msg;
+
+ switch (msg) {
+ case 0:
+ case -ERESTARTSYS:
+ case -EINTR:
+ out_msg = VM_FAULT_NOPAGE;
+ break;
+
+ case -ENOMEM:
+ out_msg = VM_FAULT_OOM;
+ break;
+
+ default:
+ out_msg = VM_FAULT_SIGBUS;
+ break;
+ }
+
+ return out_msg;
+}
+
+static int check_gem_flags(unsigned int flags)
+{
+ if (flags & ~(ROCKCHIP_BO_MASK)) {
+ DRM_ERROR("invalid flags.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void update_vm_cache_attr(struct rockchip_drm_gem_obj *obj,
+ struct vm_area_struct *vma)
+{
+ DRM_DEBUG_KMS("flags = 0x%x\n", obj->flags);
+
+ /* non-cachable as default. */
+ if (obj->flags & ROCKCHIP_BO_CACHABLE)
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+ else if (obj->flags & ROCKCHIP_BO_WC)
+ vma->vm_page_prot =
+ pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
+ else
+ vma->vm_page_prot =
+ pgprot_noncached(vm_get_page_prot(vma->vm_flags));
+}
+
+static unsigned long roundup_gem_size(unsigned long size, unsigned int flags)
+{
+ /* TODO */
+
+ return roundup(size, PAGE_SIZE);
+}
+
+static int rockchip_drm_gem_map_buf(struct drm_gem_object *obj,
+ struct vm_area_struct *vma,
+ unsigned long f_vaddr,
+ pgoff_t page_offset)
+{
+ struct rockchip_drm_gem_obj *rockchip_gem_obj = to_rockchip_gem_obj(obj);
+ struct rockchip_drm_gem_buf *buf = rockchip_gem_obj->buffer;
+ struct scatterlist *sgl;
+ unsigned long pfn;
+ int i;
+
+ if (!buf->sgt)
+ return -EINTR;
+
+ if (page_offset >= (buf->size >> PAGE_SHIFT)) {
+ DRM_ERROR("invalid page offset\n");
+ return -EINVAL;
+ }
+
+ sgl = buf->sgt->sgl;
+ for_each_sg(buf->sgt->sgl, sgl, buf->sgt->nents, i) {
+ if (page_offset < (sgl->length >> PAGE_SHIFT))
+ break;
+ page_offset -= (sgl->length >> PAGE_SHIFT);
+ }
+
+ pfn = __phys_to_pfn(sg_phys(sgl)) + page_offset;
+
+ return vm_insert_mixed(vma, f_vaddr, pfn);
+}
+
+static int rockchip_drm_gem_handle_create(struct drm_gem_object *obj,
+ struct drm_file *file_priv,
+ unsigned int *handle)
+{
+ int ret;
+
+ /*
+ * allocate a id of idr table where the obj is registered
+ * and handle has the id what user can see.
+ */
+ ret = drm_gem_handle_create(file_priv, obj, handle);
+ if (ret)
+ return ret;
+
+ DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle);
+
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_unreference_unlocked(obj);
+
+ return 0;
+}
+
+void rockchip_drm_gem_destroy(struct rockchip_drm_gem_obj *rockchip_gem_obj)
+{
+ struct drm_gem_object *obj;
+ struct rockchip_drm_gem_buf *buf;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ obj = &rockchip_gem_obj->base;
+ buf = rockchip_gem_obj->buffer;
+
+ DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count));
+
+ /*
+ * do not release memory region from exporter.
+ *
+ * the region will be released by exporter
+ * once dmabuf's refcount becomes 0.
+ */
+ if (obj->import_attach)
+ goto out;
+
+ rockchip_drm_free_buf(obj->dev, rockchip_gem_obj->flags, buf);
+
+out:
+ rockchip_drm_fini_buf(obj->dev, buf);
+ rockchip_gem_obj->buffer = NULL;
+
+ if (obj->map_list.map)
+ drm_gem_free_mmap_offset(obj);
+
+ /* release file pointer to gem object. */
+ drm_gem_object_release(obj);
+
+ kfree(rockchip_gem_obj);
+ rockchip_gem_obj = NULL;
+}
+
+unsigned long rockchip_drm_gem_get_size(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *file_priv)
+{
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ struct drm_gem_object *obj;
+
+ obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ return 0;
+ }
+
+ rockchip_gem_obj = to_rockchip_gem_obj(obj);
+
+ drm_gem_object_unreference_unlocked(obj);
+
+ return rockchip_gem_obj->buffer->size;
+}
+
+
+struct rockchip_drm_gem_obj *rockchip_drm_gem_init(struct drm_device *dev,
+ unsigned long size)
+{
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ struct drm_gem_object *obj;
+ int ret;
+
+ rockchip_gem_obj = kzalloc(sizeof(*rockchip_gem_obj), GFP_KERNEL);
+ if (!rockchip_gem_obj) {
+ DRM_ERROR("failed to allocate rockchip gem object\n");
+ return NULL;
+ }
+
+ rockchip_gem_obj->size = size;
+ obj = &rockchip_gem_obj->base;
+
+ ret = drm_gem_object_init(dev, obj, size);
+ if (ret < 0) {
+ DRM_ERROR("failed to initialize gem object\n");
+ kfree(rockchip_gem_obj);
+ return NULL;
+ }
+
+ DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
+
+ return rockchip_gem_obj;
+}
+
+struct rockchip_drm_gem_obj *rockchip_drm_gem_create(struct drm_device *dev,
+ unsigned int flags,
+ unsigned long size)
+{
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ struct rockchip_drm_gem_buf *buf;
+ int ret;
+
+ if (!size) {
+ DRM_ERROR("invalid size.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ size = roundup_gem_size(size, flags);
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ ret = check_gem_flags(flags);
+ if (ret)
+ return ERR_PTR(ret);
+
+ buf = rockchip_drm_init_buf(dev, size);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ rockchip_gem_obj = rockchip_drm_gem_init(dev, size);
+ if (!rockchip_gem_obj) {
+ ret = -ENOMEM;
+ goto err_fini_buf;
+ }
+
+ rockchip_gem_obj->buffer = buf;
+
+ /* set memory type and cache attribute from user side. */
+ rockchip_gem_obj->flags = flags;
+
+ ret = rockchip_drm_alloc_buf(dev, buf, flags);
+ if (ret < 0) {
+ drm_gem_object_release(&rockchip_gem_obj->base);
+ goto err_fini_buf;
+ }
+
+ return rockchip_gem_obj;
+
+err_fini_buf:
+ rockchip_drm_fini_buf(dev, buf);
+ return ERR_PTR(ret);
+}
+
+int rockchip_drm_gem_create_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_rockchip_gem_create *args = data;
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ rockchip_gem_obj = rockchip_drm_gem_create(dev, args->flags, args->size);
+ if (IS_ERR(rockchip_gem_obj))
+ return PTR_ERR(rockchip_gem_obj);
+
+ ret = rockchip_drm_gem_handle_create(&rockchip_gem_obj->base, file_priv,
+ &args->handle);
+ if (ret) {
+ rockchip_drm_gem_destroy(rockchip_gem_obj);
+ return ret;
+ }
+
+ return 0;
+}
+
+dma_addr_t *rockchip_drm_gem_get_dma_addr(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *filp)
+{
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ struct drm_gem_object *obj;
+
+ obj = drm_gem_object_lookup(dev, filp, gem_handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rockchip_gem_obj = to_rockchip_gem_obj(obj);
+
+ return &rockchip_gem_obj->buffer->dma_addr;
+}
+
+void rockchip_drm_gem_put_dma_addr(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *filp)
+{
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ struct drm_gem_object *obj;
+
+ obj = drm_gem_object_lookup(dev, filp, gem_handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ return;
+ }
+
+ rockchip_gem_obj = to_rockchip_gem_obj(obj);
+
+ drm_gem_object_unreference_unlocked(obj);
+
+ /*
+ * decrease obj->refcount one more time because we has already
+ * increased it at rockchip_drm_gem_get_dma_addr().
+ */
+ drm_gem_object_unreference_unlocked(obj);
+}
+
+int rockchip_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_rockchip_gem_map_off *args = data;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n",
+ args->handle, (unsigned long)args->offset);
+
+ if (!(dev->driver->driver_features & DRIVER_GEM)) {
+ DRM_ERROR("does not support GEM.\n");
+ return -ENODEV;
+ }
+
+ return rockchip_drm_gem_dumb_map_offset(file_priv, dev, args->handle,
+ &args->offset);
+}
+
+static struct drm_file *rockchip_drm_find_drm_file(struct drm_device *drm_dev,
+ struct file *filp)
+{
+ struct drm_file *file_priv;
+
+ /* find current process's drm_file from filelist. */
+ list_for_each_entry(file_priv, &drm_dev->filelist, lhead)
+ if (file_priv->filp == filp)
+ return file_priv;
+
+ WARN_ON(1);
+
+ return ERR_PTR(-EFAULT);
+}
+
+static int rockchip_drm_gem_mmap_buffer(struct file *filp,
+ struct vm_area_struct *vma)
+{
+ struct drm_gem_object *obj = filp->private_data;
+ struct rockchip_drm_gem_obj *rockchip_gem_obj = to_rockchip_gem_obj(obj);
+ struct drm_device *drm_dev = obj->dev;
+ struct rockchip_drm_gem_buf *buffer;
+ struct drm_file *file_priv;
+ unsigned long vm_size;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
+ vma->vm_private_data = obj;
+ vma->vm_ops = drm_dev->driver->gem_vm_ops;
+
+ /* restore it to driver's fops. */
+ filp->f_op = fops_get(drm_dev->driver->fops);
+
+ file_priv = rockchip_drm_find_drm_file(drm_dev, filp);
+ if (IS_ERR(file_priv))
+ return PTR_ERR(file_priv);
+
+ /* restore it to drm_file. */
+ filp->private_data = file_priv;
+
+ update_vm_cache_attr(rockchip_gem_obj, vma);
+
+ vm_size = vma->vm_end - vma->vm_start;
+
+ /*
+ * a buffer contains information to physically continuous memory
+ * allocated by user request or at framebuffer creation.
+ */
+ buffer = rockchip_gem_obj->buffer;
+
+ /* check if user-requested size is valid. */
+ if (vm_size > buffer->size)
+ return -EINVAL;
+
+ ret = dma_mmap_attrs(drm_dev->dev, vma, buffer->pages,
+ buffer->dma_addr, buffer->size,
+ &buffer->dma_attrs);
+ if (ret < 0) {
+ DRM_ERROR("failed to mmap.\n");
+ return ret;
+ }
+
+ /*
+ * take a reference to this mapping of the object. And this reference
+ * is unreferenced by the corresponding vm_close call.
+ */
+ drm_gem_object_reference(obj);
+
+ drm_vm_open_locked(drm_dev, vma);
+
+ return 0;
+}
+
+static const struct file_operations rockchip_drm_gem_fops = {
+ .mmap = rockchip_drm_gem_mmap_buffer,
+};
+
+int rockchip_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_rockchip_gem_mmap *args = data;
+ struct drm_gem_object *obj;
+ unsigned int addr;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!(dev->driver->driver_features & DRIVER_GEM)) {
+ DRM_ERROR("does not support GEM.\n");
+ return -ENODEV;
+ }
+
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ return -EINVAL;
+ }
+
+ /*
+ * We have to use gem object and its fops for specific mmaper,
+ * but vm_mmap() can deliver only filp. So we have to change
+ * filp->f_op and filp->private_data temporarily, then restore
+ * again. So it is important to keep lock until restoration the
+ * settings to prevent others from misuse of filp->f_op or
+ * filp->private_data.
+ */
+ mutex_lock(&dev->struct_mutex);
+
+ /*
+ * Set specific mmper's fops. And it will be restored by
+ * rockchip_drm_gem_mmap_buffer to dev->driver->fops.
+ * This is used to call specific mapper temporarily.
+ */
+ file_priv->filp->f_op = &rockchip_drm_gem_fops;
+
+ /*
+ * Set gem object to private_data so that specific mmaper
+ * can get the gem object. And it will be restored by
+ * rockchip_drm_gem_mmap_buffer to drm_file.
+ */
+ file_priv->filp->private_data = obj;
+
+ addr = vm_mmap(file_priv->filp, 0, args->size,
+ PROT_READ | PROT_WRITE, MAP_SHARED, 0);
+
+ drm_gem_object_unreference(obj);
+
+ if (IS_ERR((void *)addr)) {
+ /* check filp->f_op, filp->private_data are restored */
+ if (file_priv->filp->f_op == &rockchip_drm_gem_fops) {
+ file_priv->filp->f_op = fops_get(dev->driver->fops);
+ file_priv->filp->private_data = file_priv;
+ }
+ mutex_unlock(&dev->struct_mutex);
+ return PTR_ERR((void *)addr);
+ }
+
+ mutex_unlock(&dev->struct_mutex);
+
+ args->mapped = addr;
+
+ DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
+
+ return 0;
+}
+
+int rockchip_drm_gem_get_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ struct drm_rockchip_gem_info *args = data;
+ struct drm_gem_object *obj;
+
+ mutex_lock(&dev->struct_mutex);
+
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ mutex_unlock(&dev->struct_mutex);
+ return -EINVAL;
+ }
+
+ rockchip_gem_obj = to_rockchip_gem_obj(obj);
+
+ args->flags = rockchip_gem_obj->flags;
+ args->size = rockchip_gem_obj->size;
+
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
+struct vm_area_struct *rockchip_gem_get_vma(struct vm_area_struct *vma)
+{
+ struct vm_area_struct *vma_copy;
+
+ vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
+ if (!vma_copy)
+ return NULL;
+
+ if (vma->vm_ops && vma->vm_ops->open)
+ vma->vm_ops->open(vma);
+
+ if (vma->vm_file)
+ get_file(vma->vm_file);
+
+ memcpy(vma_copy, vma, sizeof(*vma));
+
+ vma_copy->vm_mm = NULL;
+ vma_copy->vm_next = NULL;
+ vma_copy->vm_prev = NULL;
+
+ return vma_copy;
+}
+
+void rockchip_gem_put_vma(struct vm_area_struct *vma)
+{
+ if (!vma)
+ return;
+
+ if (vma->vm_ops && vma->vm_ops->close)
+ vma->vm_ops->close(vma);
+
+ if (vma->vm_file)
+ fput(vma->vm_file);
+
+ kfree(vma);
+}
+
+int rockchip_gem_get_pages_from_userptr(unsigned long start,
+ unsigned int npages,
+ struct page **pages,
+ struct vm_area_struct *vma)
+{
+ int get_npages;
+
+ /* the memory region mmaped with VM_PFNMAP. */
+ if (vma_is_io(vma)) {
+ unsigned int i;
+
+ for (i = 0; i < npages; ++i, start += PAGE_SIZE) {
+ unsigned long pfn;
+ int ret = follow_pfn(vma, start, &pfn);
+ if (ret)
+ return ret;
+
+ pages[i] = pfn_to_page(pfn);
+ }
+
+ if (i != npages) {
+ DRM_ERROR("failed to get user_pages.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ get_npages = get_user_pages(current, current->mm, start,
+ npages, 1, 1, pages, NULL);
+ get_npages = max(get_npages, 0);
+ if (get_npages != npages) {
+ DRM_ERROR("failed to get user_pages.\n");
+ while (get_npages)
+ put_page(pages[--get_npages]);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+void rockchip_gem_put_pages_to_userptr(struct page **pages,
+ unsigned int npages,
+ struct vm_area_struct *vma)
+{
+ if (!vma_is_io(vma)) {
+ unsigned int i;
+
+ for (i = 0; i < npages; i++) {
+ set_page_dirty_lock(pages[i]);
+
+ /*
+ * undo the reference we took when populating
+ * the table.
+ */
+ put_page(pages[i]);
+ }
+ }
+}
+
+int rockchip_gem_map_sgt_with_dma(struct drm_device *drm_dev,
+ struct sg_table *sgt,
+ enum dma_data_direction dir)
+{
+ int nents;
+
+ mutex_lock(&drm_dev->struct_mutex);
+
+ nents = dma_map_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir);
+ if (!nents) {
+ DRM_ERROR("failed to map sgl with dma.\n");
+ mutex_unlock(&drm_dev->struct_mutex);
+ return nents;
+ }
+
+ mutex_unlock(&drm_dev->struct_mutex);
+ return 0;
+}
+
+void rockchip_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,
+ struct sg_table *sgt,
+ enum dma_data_direction dir)
+{
+ dma_unmap_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir);
+}
+
+int rockchip_drm_gem_init_object(struct drm_gem_object *obj)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ return 0;
+}
+
+void rockchip_drm_gem_free_object(struct drm_gem_object *obj)
+{
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ struct rockchip_drm_gem_buf *buf;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ rockchip_gem_obj = to_rockchip_gem_obj(obj);
+ buf = rockchip_gem_obj->buffer;
+
+ if (obj->import_attach)
+ drm_prime_gem_destroy(obj, buf->sgt);
+
+ rockchip_drm_gem_destroy(to_rockchip_gem_obj(obj));
+}
+
+int rockchip_drm_gem_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * alocate memory to be used for framebuffer.
+ * - this callback would be called by user application
+ * with DRM_IOCTL_MODE_CREATE_DUMB command.
+ */
+
+ args->pitch = args->width * ((args->bpp + 7) / 8);
+ args->size = args->pitch * args->height;
+
+ rockchip_gem_obj = rockchip_drm_gem_create(dev, ROCKCHIP_BO_CONTIG |
+ ROCKCHIP_BO_WC, args->size);
+ if (IS_ERR(rockchip_gem_obj))
+ return PTR_ERR(rockchip_gem_obj);
+
+ ret = rockchip_drm_gem_handle_create(&rockchip_gem_obj->base, file_priv,
+ &args->handle);
+ if (ret) {
+ rockchip_drm_gem_destroy(rockchip_gem_obj);
+ return ret;
+ }
+
+ return 0;
+}
+
+int rockchip_drm_gem_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *dev, uint32_t handle,
+ uint64_t *offset)
+{
+ struct drm_gem_object *obj;
+ int ret = 0;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ mutex_lock(&dev->struct_mutex);
+
+ /*
+ * get offset of memory allocated for drm framebuffer.
+ * - this callback would be called by user application
+ * with DRM_IOCTL_MODE_MAP_DUMB command.
+ */
+
+ obj = drm_gem_object_lookup(dev, file_priv, handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (!obj->map_list.map) {
+ ret = drm_gem_create_mmap_offset(obj);
+ if (ret)
+ goto out;
+ }
+
+ *offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
+ DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
+
+out:
+ drm_gem_object_unreference(obj);
+unlock:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+int rockchip_drm_gem_dumb_destroy(struct drm_file *file_priv,
+ struct drm_device *dev,
+ unsigned int handle)
+{
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * obj->refcount and obj->handle_count are decreased and
+ * if both them are 0 then rockchip_drm_gem_free_object()
+ * would be called by callback to release resources.
+ */
+ ret = drm_gem_handle_delete(file_priv, handle);
+ if (ret < 0) {
+ DRM_ERROR("failed to delete drm_gem_handle.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int rockchip_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct drm_gem_object *obj = vma->vm_private_data;
+ struct drm_device *dev = obj->dev;
+ unsigned long f_vaddr;
+ pgoff_t page_offset;
+ int ret;
+
+ page_offset = ((unsigned long)vmf->virtual_address -
+ vma->vm_start) >> PAGE_SHIFT;
+ f_vaddr = (unsigned long)vmf->virtual_address;
+
+ mutex_lock(&dev->struct_mutex);
+
+ ret = rockchip_drm_gem_map_buf(obj, vma, f_vaddr, page_offset);
+ if (ret < 0)
+ DRM_ERROR("failed to map a buffer with user.\n");
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return convert_to_vm_err_msg(ret);
+}
+
+int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct rockchip_drm_gem_obj *rockchip_gem_obj;
+ struct drm_gem_object *obj;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* set vm_area_struct. */
+ ret = drm_gem_mmap(filp, vma);
+ if (ret < 0) {
+ DRM_ERROR("failed to mmap.\n");
+ return ret;
+ }
+
+ obj = vma->vm_private_data;
+ rockchip_gem_obj = to_rockchip_gem_obj(obj);
+
+ ret = check_gem_flags(rockchip_gem_obj->flags);
+ if (ret) {
+ drm_gem_vm_close(vma);
+ drm_gem_free_mmap_offset(obj);
+ return ret;
+ }
+
+ vma->vm_flags &= ~VM_PFNMAP;
+ vma->vm_flags |= VM_MIXEDMAP;
+
+ update_vm_cache_attr(rockchip_gem_obj, vma);
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_GEM_H_
+#define _ROCKCHIP_DRM_GEM_H_
+
+#define to_rockchip_gem_obj(x) container_of(x,\
+ struct rockchip_drm_gem_obj, base)
+
+#define IS_NONCONTIG_BUFFER(f) (f & ROCKCHIP_BO_NONCONTIG)
+
+/*
+ * rockchip drm gem buffer structure.
+ *
+ * @kvaddr: kernel virtual address to allocated memory region.
+ * *userptr: user space address.
+ * @dma_addr: bus address(accessed by dma) to allocated memory region.
+ * - this address could be physical address without IOMMU and
+ * device address with IOMMU.
+ * @write: whether pages will be written to by the caller.
+ * @pages: Array of backing pages.
+ * @sgt: sg table to transfer page data.
+ * @size: size of allocated memory region.
+ * @pfnmap: indicate whether memory region from userptr is mmaped with
+ * VM_PFNMAP or not.
+ */
+struct rockchip_drm_gem_buf {
+ void __iomem *kvaddr;
+ unsigned long userptr;
+ dma_addr_t dma_addr;
+ struct dma_attrs dma_attrs;
+ unsigned int write;
+ struct page **pages;
+ struct sg_table *sgt;
+ unsigned long size;
+ bool pfnmap;
+};
+
+/*
+ * rockchip drm buffer structure.
+ *
+ * @base: a gem object.
+ * - a new handle to this gem object would be created
+ * by drm_gem_handle_create().
+ * @buffer: a pointer to rockchip_drm_gem_buffer object.
+ * - contain the information to memory region allocated
+ * by user request or at framebuffer creation.
+ * continuous memory region allocated by user request
+ * or at framebuffer creation.
+ * @size: size requested from user, in bytes and this size is aligned
+ * in page unit.
+ * @vma: a pointer to vm_area.
+ * @flags: indicate memory type to allocated buffer and cache attruibute.
+ *
+ * P.S. this object would be transfered to user as kms_bo.handle so
+ * user can access the buffer through kms_bo.handle.
+ */
+struct rockchip_drm_gem_obj {
+ struct drm_gem_object base;
+ struct rockchip_drm_gem_buf *buffer;
+ unsigned long size;
+ struct vm_area_struct *vma;
+ unsigned int flags;
+};
+
+struct page **rockchip_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask);
+
+/* destroy a buffer with gem object */
+void rockchip_drm_gem_destroy(struct rockchip_drm_gem_obj *rockchip_gem_obj);
+
+/* create a private gem object and initialize it. */
+struct rockchip_drm_gem_obj *rockchip_drm_gem_init(struct drm_device *dev,
+ unsigned long size);
+
+/* create a new buffer with gem object */
+struct rockchip_drm_gem_obj *rockchip_drm_gem_create(struct drm_device *dev,
+ unsigned int flags,
+ unsigned long size);
+
+/*
+ * request gem object creation and buffer allocation as the size
+ * that it is calculated with framebuffer information such as width,
+ * height and bpp.
+ */
+int rockchip_drm_gem_create_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+/*
+ * get dma address from gem handle and this function could be used for
+ * other drivers such as 2d/3d acceleration drivers.
+ * with this function call, gem object reference count would be increased.
+ */
+dma_addr_t *rockchip_drm_gem_get_dma_addr(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *filp);
+
+/*
+ * put dma address from gem handle and this function could be used for
+ * other drivers such as 2d/3d acceleration drivers.
+ * with this function call, gem object reference count would be decreased.
+ */
+void rockchip_drm_gem_put_dma_addr(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *filp);
+
+/* get buffer offset to map to user space. */
+int rockchip_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+/*
+ * mmap the physically continuous memory that a gem object contains
+ * to user space.
+ */
+int rockchip_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+/* map user space allocated by malloc to pages. */
+int rockchip_drm_gem_userptr_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+/* get buffer information to memory region allocated by gem. */
+int rockchip_drm_gem_get_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+/* get buffer size to gem handle. */
+unsigned long rockchip_drm_gem_get_size(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *file_priv);
+
+/* initialize gem object. */
+int rockchip_drm_gem_init_object(struct drm_gem_object *obj);
+
+/* free gem object. */
+void rockchip_drm_gem_free_object(struct drm_gem_object *gem_obj);
+
+/* create memory region for drm framebuffer. */
+int rockchip_drm_gem_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+
+/* map memory region for drm framebuffer to user space. */
+int rockchip_drm_gem_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *dev, uint32_t handle,
+ uint64_t *offset);
+
+/*
+ * destroy memory region allocated.
+ * - a gem handle and physical memory region pointed by a gem object
+ * would be released by drm_gem_handle_delete().
+ */
+int rockchip_drm_gem_dumb_destroy(struct drm_file *file_priv,
+ struct drm_device *dev,
+ unsigned int handle);
+
+/* page fault handler and mmap fault address(virtual) to physical memory. */
+int rockchip_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+
+/* set vm_flags and we can change the vm attribute to other one at here. */
+int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
+
+static inline int vma_is_io(struct vm_area_struct *vma)
+{
+ return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
+}
+
+/* get a copy of a virtual memory region. */
+struct vm_area_struct *rockchip_gem_get_vma(struct vm_area_struct *vma);
+
+/* release a userspace virtual memory area. */
+void rockchip_gem_put_vma(struct vm_area_struct *vma);
+
+/* get pages from user space. */
+int rockchip_gem_get_pages_from_userptr(unsigned long start,
+ unsigned int npages,
+ struct page **pages,
+ struct vm_area_struct *vma);
+
+/* drop the reference to pages. */
+void rockchip_gem_put_pages_to_userptr(struct page **pages,
+ unsigned int npages,
+ struct vm_area_struct *vma);
+
+/* map sgt with dma region. */
+int rockchip_gem_map_sgt_with_dma(struct drm_device *drm_dev,
+ struct sg_table *sgt,
+ enum dma_data_direction dir);
+
+/* unmap sgt from dma region. */
+void rockchip_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,
+ struct sg_table *sgt,
+ enum dma_data_direction dir);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_hdmi.h"
+
+#define to_context(dev) platform_get_drvdata(to_platform_device(dev))
+#define to_subdrv(dev) to_context(dev)
+#define get_ctx_from_subdrv(subdrv) container_of(subdrv,\
+ struct drm_hdmi_context, subdrv);
+
+/* platform device pointer for common drm hdmi device. */
+static struct platform_device *rockchip_drm_hdmi_pdev;
+
+/* Common hdmi subdrv needs to access the hdmi and mixer though context.
+* These should be initialied by the repective drivers */
+static struct rockchip_drm_hdmi_context *hdmi_ctx;
+static struct rockchip_drm_hdmi_context *mixer_ctx;
+
+/* these callback points shoud be set by specific drivers. */
+static struct rockchip_hdmi_ops *hdmi_ops;
+static struct rockchip_mixer_ops *mixer_ops;
+
+struct drm_hdmi_context {
+ struct rockchip_drm_subdrv subdrv;
+ struct rockchip_drm_hdmi_context *hdmi_ctx;
+ struct rockchip_drm_hdmi_context *mixer_ctx;
+
+ bool enabled[MIXER_WIN_NR];
+};
+
+int rockchip_platform_device_hdmi_register(void)
+{
+ struct platform_device *pdev;
+
+ if (rockchip_drm_hdmi_pdev)
+ return -EEXIST;
+
+ pdev = platform_device_register_simple(
+ "rockchip-drm-hdmi", -1, NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ rockchip_drm_hdmi_pdev = pdev;
+
+ return 0;
+}
+
+void rockchip_platform_device_hdmi_unregister(void)
+{
+ if (rockchip_drm_hdmi_pdev) {
+ platform_device_unregister(rockchip_drm_hdmi_pdev);
+ rockchip_drm_hdmi_pdev = NULL;
+ }
+}
+
+void rockchip_hdmi_drv_attach(struct rockchip_drm_hdmi_context *ctx)
+{
+ if (ctx)
+ hdmi_ctx = ctx;
+}
+
+void rockchip_mixer_drv_attach(struct rockchip_drm_hdmi_context *ctx)
+{
+ if (ctx)
+ mixer_ctx = ctx;
+}
+
+void rockchip_hdmi_ops_register(struct rockchip_hdmi_ops *ops)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ops)
+ hdmi_ops = ops;
+}
+
+void rockchip_mixer_ops_register(struct rockchip_mixer_ops *ops)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ops)
+ mixer_ops = ops;
+}
+
+static bool drm_hdmi_is_connected(struct device *dev)
+{
+ struct drm_hdmi_context *ctx = to_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (hdmi_ops && hdmi_ops->is_connected)
+ return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx);
+
+ return false;
+}
+
+static struct edid *drm_hdmi_get_edid(struct device *dev,
+ struct drm_connector *connector)
+{
+ struct drm_hdmi_context *ctx = to_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (hdmi_ops && hdmi_ops->get_edid)
+ return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector);
+
+ return NULL;
+}
+
+static int drm_hdmi_check_timing(struct device *dev, void *timing)
+{
+ struct drm_hdmi_context *ctx = to_context(dev);
+ int ret = 0;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * Both, mixer and hdmi should be able to handle the requested mode.
+ * If any of the two fails, return mode as BAD.
+ */
+
+ if (mixer_ops && mixer_ops->check_timing)
+ ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, timing);
+
+ if (ret)
+ return ret;
+
+ if (hdmi_ops && hdmi_ops->check_timing)
+ return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
+
+ return 0;
+}
+
+static int drm_hdmi_power_on(struct device *dev, int mode)
+{
+ struct drm_hdmi_context *ctx = to_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (hdmi_ops && hdmi_ops->power_on)
+ return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode);
+
+ return 0;
+}
+
+static struct rockchip_drm_display_ops drm_hdmi_display_ops = {
+ .type = ROCKCHIP_DISPLAY_TYPE_HDMI,
+ .is_connected = drm_hdmi_is_connected,
+ .get_edid = drm_hdmi_get_edid,
+ .check_timing = drm_hdmi_check_timing,
+ .power_on = drm_hdmi_power_on,
+};
+
+static int drm_hdmi_enable_vblank(struct device *subdrv_dev)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+ struct rockchip_drm_subdrv *subdrv = &ctx->subdrv;
+ struct rockchip_drm_manager *manager = subdrv->manager;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (mixer_ops && mixer_ops->enable_vblank)
+ return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx,
+ manager->pipe);
+
+ return 0;
+}
+
+static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (mixer_ops && mixer_ops->disable_vblank)
+ return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx);
+}
+
+static void drm_hdmi_wait_for_vblank(struct device *subdrv_dev)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (mixer_ops && mixer_ops->wait_for_vblank)
+ mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx);
+}
+
+static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
+ struct drm_connector *connector,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_display_mode *m;
+ int mode_ok;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ drm_mode_set_crtcinfo(adjusted_mode, 0);
+
+ mode_ok = drm_hdmi_check_timing(subdrv_dev, adjusted_mode);
+
+ /* just return if user desired mode exists. */
+ if (mode_ok == 0)
+ return;
+
+ /*
+ * otherwise, find the most suitable mode among modes and change it
+ * to adjusted_mode.
+ */
+ list_for_each_entry(m, &connector->modes, head) {
+ mode_ok = drm_hdmi_check_timing(subdrv_dev, m);
+
+ if (mode_ok == 0) {
+ struct drm_mode_object base;
+ struct list_head head;
+
+ DRM_INFO("desired mode doesn't exist so\n");
+ DRM_INFO("use the most suitable mode among modes.\n");
+
+ DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
+ m->hdisplay, m->vdisplay, m->vrefresh);
+
+ /* preserve display mode header while copying. */
+ head = adjusted_mode->head;
+ base = adjusted_mode->base;
+ memcpy(adjusted_mode, m, sizeof(*m));
+ adjusted_mode->head = head;
+ adjusted_mode->base = base;
+ break;
+ }
+ }
+}
+
+static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (hdmi_ops && hdmi_ops->mode_set)
+ hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
+}
+
+static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
+ unsigned int *width, unsigned int *height)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (hdmi_ops && hdmi_ops->get_max_resol)
+ hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height);
+}
+
+static void drm_hdmi_commit(struct device *subdrv_dev)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (hdmi_ops && hdmi_ops->commit)
+ hdmi_ops->commit(ctx->hdmi_ctx->ctx);
+}
+
+static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (mixer_ops && mixer_ops->dpms)
+ mixer_ops->dpms(ctx->mixer_ctx->ctx, mode);
+
+ if (hdmi_ops && hdmi_ops->dpms)
+ hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
+}
+
+static void drm_hdmi_apply(struct device *subdrv_dev)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+ int i;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ for (i = 0; i < MIXER_WIN_NR; i++) {
+ if (!ctx->enabled[i])
+ continue;
+ if (mixer_ops && mixer_ops->win_commit)
+ mixer_ops->win_commit(ctx->mixer_ctx->ctx, i);
+ }
+
+ if (hdmi_ops && hdmi_ops->commit)
+ hdmi_ops->commit(ctx->hdmi_ctx->ctx);
+}
+
+static struct rockchip_drm_manager_ops drm_hdmi_manager_ops = {
+ .dpms = drm_hdmi_dpms,
+ .apply = drm_hdmi_apply,
+ .enable_vblank = drm_hdmi_enable_vblank,
+ .disable_vblank = drm_hdmi_disable_vblank,
+ .wait_for_vblank = drm_hdmi_wait_for_vblank,
+ .mode_fixup = drm_hdmi_mode_fixup,
+ .mode_set = drm_hdmi_mode_set,
+ .get_max_resol = drm_hdmi_get_max_resol,
+ .commit = drm_hdmi_commit,
+};
+
+static void drm_mixer_mode_set(struct device *subdrv_dev,
+ struct rockchip_drm_overlay *overlay)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (mixer_ops && mixer_ops->win_mode_set)
+ mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay);
+}
+
+static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+ int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (win < 0 || win > MIXER_WIN_NR) {
+ DRM_ERROR("mixer window[%d] is wrong\n", win);
+ return;
+ }
+
+ if (mixer_ops && mixer_ops->win_commit)
+ mixer_ops->win_commit(ctx->mixer_ctx->ctx, win);
+
+ ctx->enabled[win] = true;
+}
+
+static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+ int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (win < 0 || win > MIXER_WIN_NR) {
+ DRM_ERROR("mixer window[%d] is wrong\n", win);
+ return;
+ }
+
+ if (mixer_ops && mixer_ops->win_disable)
+ mixer_ops->win_disable(ctx->mixer_ctx->ctx, win);
+
+ ctx->enabled[win] = false;
+}
+
+static struct rockchip_drm_overlay_ops drm_hdmi_overlay_ops = {
+ .mode_set = drm_mixer_mode_set,
+ .commit = drm_mixer_commit,
+ .disable = drm_mixer_disable,
+};
+
+static struct rockchip_drm_manager hdmi_manager = {
+ .pipe = -1,
+ .ops = &drm_hdmi_manager_ops,
+ .overlay_ops = &drm_hdmi_overlay_ops,
+ .display_ops = &drm_hdmi_display_ops,
+};
+
+static int hdmi_subdrv_probe(struct drm_device *drm_dev,
+ struct device *dev)
+{
+ struct rockchip_drm_subdrv *subdrv = to_subdrv(dev);
+ struct drm_hdmi_context *ctx;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!hdmi_ctx) {
+ DRM_ERROR("hdmi context not initialized.\n");
+ return -EFAULT;
+ }
+
+ if (!mixer_ctx) {
+ DRM_ERROR("mixer context not initialized.\n");
+ return -EFAULT;
+ }
+
+ ctx = get_ctx_from_subdrv(subdrv);
+
+ if (!ctx) {
+ DRM_ERROR("no drm hdmi context.\n");
+ return -EFAULT;
+ }
+
+ ctx->hdmi_ctx = hdmi_ctx;
+ ctx->mixer_ctx = mixer_ctx;
+
+ ctx->hdmi_ctx->drm_dev = drm_dev;
+ ctx->mixer_ctx->drm_dev = drm_dev;
+
+ if (mixer_ops->iommu_on)
+ mixer_ops->iommu_on(ctx->mixer_ctx->ctx, true);
+
+ return 0;
+}
+
+static void hdmi_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
+{
+ struct drm_hdmi_context *ctx;
+ struct rockchip_drm_subdrv *subdrv = to_subdrv(dev);
+
+ ctx = get_ctx_from_subdrv(subdrv);
+
+ if (mixer_ops->iommu_on)
+ mixer_ops->iommu_on(ctx->mixer_ctx->ctx, false);
+}
+
+static int rockchip_drm_hdmi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rockchip_drm_subdrv *subdrv;
+ struct drm_hdmi_context *ctx;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ DRM_LOG_KMS("failed to alloc common hdmi context.\n");
+ return -ENOMEM;
+ }
+
+ subdrv = &ctx->subdrv;
+
+ subdrv->dev = dev;
+ subdrv->manager = &hdmi_manager;
+ subdrv->probe = hdmi_subdrv_probe;
+ subdrv->remove = hdmi_subdrv_remove;
+
+ platform_set_drvdata(pdev, subdrv);
+
+ rockchip_drm_subdrv_register(subdrv);
+
+ return 0;
+}
+
+static int rockchip_drm_hdmi_remove(struct platform_device *pdev)
+{
+ struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ rockchip_drm_subdrv_unregister(&ctx->subdrv);
+
+ return 0;
+}
+
+struct platform_driver rockchip_drm_common_hdmi_driver = {
+ .probe = rockchip_drm_hdmi_probe,
+ .remove = rockchip_drm_hdmi_remove,
+ .driver = {
+ .name = "rockchip-drm-hdmi",
+ .owner = THIS_MODULE,
+ },
+};
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_HDMI_H_
+#define _ROCKCHIP_DRM_HDMI_H_
+
+#define MIXER_WIN_NR 3
+#define MIXER_DEFAULT_WIN 0
+
+/*
+ * rockchip hdmi common context structure.
+ *
+ * @drm_dev: pointer to drm_device.
+ * @ctx: pointer to the context of specific device driver.
+ * this context should be hdmi_context or mixer_context.
+ */
+struct rockchip_drm_hdmi_context {
+ struct drm_device *drm_dev;
+ void *ctx;
+};
+
+struct rockchip_hdmi_ops {
+ /* display */
+ bool (*is_connected)(void *ctx);
+ struct edid *(*get_edid)(void *ctx,
+ struct drm_connector *connector);
+ int (*check_timing)(void *ctx, struct fb_videomode *timing);
+ int (*power_on)(void *ctx, int mode);
+
+ /* manager */
+ void (*mode_set)(void *ctx, void *mode);
+ void (*get_max_resol)(void *ctx, unsigned int *width,
+ unsigned int *height);
+ void (*commit)(void *ctx);
+ void (*dpms)(void *ctx, int mode);
+};
+
+struct rockchip_mixer_ops {
+ /* manager */
+ int (*iommu_on)(void *ctx, bool enable);
+ int (*enable_vblank)(void *ctx, int pipe);
+ void (*disable_vblank)(void *ctx);
+ void (*wait_for_vblank)(void *ctx);
+ void (*dpms)(void *ctx, int mode);
+
+ /* overlay */
+ void (*win_mode_set)(void *ctx, struct rockchip_drm_overlay *overlay);
+ void (*win_commit)(void *ctx, int zpos);
+ void (*win_disable)(void *ctx, int zpos);
+
+ /* display */
+ int (*check_timing)(void *ctx, struct fb_videomode *timing);
+};
+
+void rockchip_hdmi_drv_attach(struct rockchip_drm_hdmi_context *ctx);
+void rockchip_mixer_drv_attach(struct rockchip_drm_hdmi_context *ctx);
+void rockchip_hdmi_ops_register(struct rockchip_hdmi_ops *ops);
+void rockchip_mixer_ops_register(struct rockchip_mixer_ops *ops);
+#endif
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drmP.h>
+#include <drm/rockchip_drm.h>
+
+#include <linux/dma-mapping.h>
+#include <linux/iommu.h>
+#include <linux/kref.h>
+
+#include <asm/dma-iommu.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_iommu.h"
+
+/*
+ * drm_create_iommu_mapping - create a mapping structure
+ *
+ * @drm_dev: DRM device
+ */
+int drm_create_iommu_mapping(struct drm_device *drm_dev)
+{
+ struct dma_iommu_mapping *mapping = NULL;
+ struct rockchip_drm_private *priv = drm_dev->dev_private;
+ struct device *dev = drm_dev->dev;
+
+ if (!priv->da_start)
+ priv->da_start = ROCKCHIP_DEV_ADDR_START;
+ if (!priv->da_space_size)
+ priv->da_space_size = ROCKCHIP_DEV_ADDR_SIZE;
+ if (!priv->da_space_order)
+ priv->da_space_order = ROCKCHIP_DEV_ADDR_ORDER;
+
+ mapping = arm_iommu_create_mapping(&platform_bus_type, priv->da_start,
+ priv->da_space_size,
+ priv->da_space_order);
+ if (IS_ERR(mapping))
+ return PTR_ERR(mapping);
+
+ dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
+ GFP_KERNEL);
+ dma_set_max_seg_size(dev, 0xffffffffu);
+ dev->archdata.mapping = mapping;
+
+ return 0;
+}
+
+/*
+ * drm_release_iommu_mapping - release iommu mapping structure
+ *
+ * @drm_dev: DRM device
+ *
+ * if mapping->kref becomes 0 then all things related to iommu mapping
+ * will be released
+ */
+void drm_release_iommu_mapping(struct drm_device *drm_dev)
+{
+ struct device *dev = drm_dev->dev;
+
+ arm_iommu_release_mapping(dev->archdata.mapping);
+}
+
+/*
+ * drm_iommu_attach_device- attach device to iommu mapping
+ *
+ * @drm_dev: DRM device
+ * @subdrv_dev: device to be attach
+ *
+ * This function should be called by sub drivers to attach it to iommu
+ * mapping.
+ */
+int drm_iommu_attach_device(struct drm_device *drm_dev,
+ struct device *subdrv_dev)
+{
+ struct device *dev = drm_dev->dev;
+ int ret;
+
+ if (!dev->archdata.mapping) {
+ DRM_ERROR("iommu_mapping is null.\n");
+ return -EFAULT;
+ }
+
+ subdrv_dev->dma_parms = devm_kzalloc(subdrv_dev,
+ sizeof(*subdrv_dev->dma_parms),
+ GFP_KERNEL);
+ dma_set_max_seg_size(subdrv_dev, 0xffffffffu);
+
+ ret = arm_iommu_attach_device(subdrv_dev, dev->archdata.mapping);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("failed iommu attach.\n");
+ return ret;
+ }
+
+ /*
+ * Set dma_ops to drm_device just one time.
+ *
+ * The dma mapping api needs device object and the api is used
+ * to allocate physial memory and map it with iommu table.
+ * If iommu attach succeeded, the sub driver would have dma_ops
+ * for iommu and also all sub drivers have same dma_ops.
+ */
+ if (!dev->archdata.dma_ops)
+ dev->archdata.dma_ops = subdrv_dev->archdata.dma_ops;
+
+ return 0;
+}
+
+/*
+ * drm_iommu_detach_device -detach device address space mapping from device
+ *
+ * @drm_dev: DRM device
+ * @subdrv_dev: device to be detached
+ *
+ * This function should be called by sub drivers to detach it from iommu
+ * mapping
+ */
+void drm_iommu_detach_device(struct drm_device *drm_dev,
+ struct device *subdrv_dev)
+{
+ struct device *dev = drm_dev->dev;
+ struct dma_iommu_mapping *mapping = dev->archdata.mapping;
+
+ if (!mapping || !mapping->domain)
+ return;
+
+ iommu_detach_device(mapping->domain, subdrv_dev);
+ drm_release_iommu_mapping(drm_dev);
+}
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _ROCKCHIP_DRM_IOMMU_H_
+#define _ROCKCHIP_DRM_IOMMU_H_
+
+#define ROCKCHIP_DEV_ADDR_START 0x20000000
+#define ROCKCHIP_DEV_ADDR_SIZE 0x40000000
+#define ROCKCHIP_DEV_ADDR_ORDER 0x0
+
+#ifdef CONFIG_DRM_ROCKCHIP_IOMMU
+
+int drm_create_iommu_mapping(struct drm_device *drm_dev);
+
+void drm_release_iommu_mapping(struct drm_device *drm_dev);
+
+int drm_iommu_attach_device(struct drm_device *drm_dev,
+ struct device *subdrv_dev);
+
+void drm_iommu_detach_device(struct drm_device *dev_dev,
+ struct device *subdrv_dev);
+
+static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)
+{
+#ifdef CONFIG_ARM_DMA_USE_IOMMU
+ struct device *dev = drm_dev->dev;
+
+ return dev->archdata.mapping ? true : false;
+#else
+ return false;
+#endif
+}
+
+#else
+
+struct dma_iommu_mapping;
+static inline int drm_create_iommu_mapping(struct drm_device *drm_dev)
+{
+ return 0;
+}
+
+static inline void drm_release_iommu_mapping(struct drm_device *drm_dev)
+{
+}
+
+static inline int drm_iommu_attach_device(struct drm_device *drm_dev,
+ struct device *subdrv_dev)
+{
+ return 0;
+}
+
+static inline void drm_iommu_detach_device(struct drm_device *drm_dev,
+ struct device *subdrv_dev)
+{
+}
+
+static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)
+{
+ return false;
+}
+
+#endif
+#endif
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+
+#include <drm/rockchip_drm.h>
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_encoder.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_gem.h"
+
+#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
+
+struct rockchip_plane {
+ struct drm_plane base;
+ struct rockchip_drm_overlay overlay;
+ bool enabled;
+};
+
+static const uint32_t formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_NV12,
+ DRM_FORMAT_NV12MT,
+};
+
+/*
+ * This function is to get X or Y size shown via screen. This needs length and
+ * start position of CRTC.
+ *
+ * <--- length --->
+ * CRTC ----------------
+ * ^ start ^ end
+ *
+ * There are six cases from a to f.
+ *
+ * <----- SCREEN ----->
+ * 0 last
+ * ----------|------------------|----------
+ * CRTCs
+ * a -------
+ * b -------
+ * c --------------------------
+ * d --------
+ * e -------
+ * f -------
+ */
+static int rockchip_plane_get_size(int start, unsigned length, unsigned last)
+{
+ int end = start + length;
+ int size = 0;
+
+ if (start <= 0) {
+ if (end > 0)
+ size = min_t(unsigned, end, last);
+ } else if (start <= last) {
+ size = min_t(unsigned, last - start, length);
+ }
+
+ return size;
+}
+
+int rockchip_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
+ struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
+{
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+ struct rockchip_drm_overlay *overlay = &rockchip_plane->overlay;
+ unsigned int actual_w;
+ unsigned int actual_h;
+ int nr;
+ int i;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ nr = rockchip_drm_fb_get_buf_cnt(fb);
+ for (i = 0; i < nr; i++) {
+ struct rockchip_drm_gem_buf *buffer = rockchip_drm_fb_buffer(fb, i);
+
+ if (!buffer) {
+ DRM_LOG_KMS("buffer is null\n");
+ return -EFAULT;
+ }
+
+ overlay->dma_addr[i] = buffer->dma_addr;
+
+ DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n",
+ i, (unsigned long)overlay->dma_addr[i]);
+ }
+
+ actual_w = rockchip_plane_get_size(crtc_x, crtc_w, crtc->mode.hdisplay);
+ actual_h = rockchip_plane_get_size(crtc_y, crtc_h, crtc->mode.vdisplay);
+
+ if (crtc_x < 0) {
+ if (actual_w)
+ src_x -= crtc_x;
+ crtc_x = 0;
+ }
+
+ if (crtc_y < 0) {
+ if (actual_h)
+ src_y -= crtc_y;
+ crtc_y = 0;
+ }
+
+ /* set drm framebuffer data. */
+ overlay->fb_x = src_x;
+ overlay->fb_y = src_y;
+ overlay->fb_width = fb->width;
+ overlay->fb_height = fb->height;
+ overlay->src_width = src_w;
+ overlay->src_height = src_h;
+ overlay->bpp = fb->bits_per_pixel;
+ overlay->pitch = fb->pitches[0];
+ overlay->pixel_format = fb->pixel_format;
+
+ /* set overlay range to be displayed. */
+ overlay->crtc_x = crtc_x;
+ overlay->crtc_y = crtc_y;
+ overlay->crtc_width = actual_w;
+ overlay->crtc_height = actual_h;
+
+ /* set drm mode data. */
+ overlay->mode_width = crtc->mode.hdisplay;
+ overlay->mode_height = crtc->mode.vdisplay;
+ overlay->refresh = crtc->mode.vrefresh;
+ overlay->scan_flag = crtc->mode.flags;
+
+ DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)",
+ overlay->crtc_x, overlay->crtc_y,
+ overlay->crtc_width, overlay->crtc_height);
+
+ rockchip_drm_fn_encoder(crtc, overlay, rockchip_drm_encoder_plane_mode_set);
+
+ return 0;
+}
+
+void rockchip_plane_commit(struct drm_plane *plane)
+{
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+ struct rockchip_drm_overlay *overlay = &rockchip_plane->overlay;
+
+ rockchip_drm_fn_encoder(plane->crtc, &overlay->zpos,
+ rockchip_drm_encoder_plane_commit);
+}
+
+void rockchip_plane_dpms(struct drm_plane *plane, int mode)
+{
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+ struct rockchip_drm_overlay *overlay = &rockchip_plane->overlay;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ if (mode == DRM_MODE_DPMS_ON) {
+ if (rockchip_plane->enabled)
+ return;
+
+ rockchip_drm_fn_encoder(plane->crtc, &overlay->zpos,
+ rockchip_drm_encoder_plane_enable);
+
+ rockchip_plane->enabled = true;
+ } else {
+ if (!rockchip_plane->enabled)
+ return;
+
+ rockchip_drm_fn_encoder(plane->crtc, &overlay->zpos,
+ rockchip_drm_encoder_plane_disable);
+
+ rockchip_plane->enabled = false;
+ }
+}
+
+static int
+rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
+ struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
+{
+ int ret;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ ret = rockchip_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y,
+ crtc_w, crtc_h, src_x >> 16, src_y >> 16,
+ src_w >> 16, src_h >> 16);
+ if (ret < 0)
+ return ret;
+
+ plane->crtc = crtc;
+
+ rockchip_plane_commit(plane);
+ rockchip_plane_dpms(plane, DRM_MODE_DPMS_ON);
+
+ return 0;
+}
+
+static int rockchip_disable_plane(struct drm_plane *plane)
+{
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ rockchip_plane_dpms(plane, DRM_MODE_DPMS_OFF);
+
+ return 0;
+}
+
+static void rockchip_plane_destroy(struct drm_plane *plane)
+{
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ rockchip_disable_plane(plane);
+ drm_plane_cleanup(plane);
+ kfree(rockchip_plane);
+}
+
+static int rockchip_plane_set_property(struct drm_plane *plane,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct drm_device *dev = plane->dev;
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+ struct rockchip_drm_private *dev_priv = dev->dev_private;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ if (property == dev_priv->plane_zpos_property) {
+ rockchip_plane->overlay.zpos = val;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static struct drm_plane_funcs rockchip_plane_funcs = {
+ .update_plane = rockchip_update_plane,
+ .disable_plane = rockchip_disable_plane,
+ .destroy = rockchip_plane_destroy,
+ .set_property = rockchip_plane_set_property,
+};
+
+static void rockchip_plane_attach_zpos_property(struct drm_plane *plane)
+{
+ struct drm_device *dev = plane->dev;
+ struct rockchip_drm_private *dev_priv = dev->dev_private;
+ struct drm_property *prop;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ prop = dev_priv->plane_zpos_property;
+ if (!prop) {
+ prop = drm_property_create_range(dev, 0, "zpos", 0,
+ MAX_PLANE - 1);
+ if (!prop)
+ return;
+
+ dev_priv->plane_zpos_property = prop;
+ }
+
+ drm_object_attach_property(&plane->base, prop, 0);
+}
+
+struct drm_plane *rockchip_plane_init(struct drm_device *dev,
+ unsigned int possible_crtcs, bool priv)
+{
+ struct rockchip_plane *rockchip_plane;
+ int err;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ rockchip_plane = kzalloc(sizeof(struct rockchip_plane), GFP_KERNEL);
+ if (!rockchip_plane) {
+ DRM_ERROR("failed to allocate plane\n");
+ return NULL;
+ }
+
+ err = drm_plane_init(dev, &rockchip_plane->base, possible_crtcs,
+ &rockchip_plane_funcs, formats, ARRAY_SIZE(formats),
+ priv);
+ if (err) {
+ DRM_ERROR("failed to initialize plane\n");
+ kfree(rockchip_plane);
+ return NULL;
+ }
+
+ if (priv)
+ rockchip_plane->overlay.zpos = DEFAULT_ZPOS;
+ else
+ rockchip_plane_attach_zpos_property(&rockchip_plane->base);
+
+ return &rockchip_plane->base;
+}
--- /dev/null
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+int rockchip_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
+ struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h);
+void rockchip_plane_commit(struct drm_plane *plane);
+void rockchip_plane_dpms(struct drm_plane *plane, int mode);
+struct drm_plane *rockchip_plane_init(struct drm_device *dev,
+ unsigned int possible_crtcs, bool priv);
--- /dev/null
+#
+# Generated files
+#
+*lcd.h
--- /dev/null
+choice
+ prompt "LCD Panel Select"
+
+config LCD_GENERAL
+ bool "General lcd panel"
+ help
+ select if the panel do not need initialization
+config LCD_LD089WU1_MIPI
+ bool "mipi dsi lcd LD089WU1 1920X1200"
+config LCD_B080XAN02_MIPI
+ bool "mipi dsi lcd B080XAN02 1024X768"
+endchoice
+
+
--- /dev/null
+obj-$(CONFIG_LCD_GENERAL) += lcd_general.o
+obj-$(CONFIG_LCD_LD089WU1_MIPI) += lcd_LD089WU1_mipi.o
+obj-$(CONFIG_LCD_B080XAN02_MIPI) += lcd_B080XAN02_mipi.o
+
+
+quiet_cmd_gen = GEN $@
+ cmd_gen = cmp -s $< $@ || cp $< $@
+
+lcd-obj := $(filter lcd_%.o,$(obj-y))
+lcd-cfile := $(patsubst %.o,%.c,$(lcd-obj))
+lcd-cpath := $(src)/$(lcd-cfile)
+
+obj-y := $(filter-out $(lcd-obj),$(obj-y))
+
+$(obj)/lcd.h: $(lcd-cpath) FORCE
+ $(call if_changed,gen)
+
+$(obj)/rk_screen.o: $(obj)/lcd.h
+obj-y += rk_screen.o
+
+clean-files := lcd.h
+
+
+
--- /dev/null
+#ifndef __LCD_B080XAN02__
+#define __LCD_B080XAN02__
+
+#if defined(CONFIG_MIPI_DSI)
+#include "../transmitter/mipi_dsi.h"
+#endif
+#include <linux/delay.h>
+
+
+
+#define RK_SCREEN_INIT 1
+
+/* about mipi */
+#define MIPI_DSI_LANE 4
+#define MIPI_DSI_HS_CLK 528*1000000 //1000*1000000
+
+
+#if defined(RK_SCREEN_INIT)
+static struct rk29lcd_info *gLcd_info = NULL;
+
+int rk_lcd_init(void) {
+
+ u8 dcs[16] = {0};
+ if(dsi_is_active() != 1)
+ return -1;
+ /*below is changeable*/
+ dsi_enable_hs_clk(1);
+
+ dcs[0] = LPDT;
+ dcs[1] = dcs_exit_sleep_mode;
+ dsi_send_dcs_packet(dcs, 2);
+ msleep(1);
+ dcs[0] = LPDT;
+ dcs[1] = dcs_set_display_on;
+ dsi_send_dcs_packet(dcs, 2);
+ msleep(10);
+ //dsi_enable_command_mode(0);
+ dsi_enable_video_mode(1);
+
+ printk("++++++++++++++++%s:%d\n", __func__, __LINE__);
+ return 0;
+}
+
+int rk_lcd_standby(u8 enable) {
+
+ u8 dcs[16] = {0};
+ if(dsi_is_active() != 1)
+ return -1;
+
+ if(enable) {
+ /*below is changeable*/
+ dcs[0] = LPDT;
+ dcs[1] = dcs_set_display_off;
+ dsi_send_dcs_packet(dcs, 2);
+ msleep(1);
+ dcs[0] = LPDT;
+ dcs[1] = dcs_enter_sleep_mode;
+ dsi_send_dcs_packet(dcs, 2);
+ msleep(1);
+
+ printk("++++enable++++++++++++%s:%d\n", __func__, __LINE__);
+
+ } else {
+ /*below is changeable*/
+ rk_lcd_init();
+ printk("++++++++++++++++%s:%d\n", __func__, __LINE__);
+ }
+ return 0;
+}
+#endif
+
+#endif
--- /dev/null
+#ifndef __LCD_LD089WU1__
+#define __LCD_LD089WU1__
+
+#if defined(CONFIG_MIPI_DSI)
+#include "../transmitter/mipi_dsi.h"
+#endif
+
+
+
+
+#define RK_SCREEN_INIT 1
+
+/* about mipi */
+#define MIPI_DSI_LANE 4
+#define MIPI_DSI_HS_CLK 1000*1000000
+
+#if defined(RK_SCREEN_INIT)
+static struct rk29lcd_info *gLcd_info = NULL;
+
+int rk_lcd_init(void) {
+
+ u8 dcs[16] = {0};
+ if(dsi_is_active() != 1)
+ return -1;
+
+ /*below is changeable*/
+ dsi_enable_hs_clk(1);
+ dsi_enable_video_mode(0);
+ dsi_enable_command_mode(1);
+ dcs[0] = dcs_exit_sleep_mode;
+ dsi_send_dcs_packet(dcs, 1);
+ msleep(1);
+ dcs[0] = dcs_set_display_on;
+ dsi_send_dcs_packet(dcs, 1);
+ msleep(10);
+ dsi_enable_command_mode(0);
+ dsi_enable_video_mode(1);
+ //printk("++++++++++++++++%s:%d\n", __func__, __LINE__);
+};
+
+
+
+int rk_lcd_standby(u8 enable) {
+
+ u8 dcs[16] = {0};
+ if(dsi_is_active() != 1)
+ return -1;
+
+ if(enable) {
+ dsi_enable_video_mode(0);
+ dsi_enable_command_mode(1);
+ /*below is changeable*/
+ dcs[0] = dcs_set_display_off;
+ dsi_send_dcs_packet(dcs, 1);
+ msleep(1);
+ dcs[0] = dcs_enter_sleep_mode;
+ dsi_send_dcs_packet(dcs, 1);
+ msleep(1);
+ //printk("++++++++++++++++%s:%d\n", __func__, __LINE__);
+
+ } else {
+ /*below is changeable*/
+ rk_lcd_init();
+ //printk("++++++++++++++++%s:%d\n", __func__, __LINE__);
+
+ }
+};
+#endif
+
+#endif
--- /dev/null
+
+#ifndef __LCD_NULL__
+#define __LCD_NULL__
+
+
+
+#endif
+
--- /dev/null
+
+#include <linux/rk_fb.h>
+#include <linux/device.h>
+#include "lcd.h"
+
+static struct rk_screen *rk_screen;
+int rk_fb_get_prmry_screen(struct rk_screen *screen)
+{
+ memcpy(screen, rk_screen, sizeof(struct rk_screen));
+ return 0;
+}
+
+
+size_t get_fb_size(void)
+{
+
+}
+
+static int rk_screen_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ if (!np) {
+ dev_err(&pdev->dev, "Missing device tree node.\n");
+ return -EINVAL;
+ }
+ rk_screen = devm_kzalloc(&pdev->dev, sizeof(struct rk_screen), GFP_KERNEL);
+ if (!rk_screen) {
+ dev_err(&pdev->dev, "kmalloc for rk screen fail!");
+ return -ENOMEM;
+ }
+ ret = rk_fb_prase_timing_dt(np,rk_screen);
+ dev_info(&pdev->dev, "rockchip screen probe %s\n",
+ ret? "failed" : "success");
+ return ret;
+}
+
+static const struct of_device_id rk_screen_dt_ids[] = {
+ { .compatible = "rockchip,screen", },
+ {}
+};
+
+static struct platform_driver rk_screen_driver = {
+ .probe = rk_screen_probe,
+ .driver = {
+ .name = "rk-screen",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(rk_screen_dt_ids),
+ },
+};
+
+static int __init rk_screen_init(void)
+{
+ return platform_driver_register(&rk_screen_driver);
+}
+
+static void __exit rk_screen_exit(void)
+{
+ platform_driver_unregister(&rk_screen_driver);
+}
+
+subsys_initcall_sync(rk_screen_init);
+module_exit(rk_screen_exit);
+
--- /dev/null
+
+menuconfig RK_TRSM
+ bool "RockChip display transmitter support"
+ depends on FB_ROCKCHIP
+
+config RK2928_LVDS
+ bool "RK2928/RK2926 lvds transmitter support"
+ depends on ARCH_RK2928 && RK_TRSM
+
+config RK3026_LVDS
+ depends on ARCH_RK3026 && RK_TRSM
+ bool "RK3026/RK3028A lvds transmitter support"
+ default y
+
+config RK32_LVDS
+ bool "RK32 lvds transmitter support"
+ depends on RK_TRSM
+
+config RK610_LVDS
+ bool "RK610(Jetta) lvds transmitter support"
+ depends on MFD_RK610 && RK_TRSM
+ help
+ Support Jetta(RK610) to output LCD1 and LVDS.
+
+config RK616_LVDS
+ bool "RK616(JettaB) lvds,lcd,scaler vido interface support"
+ depends on MFD_RK616 && RK_TRSM
+ help
+ RK616(Jetta B) LVDS,LCD,scaler transmitter support.
+
+
+config DP_ANX6345
+ bool "RGB to DisplayPort transmitter anx6345,anx9804,anx9805 support"
+ depends on RK_TRSM
+
+config DP501
+ bool"RGB to DisplayPort transmitter dp501 support"
+ depends on RK_TRSM
+
+config RK32_DP
+ bool "RK32 RGB to DisplayPort transmitter support "
+ depends on RK_TRSM
+
+config MIPI_DSI
+ depends on RK_TRSM
+ bool "Rockchip MIPI DSI support"
+
+config TC358768_RGB2MIPI
+ tristate "toshiba TC358768 RGB to MIPI DSI"
+ depends on MIPI_DSI
+ help
+ "a chip that change RGB interface parallel signal into DSI serial signal"
+
+config SSD2828_RGB2MIPI
+ tristate "solomon SSD2828 RGB to MIPI DSI"
+ depends on MIPI_DSI
+ help
+ "a chip that change RGB interface parallel signal into DSI serial signal"
+
+config RK616_MIPI_DSI
+ tristate "Rockchip mipi dsi support"
+ depends on MIPI_DSI
+ help
+ Rockchip mipi dsi support.
+
+
+config RK616_MIPI_DSI_RST
+ bool "Reset the rockchip mipi dsi"
+ depends on MFD_RK616 && RK616_MIPI_DSI && RK616_USE_MCLK_12M
+ default y
+ help
+ if you say y here: inset the hdmi, mipi lcd will be reset.
--- /dev/null
+#
+# Makefile for display transmitter like lvds edp mipi
+#
+obj-$(CONFIG_RK2928_LVDS) += rk2928_lvds.o
+obj-$(CONFIG_RK3026_LVDS) += rk3026_lvds.o
+obj-$(CONFIG_RK610_LVDS) += rk610_lcd.o
+obj-$(CONFIG_RK616_LVDS) += rk616_lvds.o
+obj-y += rk32_lvds.o
+obj-$(CONFIG_DP_ANX6345) += dp_anx6345.o
+obj-$(CONFIG_DP501) += dp501.o
+obj-$(CONFIG_RK32_DP) += rk32_dp.o rk32_dp_reg.o
+obj-$(CONFIG_MIPI_DSI) += mipi_dsi.o
+obj-$(CONFIG_RK616_MIPI_DSI) += rk616_mipi_dsi.o
+obj-$(CONFIG_TC358768_RGB2MIPI) += tc358768.o
+obj-$(CONFIG_SSD2828_RGB2MIPI) += ssd2828.o
+
--- /dev/null
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/rk_fb.h>
+#include <linux/rockchip/iomap.h>
+#include <linux/rockchip/grf.h>
+#include "rk32_lvds.h"
+
+//#define TTL_TO_LVDS 1
+static struct rk32_lvds *rk32_lvds;
+static int rk32_lvds_disable(void)
+{
+ struct rk32_lvds *lvds = rk32_lvds;
+ writel_relaxed(0x80008000, RK_GRF_VIRT + RK3288_GRF_SOC_CON7);
+ writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_21); /*disable tx*/
+ writel_relaxed(0xff, lvds->regs + LVDS_CFG_REG_c); /*disable pll*/
+ clk_disable_unprepare(lvds->clk);
+ return 0;
+}
+
+static int rk32_lvds_en(void)
+{
+ struct rk32_lvds *lvds = rk32_lvds;
+ struct rk_screen *screen = &lvds->screen;
+ u32 h_bp = screen->mode.hsync_len + screen->mode.left_margin;
+ u32 i,j, val ;
+
+ clk_prepare_enable(lvds->clk);
+ screen->type = SCREEN_RGB;
+
+ screen->lcdc_id = 1;
+ if (screen->lcdc_id == 1) /*lcdc1 = vop little,lcdc0 = vop big*/
+ val = LVDS_SEL_VOP_LIT | (LVDS_SEL_VOP_LIT << 16);
+ else
+ val = LVDS_SEL_VOP_LIT << 16;
+ writel_relaxed(val, RK_GRF_VIRT + RK3288_GRF_SOC_CON6);
+
+ val = screen->lvds_format;
+ if (screen->type == SCREEN_DUAL_LVDS)
+ val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN;
+ else if(screen->type == SCREEN_LVDS)
+ val |= LVDS_CH0_EN;
+
+ //val |= LVDS_MSB;
+ else if (screen->type == SCREEN_RGB)
+ val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN;
+
+ if (h_bp & 0x01)
+ val |= LVDS_START_PHASE_RST_1;
+
+ val |= (screen->pin_dclk << 8) | (screen->pin_hsync << 9) |
+ (screen->pin_den << 10);
+ val |= 0xffff << 16;
+ //val = 0x08010801;
+ writel_relaxed(val, RK_GRF_VIRT + RK3288_GRF_SOC_CON7);
+
+ if (screen->type == SCREEN_LVDS)
+ val = 0xbf;
+ else
+ val = 0x7f;
+#if 0
+ for(i=0;i<0x200;){
+ val = readl_relaxed(lvds->regs + i);
+ printk("0x%08x:0x%08x ",i,val);
+ i += 4;
+ if(i % 16 == 0)
+ printk("\n");
+ }
+#endif
+ #ifdef TTL_TO_LVDS // 0 ttl 1 lvds
+ val = 0x007f007f;//0x1<<6 |0x1 <<4;
+ writel_relaxed(val, RK_GRF_VIRT + 0xc);
+
+
+ lvds_writel(lvds, LVDS_CH0_REG_0, 0x7f);
+ lvds_writel(lvds, LVDS_CH0_REG_1, 0x40);
+ lvds_writel(lvds, LVDS_CH0_REG_2, 0x00);
+
+ if (screen->type == SCREEN_RGB)
+ val = 0x1f;
+ else
+ val = 0x00;
+ lvds_writel(lvds, LVDS_CH0_REG_4, 0x3f);
+ lvds_writel(lvds, LVDS_CH0_REG_5, 0x3f);
+ lvds_writel(lvds, LVDS_CH0_REG_3, 0x46);
+ lvds_writel(lvds, LVDS_CH0_REG_d, 0x0a);
+ lvds_writel(lvds, LVDS_CH0_REG_20,0x44);/* 44:LSB 45:MSB*/
+ writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_c); /*eanble pll*/
+ writel_relaxed(0x92, lvds->regs + LVDS_CFG_REG_21); /*enable tx*/
+
+ lvds_writel(lvds, 0x100, 0x7f);
+ lvds_writel(lvds, 0x104, 0x40);
+ lvds_writel(lvds, 0x108, 0x00);
+ lvds_writel(lvds, 0x10c, 0x46);
+ lvds_writel(lvds, 0x110, 0x3f);
+ lvds_writel(lvds, 0x114, 0x3f);
+ lvds_writel(lvds, 0x134, 0x0a);
+ #else
+ val = readl_relaxed(lvds->regs + 0x88);
+ printk("0x88:0x%x\n",val);
+
+ lvds_writel(lvds, LVDS_CH0_REG_0, 0xbf);
+ lvds_writel(lvds, LVDS_CH0_REG_1, 0x3f);// 3f
+ lvds_writel(lvds, LVDS_CH0_REG_2, 0xfe);
+ lvds_writel(lvds, LVDS_CH0_REG_3, 0x46);//0x46
+ lvds_writel(lvds, LVDS_CH0_REG_4, 0x00);
+ //lvds_writel(lvds, LVDS_CH0_REG_9, 0x20);
+ //lvds_writel(lvds, LVDS_CH0_REG_d, 0x4b);
+ //lvds_writel(lvds, LVDS_CH0_REG_f, 0x0d);
+ lvds_writel(lvds, LVDS_CH0_REG_d, 0x0a);//0a
+ lvds_writel(lvds, LVDS_CH0_REG_20,0x44);/* 44:LSB 45:MSB*/
+ //lvds_writel(lvds, 0x24,0x20);
+ //writel_relaxed(0x23, lvds->regs + 0x88);
+ writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_c); /*eanble pll*/
+ writel_relaxed(0x92, lvds->regs + LVDS_CFG_REG_21); /*enable tx*/
+
+
+ //lvds_writel(lvds, 0x100, 0xbf);
+ //lvds_writel(lvds, 0x104, 0x3f);
+ //lvds_writel(lvds, 0x108, 0xfe);
+ //lvds_writel(lvds, 0x10c, 0x46); //0x46
+ //lvds_writel(lvds, 0x110, 0x00);
+ //lvds_writel(lvds, 0x114, 0x00);
+ //lvds_writel(lvds, 0x134, 0x0a);
+
+ #endif
+#if 0
+ for(i=0;i<100;i++){
+ mdelay(1000);
+ mdelay(1000);
+ mdelay(1000);
+ mdelay(1000);
+ mdelay(1000);
+ printk("write LVDS_CH0_REG_20 :0x40\n");
+ //writel_relaxed(0x10, lvds->regs + LVDS_CFG_REG_c);
+ lvds_writel(lvds, LVDS_CH0_REG_20,0x40);/* 44:LSB 45:MSB*/
+ val = readl_relaxed(lvds->regs + LVDS_CH0_REG_20);
+ printk("read back LVDS_CH0_REG_20:0x%x\n",val);
+ mdelay(1000);
+ mdelay(1000);
+ mdelay(1000);
+ mdelay(1000);
+ mdelay(1000);
+ printk("write LVDS_CH0_REG_20 :0x44\n");
+ lvds_writel(lvds, LVDS_CH0_REG_20,0x44);/* 44:LSB 45:MSB*/
+ val = readl_relaxed(lvds->regs + LVDS_CH0_REG_20);
+ printk("read back LVDS_CH0_REG_20:0x%x\n",val);
+ }
+#endif
+ //while(1)
+#if 0
+ {
+ val = readl_relaxed(RK_GRF_VIRT + RK3288_GRF_SOC_CON6);
+ printk("RK3288_GRF_SOC_CON6:0x%x\n",val);
+ val = readl_relaxed(RK_GRF_VIRT + RK3288_GRF_SOC_CON7);
+ printk("RK3288_GRF_SOC_CON7:0x%x\n",val);
+ val = readl_relaxed(RK_GRF_VIRT + RK3288_GRF_SOC_CON15);
+ printk("RK3288_GRF_SOC_CON15:0x%x\n",val);
+
+
+ val = readl_relaxed(lvds->regs + LVDS_CH0_REG_0);
+ printk("LVDS_CH0_REG_0:0x%x\n",val);
+
+ val = readl_relaxed(lvds->regs + LVDS_CH0_REG_1);
+ printk("LVDS_CH0_REG_1:0x%x\n",val);
+
+ val = readl_relaxed(lvds->regs + LVDS_CH0_REG_2);
+ printk("LVDS_CH0_REG_2:0x%x\n",val);
+
+ val = readl_relaxed(lvds->regs + LVDS_CH0_REG_3);
+ printk("LVDS_CH0_REG_3:0x%x\n",val);
+
+ val = readl_relaxed(lvds->regs + LVDS_CH0_REG_4);
+ printk("LVDS_CH0_REG_4:0x%x\n",val);
+
+ val = readl_relaxed(lvds->regs + LVDS_CH0_REG_5);
+ printk("LVDS_CH0_REG_5:0x%x\n",val);
+
+ val = readl_relaxed(lvds->regs + LVDS_CH0_REG_d);
+ printk("LVDS_CH0_REG_d:0x%x\n",val);
+
+ val = readl_relaxed(lvds->regs + LVDS_CH0_REG_f);
+ printk("LVDS_CH0_REG_f:0x%x\n",val);
+
+
+ val = readl_relaxed(lvds->regs + LVDS_CFG_REG_c);
+ printk("LVDS_CFG_REG_c:0x%x\n",val);
+
+ val = readl_relaxed(lvds->regs + LVDS_CFG_REG_21);
+ printk("LVDS_CFG_REG_21:0x%x\n",val);
+ val = readl_relaxed(lvds->regs + 0x100);
+ printk("0x100:0x%x\n",val);
+
+ val = readl_relaxed(lvds->regs + 0x104);
+ printk("0x104:0x%x\n",val);
+ val = readl_relaxed(lvds->regs + 0x108);
+ printk("0x108:0x%x\n",val);
+
+ val = readl_relaxed(lvds->regs + 0x10c);
+ printk("0x10c:0x%x\n",val);
+
+ val = readl_relaxed(lvds->regs + 0x110);
+ printk("0x110:0x%x\n",val);
+
+ val = readl_relaxed(lvds->regs + 0x114);
+ printk("0x114:0x%x\n",val);
+ val = readl_relaxed(lvds->regs + 0x118);
+ printk("0x118:0x%x\n",val);
+
+ val = readl_relaxed(lvds->regs + 0x11c);
+ printk("0x11c:0x%x\n",val);
+ mdelay(1000);
+ }
+
+ for(i=0;i<0x200;){
+ val = readl_relaxed(lvds->regs + i);
+ printk("0x%08x:0x%08x ",i,val);
+ i += 4;
+ if(i % 16 == 0)
+ printk("\n");
+ }
+#endif
+ return 0;
+}
+
+
+
+static struct rk_fb_trsm_ops trsm_lvds_ops = {
+ .enable = rk32_lvds_en,
+ .disable = rk32_lvds_disable,
+};
+
+static int rk32_lvds_probe(struct platform_device *pdev)
+{
+ struct rk32_lvds *lvds;
+ struct resource *res;
+ struct device_node *np = pdev->dev.of_node;
+
+ if (!np) {
+ dev_err(&pdev->dev, "Missing device tree node.\n");
+ return -EINVAL;
+ }
+
+ lvds = devm_kzalloc(&pdev->dev, sizeof(struct rk32_lvds), GFP_KERNEL);
+ if (!lvds) {
+ dev_err(&pdev->dev, "no memory for state\n");
+ return -ENOMEM;
+ }
+ lvds->dev = &pdev->dev;
+ rk_fb_get_prmry_screen(&lvds->screen);
+ if ((lvds->screen.type != SCREEN_RGB) &&
+ (lvds->screen.type != SCREEN_LVDS) &&
+ (lvds->screen.type != SCREEN_DUAL_LVDS)) {
+ dev_err(&pdev->dev, "screen is not lvds/rgb!\n");
+ return -EINVAL;
+ }
+ platform_set_drvdata(pdev, lvds);
+ dev_set_name(lvds->dev, "rk32-lvds");
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ lvds->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(lvds->regs)) {
+ dev_err(&pdev->dev, "ioremap reg failed\n");
+ return PTR_ERR(lvds->regs);
+ }
+ lvds->clk = devm_clk_get(&pdev->dev,NULL);
+ if (IS_ERR(lvds->clk)) {
+ dev_err(&pdev->dev, "get clk failed\n");
+ return PTR_ERR(lvds->clk);
+ }
+ rk32_lvds = lvds;
+ rk_fb_trsm_ops_register(&trsm_lvds_ops,SCREEN_LVDS);
+ dev_info(&pdev->dev, "rk32 lvds driver probe success\n");
+
+ return 0;
+}
+
+static void rk32_lvds_shutdown(struct platform_device *pdev)
+{
+
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rk32_lvds_dt_ids[] = {
+ {.compatible = "rockchip, rk32-lvds",},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, rk32_lvds_dt_ids);
+#endif
+
+static struct platform_driver rk32_lvds_driver = {
+ .probe = rk32_lvds_probe,
+ .driver = {
+ .name = "rk32-lvds",
+ .owner = THIS_MODULE,
+#if defined(CONFIG_OF)
+ .of_match_table = of_match_ptr(rk32_lvds_dt_ids),
+#endif
+ },
+ .shutdown = rk32_lvds_shutdown,
+};
+
+static int __init rk32_lvds_module_init(void)
+{
+ return platform_driver_register(&rk32_lvds_driver);
+}
+
+static void __exit rk32_lvds_module_exit(void)
+{
+
+}
+
+fs_initcall(rk32_lvds_module_init);
+module_exit(rk32_lvds_module_exit);
+
--- /dev/null
+#ifndef __RK32_LVDS__
+#define __RK32_LVDS__
+
+#define LVDS_CH0_REG_0 0x00
+#define LVDS_CH0_REG_1 0x04
+#define LVDS_CH0_REG_2 0x08
+#define LVDS_CH0_REG_3 0x0c
+#define LVDS_CH0_REG_4 0x10
+#define LVDS_CH0_REG_5 0x14
+#define LVDS_CH0_REG_9 0x24
+#define LVDS_CFG_REG_c 0x30
+#define LVDS_CH0_REG_d 0x34
+#define LVDS_CH0_REG_f 0x3c
+#define LVDS_CH0_REG_20 0x80
+#define LVDS_CFG_REG_21 0x84
+
+#define LVDS_SEL_VOP_LIT (1 << 3)
+
+#define LVDS_FMT_MASK (0x07 << 16)
+#define LVDS_MSB (0x01 << 3)
+#define LVDS_DUAL (0x01 << 4)
+#define LVDS_FMT_1 (0x01 << 5)
+#define LVDS_TTL_EN (0x01 << 6)
+#define LVDS_START_PHASE_RST_1 (0x01 << 7)
+#define LVDS_DCLK_INV (0x01 << 8)
+#define LVDS_CH0_EN (0x01 << 11)
+#define LVDS_CH1_EN (0x01 << 12)
+#define LVDS_PWRDN (0x01 << 15)
+
+struct rk32_lvds {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk; /*phb clk*/
+ struct rk_screen screen;
+};
+
+static int inline lvds_writel(struct rk32_lvds *lvds, u32 offset, u32 val)
+{
+ writel_relaxed(val, lvds->regs + offset);
+ //if (lvds->screen.type == SCREEN_DUAL_LVDS)
+ writel_relaxed(val, lvds->regs + offset + 0x100);
+ return 0;
+}
+#endif
* \param fmt printf() like format string.
* \param arg arguments
*/
-#if DRM_DEBUG_CODE
+#if 0// DRM_DEBUG_CODE
#define DRM_DEBUG(fmt, args...) \
do { \
drm_ut_debug_printk(DRM_UT_CORE, DRM_NAME, \
--- /dev/null
+/* rockchip_drm.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.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.
+ */
+#ifndef _rockchip_DRM_H_
+#define _rockchip_DRM_H_
+
+#include <uapi/drm/rockchip_drm.h>
+
+/**
+ * A structure for lcd panel information.
+ *
+ * @timing: default video mode for initializing
+ * @width_mm: physical size of lcd width.
+ * @height_mm: physical size of lcd height.
+ */
+struct rockchip_drm_panel_info {
+ struct fb_videomode timing;
+ u32 width_mm;
+ u32 height_mm;
+};
+
+/**
+ * Platform Specific Structure for DRM based FIMD.
+ *
+ * @panel: default panel info for initializing
+ * @default_win: default window layer number to be used for UI.
+ * @bpp: default bit per pixel.
+ */
+struct rockchip_drm_fimd_pdata {
+ struct rockchip_drm_panel_info panel;
+ u32 vidcon0;
+ u32 vidcon1;
+ unsigned int default_win;
+ unsigned int bpp;
+};
+
+/**
+ * Platform Specific Structure for DRM based HDMI.
+ *
+ * @hdmi_dev: device point to specific hdmi driver.
+ * @mixer_dev: device point to specific mixer driver.
+ *
+ * this structure is used for common hdmi driver and each device object
+ * would be used to access specific device driver(hdmi or mixer driver)
+ */
+struct rockchip_drm_common_hdmi_pd {
+ struct device *hdmi_dev;
+ struct device *mixer_dev;
+};
+
+/**
+ * Platform Specific Structure for DRM based HDMI core.
+ *
+ * @is_v13: set if hdmi version 13 is.
+ * @cfg_hpd: function pointer to configure hdmi hotplug detection pin
+ * @get_hpd: function pointer to get value of hdmi hotplug detection pin
+ */
+struct rockchip_drm_hdmi_pdata {
+ bool is_v13;
+ void (*cfg_hpd)(bool external);
+ int (*get_hpd)(void);
+};
+
+/**
+ * Platform Specific Structure for DRM based IPP.
+ *
+ * @inv_pclk: if set 1. invert pixel clock
+ * @inv_vsync: if set 1. invert vsync signal for wb
+ * @inv_href: if set 1. invert href signal
+ * @inv_hsync: if set 1. invert hsync signal for wb
+ */
+struct rockchip_drm_ipp_pol {
+ unsigned int inv_pclk;
+ unsigned int inv_vsync;
+ unsigned int inv_href;
+ unsigned int inv_hsync;
+};
+
+/**
+ * Platform Specific Structure for DRM based FIMC.
+ *
+ * @pol: current hardware block polarity settings.
+ * @clk_rate: current hardware clock rate.
+ */
+struct rockchip_drm_fimc_pdata {
+ struct rockchip_drm_ipp_pol pol;
+ int clk_rate;
+};
+
+#endif /* _rockchip_DRM_H_ */
--- /dev/null
+/*
+ *
+ * Copyright (C) ROCKCHIP, Inc.
+ *Author:yzq<yzq@rock-chips.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _UAPI_ROCKCHIP_DRM_H_
+#define _UAPI_ROCKCHIP_DRM_H_
+
+#include <drm/drm.h>
+
+/**
+ * User-desired buffer creation information structure.
+ *
+ * @size: user-desired memory allocation size.
+ * - this size value would be page-aligned internally.
+ * @flags: user request for setting memory type or cache attributes.
+ * @handle: returned a handle to created gem object.
+ * - this handle will be set by gem module of kernel side.
+ */
+struct drm_rockchip_gem_create {
+ uint64_t size;
+ unsigned int flags;
+ unsigned int handle;
+};
+
+/**
+ * A structure for getting buffer offset.
+ *
+ * @handle: a pointer to gem object created.
+ * @pad: just padding to be 64-bit aligned.
+ * @offset: relatived offset value of the memory region allocated.
+ * - this value should be set by user.
+ */
+struct drm_rockchip_gem_map_off {
+ unsigned int handle;
+ unsigned int pad;
+ uint64_t offset;
+};
+
+/**
+ * A structure for mapping buffer.
+ *
+ * @handle: a handle to gem object created.
+ * @pad: just padding to be 64-bit aligned.
+ * @size: memory size to be mapped.
+ * @mapped: having user virtual address mmaped.
+ * - this variable would be filled by exynos gem module
+ * of kernel side with user virtual address which is allocated
+ * by do_mmap().
+ */
+struct drm_rockchip_gem_mmap {
+ unsigned int handle;
+ unsigned int pad;
+ uint64_t size;
+ uint64_t mapped;
+};
+
+/**
+ * A structure to gem information.
+ *
+ * @handle: a handle to gem object created.
+ * @flags: flag value including memory type and cache attribute and
+ * this value would be set by driver.
+ * @size: size to memory region allocated by gem and this size would
+ * be set by driver.
+ */
+struct drm_rockchip_gem_info {
+ unsigned int handle;
+ unsigned int flags;
+ uint64_t size;
+};
+
+/**
+ * A structure for user connection request of virtual display.
+ *
+ * @connection: indicate whether doing connetion or not by user.
+ * @extensions: if this value is 1 then the vidi driver would need additional
+ * 128bytes edid data.
+ * @edid: the edid data pointer from user side.
+ */
+struct drm_rockchip_vidi_connection {
+ unsigned int connection;
+ unsigned int extensions;
+ uint64_t edid;
+};
+
+/* memory type definitions. */
+enum e_drm_rockchip_gem_mem_type {
+ /* Physically Continuous memory and used as default. */
+ ROCKCHIP_BO_CONTIG = 0 << 0,
+ /* Physically Non-Continuous memory. */
+ ROCKCHIP_BO_NONCONTIG = 1 << 0,
+ /* non-cachable mapping and used as default. */
+ ROCKCHIP_BO_NONCACHABLE = 0 << 1,
+ /* cachable mapping. */
+ ROCKCHIP_BO_CACHABLE = 1 << 1,
+ /* write-combine mapping. */
+ ROCKCHIP_BO_WC = 1 << 2,
+ ROCKCHIP_BO_MASK = ROCKCHIP_BO_NONCONTIG | ROCKCHIP_BO_CACHABLE |
+ ROCKCHIP_BO_WC
+};
+
+struct drm_rockchip_g2d_get_ver {
+ __u32 major;
+ __u32 minor;
+};
+
+struct drm_rockchip_g2d_cmd {
+ __u32 offset;
+ __u32 data;
+};
+
+enum drm_rockchip_g2d_buf_type {
+ G2D_BUF_USERPTR = 1 << 31,
+};
+
+enum drm_rockchip_g2d_event_type {
+ G2D_EVENT_NOT,
+ G2D_EVENT_NONSTOP,
+ G2D_EVENT_STOP, /* not yet */
+};
+
+struct drm_rockchip_g2d_userptr {
+ unsigned long userptr;
+ unsigned long size;
+};
+
+struct drm_rockchip_g2d_set_cmdlist {
+ __u64 cmd;
+ __u64 cmd_buf;
+ __u32 cmd_nr;
+ __u32 cmd_buf_nr;
+
+ /* for g2d event */
+ __u64 event_type;
+ __u64 user_data;
+};
+
+struct drm_rockchip_g2d_exec {
+ __u64 async;
+};
+
+enum drm_rockchip_ops_id {
+ ROCKCHIP_DRM_OPS_SRC,
+ ROCKCHIP_DRM_OPS_DST,
+ ROCKCHIP_DRM_OPS_MAX,
+};
+
+struct drm_rockchip_sz {
+ __u32 hsize;
+ __u32 vsize;
+};
+
+struct drm_rockchip_pos {
+ __u32 x;
+ __u32 y;
+ __u32 w;
+ __u32 h;
+};
+
+enum drm_rockchip_flip {
+ ROCKCHIP_DRM_FLIP_NONE = (0 << 0),
+ ROCKCHIP_DRM_FLIP_VERTICAL = (1 << 0),
+ ROCKCHIP_DRM_FLIP_HORIZONTAL = (1 << 1),
+ ROCKCHIP_DRM_FLIP_BOTH = ROCKCHIP_DRM_FLIP_VERTICAL |
+ ROCKCHIP_DRM_FLIP_HORIZONTAL,
+};
+
+enum drm_rockchip_degree {
+ ROCKCHIP_DRM_DEGREE_0,
+ ROCKCHIP_DRM_DEGREE_90,
+ ROCKCHIP_DRM_DEGREE_180,
+ ROCKCHIP_DRM_DEGREE_270,
+};
+
+enum drm_rockchip_planer {
+ ROCKCHIP_DRM_PLANAR_Y,
+ ROCKCHIP_DRM_PLANAR_CB,
+ ROCKCHIP_DRM_PLANAR_CR,
+ ROCKCHIP_DRM_PLANAR_MAX,
+};
+
+/**
+ * A structure for ipp supported property list.
+ *
+ * @version: version of this structure.
+ * @ipp_id: id of ipp driver.
+ * @count: count of ipp driver.
+ * @writeback: flag of writeback supporting.
+ * @flip: flag of flip supporting.
+ * @degree: flag of degree information.
+ * @csc: flag of csc supporting.
+ * @crop: flag of crop supporting.
+ * @scale: flag of scale supporting.
+ * @refresh_min: min hz of refresh.
+ * @refresh_max: max hz of refresh.
+ * @crop_min: crop min resolution.
+ * @crop_max: crop max resolution.
+ * @scale_min: scale min resolution.
+ * @scale_max: scale max resolution.
+ */
+struct drm_rockchip_ipp_prop_list {
+ __u32 version;
+ __u32 ipp_id;
+ __u32 count;
+ __u32 writeback;
+ __u32 flip;
+ __u32 degree;
+ __u32 csc;
+ __u32 crop;
+ __u32 scale;
+ __u32 refresh_min;
+ __u32 refresh_max;
+ __u32 reserved;
+ struct drm_rockchip_sz crop_min;
+ struct drm_rockchip_sz crop_max;
+ struct drm_rockchip_sz scale_min;
+ struct drm_rockchip_sz scale_max;
+};
+
+/**
+ * A structure for ipp config.
+ *
+ * @ops_id: property of operation directions.
+ * @flip: property of mirror, flip.
+ * @degree: property of rotation degree.
+ * @fmt: property of image format.
+ * @sz: property of image size.
+ * @pos: property of image position(src-cropped,dst-scaler).
+ */
+struct drm_rockchip_ipp_config {
+ enum drm_rockchip_ops_id ops_id;
+ enum drm_rockchip_flip flip;
+ enum drm_rockchip_degree degree;
+ __u32 fmt;
+ struct drm_rockchip_sz sz;
+ struct drm_rockchip_pos pos;
+};
+
+enum drm_rockchip_ipp_cmd {
+ IPP_CMD_NONE,
+ IPP_CMD_M2M,
+ IPP_CMD_WB,
+ IPP_CMD_OUTPUT,
+ IPP_CMD_MAX,
+};
+
+/**
+ * A structure for ipp property.
+ *
+ * @config: source, destination config.
+ * @cmd: definition of command.
+ * @ipp_id: id of ipp driver.
+ * @prop_id: id of property.
+ * @refresh_rate: refresh rate.
+ */
+struct drm_rockchip_ipp_property {
+ struct drm_rockchip_ipp_config config[ROCKCHIP_DRM_OPS_MAX];
+ enum drm_rockchip_ipp_cmd cmd;
+ __u32 ipp_id;
+ __u32 prop_id;
+ __u32 refresh_rate;
+};
+
+enum drm_rockchip_ipp_buf_type {
+ IPP_BUF_ENQUEUE,
+ IPP_BUF_DEQUEUE,
+};
+
+/**
+ * A structure for ipp buffer operations.
+ *
+ * @ops_id: operation directions.
+ * @buf_type: definition of buffer.
+ * @prop_id: id of property.
+ * @buf_id: id of buffer.
+ * @handle: Y, Cb, Cr each planar handle.
+ * @user_data: user data.
+ */
+struct drm_rockchip_ipp_queue_buf {
+ enum drm_rockchip_ops_id ops_id;
+ enum drm_rockchip_ipp_buf_type buf_type;
+ __u32 prop_id;
+ __u32 buf_id;
+ __u32 handle[ROCKCHIP_DRM_PLANAR_MAX];
+ __u32 reserved;
+ __u64 user_data;
+};
+
+enum drm_rockchip_ipp_ctrl {
+ IPP_CTRL_PLAY,
+ IPP_CTRL_STOP,
+ IPP_CTRL_PAUSE,
+ IPP_CTRL_RESUME,
+ IPP_CTRL_MAX,
+};
+
+/**
+ * A structure for ipp start/stop operations.
+ *
+ * @prop_id: id of property.
+ * @ctrl: definition of control.
+ */
+struct drm_rockchip_ipp_cmd_ctrl {
+ __u32 prop_id;
+ enum drm_rockchip_ipp_ctrl ctrl;
+};
+
+#define DRM_ROCKCHIP_GEM_CREATE 0x00
+#define DRM_ROCKCHIP_GEM_MAP_OFFSET 0x01
+#define DRM_ROCKCHIP_GEM_MMAP 0x02
+/* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */
+#define DRM_ROCKCHIP_GEM_GET 0x04
+#define DRM_ROCKCHIP_VIDI_CONNECTION 0x07
+
+/* G2D */
+#define DRM_ROCKCHIP_G2D_GET_VER 0x20
+#define DRM_ROCKCHIP_G2D_SET_CMDLIST 0x21
+#define DRM_ROCKCHIP_G2D_EXEC 0x22
+
+/* IPP - Image Post Processing */
+#define DRM_ROCKCHIP_IPP_GET_PROPERTY 0x30
+#define DRM_ROCKCHIP_IPP_SET_PROPERTY 0x31
+#define DRM_ROCKCHIP_IPP_QUEUE_BUF 0x32
+#define DRM_ROCKCHIP_IPP_CMD_CTRL 0x33
+
+#define DRM_IOCTL_ROCKCHIP_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_ROCKCHIP_GEM_MMAP, struct drm_rockchip_gem_mmap)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_GET DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_ROCKCHIP_GEM_GET, struct drm_rockchip_gem_info)
+
+#define DRM_IOCTL_ROCKCHIP_VIDI_CONNECTION DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_ROCKCHIP_VIDI_CONNECTION, struct drm_rockchip_vidi_connection)
+
+#define DRM_IOCTL_ROCKCHIP_G2D_GET_VER DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_ROCKCHIP_G2D_GET_VER, struct drm_rockchip_g2d_get_ver)
+#define DRM_IOCTL_ROCKCHIP_G2D_SET_CMDLIST DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_ROCKCHIP_G2D_SET_CMDLIST, struct drm_rockchip_g2d_set_cmdlist)
+#define DRM_IOCTL_ROCKCHIP_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_ROCKCHIP_G2D_EXEC, struct drm_rockchip_g2d_exec)
+
+#define DRM_IOCTL_ROCKCHIP_IPP_GET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_ROCKCHIP_IPP_GET_PROPERTY, struct drm_rockchip_ipp_prop_list)
+#define DRM_IOCTL_ROCKCHIP_IPP_SET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_ROCKCHIP_IPP_SET_PROPERTY, struct drm_rockchip_ipp_property)
+#define DRM_IOCTL_ROCKCHIP_IPP_QUEUE_BUF DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_ROCKCHIP_IPP_QUEUE_BUF, struct drm_rockchip_ipp_queue_buf)
+#define DRM_IOCTL_ROCKCHIP_IPP_CMD_CTRL DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_ROCKCHIP_IPP_CMD_CTRL, struct drm_rockchip_ipp_cmd_ctrl)
+
+/* ROCKCHIP specific events */
+#define DRM_ROCKCHIP_G2D_EVENT 0x80000000
+#define DRM_ROCKCHIP_IPP_EVENT 0x80000001
+
+struct drm_rockchip_g2d_event {
+ struct drm_event base;
+ __u64 user_data;
+ __u32 tv_sec;
+ __u32 tv_usec;
+ __u32 cmdlist_no;
+ __u32 reserved;
+};
+
+struct drm_rockchip_ipp_event {
+ struct drm_event base;
+ __u64 user_data;
+ __u32 tv_sec;
+ __u32 tv_usec;
+ __u32 prop_id;
+ __u32 reserved;
+ __u32 buf_id[ROCKCHIP_DRM_OPS_MAX];
+};
+
+#endif /* _UAPI_ROCKCHIP_DRM_H_ */
struct timing_entry vsync_len; /* ver. sync len */
enum display_flags flags; /* display flags */
-#if defined(CONFIG_FB_ROCKCHIP)
+#if defined(CONFIG_FB_ROCKCHIP) || defined(CONFIG_DRM_ROCKCHIP)
u16 screen_type; /*screen type*/
u16 lvds_format; /*lvds data format for lvds screen*/
u16 face; /*display output interface format:24bit 18bit 16bit*/