[PATCH] ARM: dove: add a kms/drm driver
Jean-Francois Moine
moinejf at free.fr
Thu May 16 07:28:22 EDT 2013
This patch adds a KMS/DRM video driver for the LCD and display
controllers of the Marvell's Dove SoC.
Signed-off-by: Jean-Francois Moine <moinejf at free.fr>
---
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/dove/Kconfig | 10 +
drivers/gpu/drm/dove/Makefile | 6 +
drivers/gpu/drm/dove/dove_crtc.c | 1378 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/dove/dove_dcon.h | 64 +++
drivers/gpu/drm/dove/dove_drv.c | 380 +++++++++++++
drivers/gpu/drm/dove/dove_drv.h | 93 ++++
drivers/gpu/drm/dove/dove_ec.c | 570 ++++++++++++++++++++
drivers/gpu/drm/dove/dove_lcd.h | 519 ++++++++++++++++++
10 files changed, 3023 insertions(+)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index b16c50e..c6e4f4f 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -220,3 +220,5 @@ source "drivers/gpu/drm/omapdrm/Kconfig"
source "drivers/gpu/drm/tilcdc/Kconfig"
source "drivers/gpu/drm/qxl/Kconfig"
+
+source "drivers/gpu/drm/dove/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 1c9f243..6428683 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -52,4 +52,5 @@ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
obj-$(CONFIG_DRM_OMAP) += omapdrm/
obj-$(CONFIG_DRM_TILCDC) += tilcdc/
obj-$(CONFIG_DRM_QXL) += qxl/
+obj-$(CONFIG_DRM_DOVE) += dove/
obj-y += i2c/
diff --git a/drivers/gpu/drm/dove/Kconfig b/drivers/gpu/drm/dove/Kconfig
new file mode 100644
index 0000000..465dc4d
--- /dev/null
+++ b/drivers/gpu/drm/dove/Kconfig
@@ -0,0 +1,10 @@
+config DRM_DOVE
+ tristate "DRM Support for Marvell Dove"
+ depends on DRM && ARCH_DOVE
+ depends on OF
+ select DRM_KMS_HELPER
+ select DRM_KMS_CMA_HELPER
+ select DRM_GEM_CMA_HELPER
+ help
+ Choose this option if you have a Marvell Dove chipset.
+ If M is selected the module will be called dove-drm.
diff --git a/drivers/gpu/drm/dove/Makefile b/drivers/gpu/drm/dove/Makefile
new file mode 100644
index 0000000..3758a19
--- /dev/null
+++ b/drivers/gpu/drm/dove/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Marvell Dove's DRM device driver
+#
+
+dove-drm-objs := dove_drv.o dove_crtc.o dove_ec.o
+obj-$(CONFIG_DRM_DOVE) += dove-drm.o
diff --git a/drivers/gpu/drm/dove/dove_crtc.c b/drivers/gpu/drm/dove/dove_crtc.c
new file mode 100644
index 0000000..9e397e7
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_crtc.c
@@ -0,0 +1,1378 @@
+/*
+ * Marvell Dove DRM driver - CRTC
+ *
+ * Copyright (C) 2013
+ * Jean-Francois Moine <moinejf at free.fr>
+ * Sebastian Hesselbarth <sebastian.hesselbarth at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/of_irq.h>
+
+#include "dove_drv.h"
+#include "dove_lcd.h"
+
+#define DOVE_LCD_REG_BASE_MASK 0xfffff
+#define DOVE_LCD0_REG_BASE 0x20000
+#define DOVE_LCD1_REG_BASE 0x10000
+
+#define to_dove_lcd(x) container_of(x, struct dove_lcd, crtc)
+
+static inline void dove_write(struct dove_lcd *dove_lcd, u32 reg, u32 data)
+{
+ writel(data, dove_lcd->mmio + reg);
+}
+static inline u32 dove_read(struct dove_lcd *dove_lcd, u32 reg)
+{
+ return readl(dove_lcd->mmio + reg);
+}
+static inline void dove_set(struct dove_lcd *dove_lcd, u32 reg, u32 mask)
+{
+ dove_write(dove_lcd, reg, dove_read(dove_lcd, reg) | mask);
+}
+static inline void dove_clear(struct dove_lcd *dove_lcd, u32 reg, u32 mask)
+{
+ dove_write(dove_lcd, reg, dove_read(dove_lcd, reg) & ~mask);
+}
+
+/*
+ * vertical blank functions
+ */
+u32 dove_vblank_count(struct drm_device *dev, int crtc)
+{
+ struct dove_lcd *dove_lcd = dove_drm.lcds[crtc];
+
+ return STA_GRA_FRAME_COUNT(dove_read(dove_lcd, SPU_IRQ_ISR));
+}
+
+int dove_enable_vblank(struct drm_device *dev, int crtc)
+{
+ struct dove_lcd *dove_lcd = dove_drm.lcds[crtc];
+
+#ifdef HANDLE_INTERLACE
+ dove_lcd->vblank_enabled = 1;
+#endif
+ dove_set(dove_lcd, SPU_IRQ_ENA, IRQ_GRA_FRAME_DONE);
+ return 0;
+}
+
+void dove_disable_vblank(struct drm_device *dev, int crtc)
+{
+ struct dove_lcd *dove_lcd = dove_drm.lcds[crtc];
+
+#ifdef HANDLE_INTERLACE
+ dove_lcd->vblank_enabled = 0;
+ if (!dove_lcd->v_sync0)
+#endif
+ dove_clear(dove_lcd, SPU_IRQ_ENA, IRQ_GRA_FRAME_DONE);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int dove_lcd_regs_show(struct seq_file *m,
+ struct dove_lcd *dove_lcd)
+{
+ u32 x, shl, shh, total_v, total_h, active_h, active_v;
+ u32 orig_buff_x, orig_buff_y, zoomed_x, zoomed_y;
+ unsigned i;
+
+ seq_printf(m, "\t\t*** LCD %d ***\n", dove_lcd->num);
+
+ /* Get resolution */
+ x = dove_read(dove_lcd, LCD_SPU_V_H_ACTIVE);
+ active_h = H_LCD(x);
+ active_v = V_LCD(x);
+
+ /* Get total line */
+ x = dove_read(dove_lcd, LCD_SPUT_V_H_TOTAL);
+ total_h = H_LCD(x);
+ total_v = V_LCD(x);
+ seq_printf(m, "----total-------------------------<%4dx%4d>"
+ "-------------------------\n"
+ "----active--------------|", total_h, total_v);
+
+ /* Get H Timings */
+ x = dove_read(dove_lcd, LCD_SPU_H_PORCH);
+ shl = F_LCD(x);
+ shh = B_LCD(x);
+ seq_printf(m, "->front porch(%d)->hsync(%d)->back porch(%d)\n",
+ shl, total_h - shl -shh - active_h, shh);
+
+ seq_printf(m, "|\t\t\t|\n"
+ "|\t\t\t|\n"
+ "|\t<%4dx%4d>\t|\n"
+ "|\t\t\t|\n"
+ "|\t\t\t|\n"
+ "------------------------|\n", active_h, active_v);
+
+ /* Get V Timings */
+ x = dove_read(dove_lcd, LCD_SPU_V_PORCH);
+ shl = F_LCD(x);
+ shh = B_LCD(x);
+ seq_printf(m, "|\n|front porch(%d)\n|vsync(%d)\n|back porch(%d)\n",
+ shl, total_v - shl - shh - active_v, shh);
+ seq_printf(m, "----------------------------------"
+ "-----------------------------------\n");
+
+ /* Get Line Pitch */
+ x = dove_read(dove_lcd, LCD_CFG_GRA_PITCH);
+ shl = x & 0x0000ffff;
+ seq_printf(m, "gfx line pitch in memory is <%d>\n",
+ shl);
+
+ /* Get scaling info */
+ x = dove_read(dove_lcd, LCD_SPU_GRA_HPXL_VLN);
+ orig_buff_x = H_LCD(x);
+ orig_buff_y = V_LCD(x);
+ x = dove_read(dove_lcd, LCD_SPU_GZM_HPXL_VLN);
+ zoomed_x = H_LCD(x);
+ zoomed_y = V_LCD(x);
+ seq_printf(m, "Scaled from <%dx%d> to <%dx%d>\n",
+ orig_buff_x, orig_buff_y, zoomed_x, zoomed_y);
+
+ seq_printf(m, "======================================\n");
+
+ for (i = 0x0080; i <= 0x01c4; i += 4) {
+ x = dove_read(dove_lcd, i);
+ seq_printf(m, "0x%04x 0x%08x\n", i, x);
+ }
+ return 0;
+}
+
+static int dove_regs_show(struct seq_file *m, void *arg)
+{
+ struct dove_lcd *dove_lcd;
+ unsigned i;
+
+ for (i = 0; i < MAX_DOVE_LCD; i++) {
+ dove_lcd = dove_drm.lcds[i];
+ if (dove_lcd)
+ dove_lcd_regs_show(m, dove_lcd);
+ }
+ return 0;
+}
+
+static struct drm_info_list dove_debugfs_list[] = {
+ { "lcd", dove_regs_show, 0 },
+ { "fb", drm_fb_cma_debugfs_show, 0 },
+};
+
+int dove_debugfs_init(struct drm_minor *minor)
+{
+ struct drm_device *dev = minor->dev;
+ int ret;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ ret = drm_debugfs_create_files(dove_debugfs_list,
+ ARRAY_SIZE(dove_debugfs_list),
+ minor->debugfs_root, minor);
+ if (ret)
+ dev_err(dev->dev, "could not install dove_debugfs_list\n");
+
+ return ret;
+}
+
+void dove_debugfs_cleanup(struct drm_minor *minor)
+{
+ drm_debugfs_remove_files(dove_debugfs_list,
+ ARRAY_SIZE(dove_debugfs_list), minor);
+}
+#endif
+
+static void dove_update_base(struct dove_lcd *dove_lcd)
+{
+ struct drm_crtc *crtc = &dove_lcd->crtc;
+ struct drm_framebuffer *fb = crtc->fb;
+ struct drm_gem_cma_object *gem;
+ unsigned int depth, bpp;
+ dma_addr_t start;
+
+ drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
+ gem = drm_fb_cma_get_gem_obj(fb, 0);
+ start = gem->paddr + fb->offsets[0] +
+ crtc->y * fb->pitches[0] +
+ crtc->x * bpp / 8;
+
+ dove_write(dove_lcd, LCD_CFG_GRA_START_ADDR0, start);
+#ifdef HANDLE_INTERLACE
+ if (dove_lcd->crtc.mode.mode->flags & DRM_MODE_FLAG_INTERLACE) {
+ dove_write(dove_lcd, LCD_CFG_GRA_START_ADDR1,
+ start + fb->pitches[0]);
+ dove_write(dove_lcd, LCD_CFG_GRA_PITCH, fb->pitches[0] * 2);
+ return;
+ }
+#endif
+ dove_write(dove_lcd, LCD_CFG_GRA_START_ADDR1, start);
+ dove_write(dove_lcd, LCD_CFG_GRA_PITCH, fb->pitches[0]);
+}
+
+static void set_frame_timings(struct dove_lcd *dove_lcd)
+{
+ struct drm_crtc *crtc = &dove_lcd->crtc;
+ const struct drm_display_mode *mode = &crtc->mode;
+ u32 h_active, v_active, h_orig, v_orig, h_zoom, v_zoom;
+ u32 hfp, hbp, vfp, vbp, hs, vs, v_total;
+ u32 x;
+
+ /*
+ * Calc active size, zoomed size, porch.
+ */
+ h_active = h_zoom = mode->hdisplay;
+ v_active = v_zoom = mode->vdisplay;
+ hfp = mode->hsync_start - mode->hdisplay;
+ hbp = mode->htotal - mode->hsync_end;
+ vfp = mode->vsync_start - mode->vdisplay;
+ vbp = mode->vtotal - mode->vsync_end;
+ hs = mode->hsync_end - mode->hsync_start;
+ vs = mode->vsync_end - mode->vsync_start;
+
+ /*
+ * Calc original size.
+ */
+ h_orig = h_active;
+ v_orig = v_active;
+
+#ifdef HANDLE_INTERLACE
+ /* interlaced workaround */
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+ v_active /= 2;
+ v_zoom /= 2;
+ v_orig /= 2;
+ }
+#endif
+
+ /* calc total width and height */
+ v_total = v_active + vfp + vs + vbp;
+
+ /* apply setting to registers */
+ dove_write(dove_lcd, LCD_SPU_V_H_ACTIVE, LCD_H_V(h_active, v_active));
+ dove_write(dove_lcd, LCD_SPU_GRA_HPXL_VLN, LCD_H_V(h_orig, v_orig));
+ dove_write(dove_lcd, LCD_SPU_GZM_HPXL_VLN, LCD_H_V(h_zoom, v_zoom));
+ dove_write(dove_lcd, LCD_SPU_H_PORCH, LCD_F_B(hfp, hbp));
+ dove_write(dove_lcd, LCD_SPU_V_PORCH, LCD_F_B(vfp, vbp));
+ dove_write(dove_lcd, LCD_SPUT_V_H_TOTAL,
+ LCD_H_V(mode->htotal, v_total));
+
+ /* configure vsync adjust logic */
+ x = dove_read(dove_lcd, LCD_TV_CONTROL1);
+ x &= ~(VSYNC_L_OFFSET_MASK | VSYNC_H_OFFSET_MASK);
+ x |= VSYNC_OFFSET_EN | /* VSYNC adjust enable */
+ VSYNC_L_OFFSET(h_active + hfp) |
+ VSYNC_H_OFFSET(h_active + hfp);
+#ifdef HANDLE_INTERLACE
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+ dove_lcd->v_sync0 = VSYNC_L_OFFSET(h_active + hfp) |
+ VSYNC_H_OFFSET(h_active + hfp);
+ dove_lcd->v_sync1 = VSYNC_L_OFFSET(h_active / 2 + hfp) |
+ VSYNC_H_OFFSET(h_active / 2 + hfp);
+ } else {
+ dove_lcd->v_sync0 = 0;
+ }
+#endif
+ dove_write(dove_lcd, LCD_TV_CONTROL1, x);
+}
+
+static int dove_set_clock(struct dove_lcd *dove_lcd)
+{
+ struct drm_crtc *crtc = &dove_lcd->crtc;
+ const struct drm_display_mode *mode = &crtc->mode;
+ struct clk *clk;
+ u32 x, needed_pixclk, ref_clk, div, fract;
+ int clk_src;
+
+ fract = 0;
+ needed_pixclk = mode->clock * 1000;
+#ifdef HANDLE_INTERLACE
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ needed_pixclk /= 2;
+#endif
+
+ /* first check if pixclk is multiple of current clock */
+ clk_src = dove_lcd->clk_src;
+ clk = dove_lcd->clk[clk_src];
+ ref_clk = clk_get_rate(clk);
+
+ DRM_DEBUG_DRIVER("clk src %d rate %u needed %u div %u mod %u\n",
+ clk_src, ref_clk, needed_pixclk,
+ ref_clk / needed_pixclk, ref_clk % needed_pixclk);
+
+ if (ref_clk % needed_pixclk == 0) {
+ div = ref_clk / needed_pixclk;
+ goto set_clock;
+ }
+
+ /* try to set current clock to requested pixclk */
+ clk_set_rate(clk, needed_pixclk);
+ ref_clk = clk_get_rate(clk);
+ if (ref_clk == needed_pixclk) {
+ div = 1;
+ goto set_clock;
+ }
+
+ /* check if any other clock can set pixclk directly */
+ for (clk_src = 0; clk_src < MAX_CLK; clk_src++) {
+ clk = dove_lcd->clk[clk_src];
+ if (!clk)
+ continue;
+
+ /* try to set clock to requested pixclk */
+ clk_set_rate(clk, needed_pixclk);
+ ref_clk = clk_get_rate(clk);
+
+ if (ref_clk % needed_pixclk == 0) {
+ div = ref_clk / needed_pixclk;
+ goto set_clock;
+ }
+ }
+
+ /* fall back to default fix clock source LCD or AXI */
+ if (dove_lcd->clk[SCLK_SRC_PLLDIV])
+ clk_src = SCLK_SRC_PLLDIV;
+ else
+ clk_src = SCLK_SRC_AXI;
+ clk = dove_lcd->clk[clk_src];
+
+ clk_set_rate(clk, needed_pixclk);
+ ref_clk = clk_get_rate(clk);
+
+ /* use internal divider */
+ if (false) {
+/*fixme: does not work*/
+ ref_clk /= 1000;
+ needed_pixclk /= 1000;
+ x = (ref_clk * 0x1000 + needed_pixclk - 1) / needed_pixclk;
+ div = x >> 12;
+ if (div < 1)
+ div = 1;
+ else
+ fract = x & 0xfff;
+ } else {
+ div = (ref_clk + needed_pixclk - 1) / needed_pixclk;
+ if (div < 1)
+ div = 1;
+ }
+
+set_clock:
+ dove_lcd->clk_src = clk_src;
+ DRM_DEBUG_DRIVER("set clk src %d ref %u div %u fract %u needed %u\n",
+ clk_src, ref_clk, div, fract, needed_pixclk);
+ x = SET_SCLK(clk_src, div, fract);
+ dove_write(dove_lcd, LCD_CFG_SCLK_DIV, x);
+ return 0;
+}
+
+static void set_dma_control(struct dove_lcd *dove_lcd)
+{
+ const struct drm_display_mode *mode = &dove_lcd->crtc.mode;
+ u32 x;
+ int fmt, rbswap;
+
+ rbswap = 1; /* default */
+ switch (dove_lcd->crtc.fb->pixel_format) {
+ case DRM_FORMAT_BGR888:
+ rbswap = 0;
+ case DRM_FORMAT_RGB888:
+ fmt = GMODE_RGB888PACKED;
+ break;
+ case DRM_FORMAT_XBGR8888:
+ rbswap = 0;
+ case DRM_FORMAT_XRGB8888: /* depth 24 */
+ fmt = GMODE_RGBA888;
+ break;
+ case DRM_FORMAT_ABGR8888:
+ rbswap = 0;
+ case DRM_FORMAT_ARGB8888: /* depth 32 */
+ fmt = GMODE_RGB888UNPACKED;
+ break;
+ case DRM_FORMAT_YVYU:
+ rbswap = 0;
+ case DRM_FORMAT_YUYV:
+ fmt = GMODE_YUV422PACKED;
+ break;
+ case DRM_FORMAT_YVU422:
+ rbswap = 0;
+ case DRM_FORMAT_YUV422:
+ fmt = GMODE_YUV422PLANAR;
+ break;
+ case DRM_FORMAT_YVU420:
+ rbswap = 0;
+ default:
+/* case DRM_FORMAT_YUV420: */
+ fmt = GMODE_YUV420PLANAR;
+ break;
+ }
+
+ x = dove_read(dove_lcd, LCD_SPU_DMA_CTRL0);
+ x &= ~(CFG_PALETTE_ENA | /* true color */
+ CFG_GRAFORMAT_MASK |
+ CFG_GRA_SWAPRB |
+ CFG_GRA_FTOGGLE);
+ x |= CFG_GRA_ENA | /* graphic enable */
+ CFG_GRA_HSMOOTH; /* horiz. smooth scaling */
+ x |= CFG_GRAFORMAT(fmt);
+
+ if (!rbswap)
+ x |= CFG_GRA_SWAPRB;
+#ifdef HANDLE_INTERLACE
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ x |= CFG_GRA_FTOGGLE;
+#endif
+ dove_write(dove_lcd, LCD_SPU_DMA_CTRL0, x);
+
+ /*
+ * trigger DMA on the falling edge of vsync if vsync is
+ * active low, or on the rising edge if vsync is active high
+ */
+ if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
+ dove_set(dove_lcd, LCD_SPU_DMA_CTRL1, CFG_VSYNC_INV);
+}
+
+/* this function is called on mode DRM_MODE_DPMS_ON
+ * and also at loading time with gpio_only set */
+static void set_dumb_panel_control(struct dove_lcd *dove_lcd,
+ int gpio_only)
+{
+ const struct drm_display_mode *mode = &dove_lcd->crtc.mode;
+ u32 x;
+
+ x = 0;
+ if (dove_lcd->dpms == DRM_MODE_DPMS_ON)
+ x = CFG_DUMB_ENA;
+ if (!gpio_only) {
+ if (dove_lcd->dpms == DRM_MODE_DPMS_ON)
+ /*
+ * When dumb interface isn't under 24bit
+ * It might be under SPI or GPIO. If set
+ * to 0x7 will force LCD_D[23:0] output
+ * blank color and damage GPIO and SPI
+ * behavior.
+ */
+ x |= CFG_DUMBMODE(DUMB24_RGB888_0);
+ else
+ x |= CFG_DUMBMODE(7);
+/*fixme
+ if (mode->flags & FB_SYNC_COMP_HIGH_ACT)
+ x |= CFG_INV_COMPSYNC;
+*/
+ if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
+ x |= CFG_INV_VSYNC;
+
+ /* Following is a weired workaround. This bit shouldn't be set
+ * For now, if it's 1080p or 720 then don't set HOR_HIGH_ACT */
+ if ((mode->hdisplay == 1920 && mode->vdisplay == 1080) ||
+ (mode->hdisplay == 1280 && mode->vdisplay == 720))
+ /* Do nothing */
+ ;
+ else
+ if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
+ x |= CFG_INV_HSYNC;
+ }
+
+ dove_write(dove_lcd, LCD_SPU_DUMB_CTRL, x);
+}
+
+void dove_crtc_start(struct dove_lcd *dove_lcd)
+{
+ struct drm_crtc *crtc = &dove_lcd->crtc;
+ struct drm_display_mode *mode = &crtc->mode;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ if (mode->clock == 0) {
+ dev_err(dove_lcd->dev, "crtc_start: no clock!\n");
+ dove_lcd->dpms = DRM_MODE_DPMS_OFF;
+ return;
+ }
+
+ set_frame_timings(dove_lcd);
+ if (dove_set_clock(dove_lcd) < 0)
+ return;
+ set_dma_control(dove_lcd);
+ dove_update_base(dove_lcd);
+ set_dumb_panel_control(dove_lcd, 0);
+
+#ifdef HANDLE_INTERLACE
+ if (dove_lcd->v_sync0) { /* interlace mode on */
+ dove_set(dove_lcd, SPU_IRQ_ENA, IRQ_GRA_FRAME_DONE);
+ } else { /* interlace mode off */
+ if (!dove_lcd->vblank_enabled)
+ dove_clear(dove_lcd, SPU_IRQ_ENA, IRQ_GRA_FRAME_DONE);
+ }
+#endif
+
+ DRM_DEBUG_DRIVER("start %s@%d\n",
+ crtc->mode.name, crtc->mode.vrefresh);
+}
+
+void dove_crtc_stop(struct dove_lcd *dove_lcd)
+{
+ DRM_DEBUG_DRIVER("\n");
+
+ dove_clear(dove_lcd, LCD_SPU_DMA_CTRL0, CFG_GRA_ENA);
+ dove_clear(dove_lcd, LCD_SPU_DUMB_CTRL, CFG_DUMB_ENA);
+#ifdef HANDLE_INTERLACE
+ if (dove_lcd->v_sync0
+ && !dove_lcd->vblank_enabled)
+ dove_clear(dove_lcd, SPU_IRQ_ENA, IRQ_GRA_FRAME_DONE);
+#endif
+}
+
+/* -----------------------------------------------------------------------------
+ * cursor
+ */
+
+/* load the hardware cursor */
+static int load_cursor(struct dove_lcd *dove_lcd,
+ struct drm_file *file_priv,
+ uint32_t handle,
+ int data_len)
+{
+ struct drm_gem_object *obj;
+ struct drm_gem_cma_object *cma_obj;
+ u8 *p_pixel;
+ u32 u, val;
+ u32 ram, color;
+ int i, j, ret;
+
+ obj = drm_gem_object_lookup(dove_drm.drm, file_priv, handle);
+ if (!obj)
+ return -ENOENT;
+
+ if (!obj->map_list.map) {
+ dev_warn(dove_lcd->dev, "cursor not mapped\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (data_len != obj->size) {
+ dev_warn(dove_lcd->dev, "bad cursor size\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cma_obj = to_drm_gem_cma_obj(obj);
+ p_pixel = cma_obj->vaddr;
+
+ u = CFG_SRAM_INIT_WR_RD(SRAMID_INIT_WRITE) |
+ CFG_SRAM_ADDR_LCDID(SRAMID_HWC);
+ ram = CFG_SRAM_INIT_WR_RD(SRAMID_INIT_WRITE);
+
+ /* load the RGBA cursor to SRAM */
+ for (i = 0; i < data_len / 4 / 4; i++) {
+ color = (p_pixel[3 * 4 + 0] << 24) | /* red */
+ (p_pixel[2 * 4 + 0] << 16) |
+ (p_pixel[1 * 4 + 0] << 8) |
+ p_pixel[0];
+ dove_write(dove_lcd, LCD_SPU_SRAM_WRDAT, color);
+ dove_write(dove_lcd, LCD_SPU_SRAM_CTRL,
+ ram | CFG_SRAM_ADDR_LCDID(SRAMID_HWC32_RAM1));
+ color = (p_pixel[3 * 4 + 1] << 24) | /* green */
+ (p_pixel[2 * 4 + 1] << 16) |
+ (p_pixel[1 * 4 + 1] << 8) |
+ p_pixel[1];
+ dove_write(dove_lcd, LCD_SPU_SRAM_WRDAT, color);
+ dove_write(dove_lcd, LCD_SPU_SRAM_CTRL,
+ ram | CFG_SRAM_ADDR_LCDID(SRAMID_HWC32_RAM2));
+ color = (p_pixel[3 * 4 + 2] << 24) | /* blue */
+ (p_pixel[2 * 4 + 2] << 16) |
+ (p_pixel[1 * 4 + 2] << 8) |
+ p_pixel[2];
+ dove_write(dove_lcd, LCD_SPU_SRAM_WRDAT, color);
+ dove_write(dove_lcd, LCD_SPU_SRAM_CTRL,
+ ram | CFG_SRAM_ADDR_LCDID(SRAMID_HWC32_RAM3));
+ p_pixel += 4 * 4;
+ if ((++ram & 0xff) == 0) {
+ ram -= 0x100; /* I[7:0] */
+ ram += 1 << 12; /* J[1:0] */
+ }
+ }
+
+ /* set the transparency */
+ p_pixel = cma_obj->vaddr;
+ for (i = 0; i < data_len / 16 / 4; i++) {
+ val = 0;
+ for (j = 16 * 4 - 4; j >= 0 ; j -= 4) {
+ val <<= 2;
+ if (p_pixel[j + 3]) /* alpha */
+ val |= 1; /* not transparent */
+ }
+ dove_write(dove_lcd, LCD_SPU_SRAM_WRDAT, val);
+ dove_write(dove_lcd, LCD_SPU_SRAM_CTRL, u++);
+ p_pixel += 16 * 4;
+ }
+ ret = 0;
+out:
+ drm_gem_object_unreference_unlocked(obj);
+ return ret;
+}
+
+static int dove_cursor_set(struct drm_crtc *crtc,
+ struct drm_file *file_priv,
+ uint32_t handle,
+ uint32_t width,
+ uint32_t height)
+{
+ struct dove_lcd *dove_lcd = to_dove_lcd(crtc);
+ int ret;
+
+ DRM_DEBUG_DRIVER("%dx%d handle %d\n", width, height, handle);
+
+ /* disable cursor */
+ dove_clear(dove_lcd, LCD_SPU_DMA_CTRL0, CFG_HWC_ENA);
+
+ if (!handle)
+ return 0; /* cursor off */
+
+ if (width != 64 || height != 64) {
+ dev_err(dove_lcd->dev, "bad cursor size\n");
+ return -EINVAL;
+ }
+
+ /* load the cursor */
+ ret = load_cursor(dove_lcd, file_priv, handle, width * height * 4);
+ if (ret < 0)
+ return ret;
+
+ /* set cursor size */
+ dove_write(dove_lcd, LCD_SPU_HWC_HPXL_VLN, LCD_H_V(width, height));
+
+ /* enable cursor */
+ dove_set(dove_lcd, LCD_SPU_DMA_CTRL0, CFG_HWC_ENA);
+
+ return 0;
+}
+
+static int dove_cursor_move(struct drm_crtc *crtc,
+ int x, int y)
+{
+ struct dove_lcd *dove_lcd = to_dove_lcd(crtc);
+
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+ dove_clear(dove_lcd, LCD_SPU_DMA_CTRL0, CFG_HWC_ENA);
+ dove_write(dove_lcd, LCD_SPU_HWC_OVSA_HPXL_VLN, LCD_H_V(x, y));
+ dove_set(dove_lcd, LCD_SPU_DMA_CTRL0, CFG_HWC_ENA);
+ return 0;
+}
+
+static void dove_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct dove_lcd *dove_lcd = to_dove_lcd(crtc);
+
+ DRM_DEBUG_DRIVER("\n");
+
+ WARN_ON(dove_lcd->dpms == DRM_MODE_DPMS_ON);
+
+ drm_crtc_cleanup(crtc);
+}
+
+static int dove_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event)
+{
+ struct dove_lcd *dove_lcd = to_dove_lcd(crtc);
+ struct drm_device *drm = dove_drm.drm;
+ unsigned long flags;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ spin_lock_irqsave(&drm->event_lock, flags);
+ if (dove_lcd->event) {
+ spin_unlock_irqrestore(&drm->event_lock, flags);
+ dev_err(drm->dev, "already pending page flip!\n");
+ return -EBUSY;
+ }
+ spin_unlock_irqrestore(&drm->event_lock, flags);
+
+ crtc->fb = fb;
+ dove_update_base(dove_lcd);
+
+ if (event) {
+ event->pipe = 0;
+ spin_lock_irqsave(&drm->event_lock, flags);
+ dove_lcd->event = event;
+ spin_unlock_irqrestore(&drm->event_lock, flags);
+ drm_vblank_get(drm, dove_lcd->num);
+ }
+
+ return 0;
+}
+
+static void dove_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct dove_lcd *dove_lcd = to_dove_lcd(crtc);
+
+ /* we really only care about on or off */
+ if (mode != DRM_MODE_DPMS_ON)
+ mode = DRM_MODE_DPMS_OFF;
+
+ DRM_DEBUG_DRIVER("dpms %s\n", mode == DRM_MODE_DPMS_ON ? "on" : "off");
+
+ if (dove_lcd->dpms == mode)
+ return;
+
+ dove_lcd->dpms = mode;
+
+ if (mode == DRM_MODE_DPMS_ON)
+ dove_crtc_start(dove_lcd);
+ else
+ dove_crtc_stop(dove_lcd);
+}
+
+static bool dove_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ DRM_DEBUG_DRIVER("\n");
+ if (adjusted_mode->vrefresh == 0) {
+/* drm_mode_set_name(adjusted_mode); */
+ adjusted_mode->vrefresh = drm_mode_vrefresh(adjusted_mode);
+ DRM_DEBUG_DRIVER("%s@%d\n",
+ adjusted_mode->name, adjusted_mode->vrefresh);
+ }
+ return true;
+}
+
+static void dove_crtc_prepare(struct drm_crtc *crtc)
+{
+ DRM_DEBUG_DRIVER("\n");
+ dove_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void dove_crtc_commit(struct drm_crtc *crtc)
+{
+ DRM_DEBUG_DRIVER("\n");
+/* dove_crtc_dpms(crtc, DRM_MODE_DPMS_ON); */
+}
+
+static int dove_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)
+{
+ unsigned int bandwidth;
+ DRM_DEBUG_DRIVER("\n");
+
+ if (mode->hdisplay > 2048)
+ return MODE_VIRTUAL_X;
+
+ /* width must be multiple of 16 */
+ if (mode->hdisplay & 0xf)
+ return MODE_VIRTUAL_X;
+
+ if (mode->vdisplay > 2048)
+ return MODE_VIRTUAL_Y;
+
+ /* filter out modes that would require too much memory bandwidth: */
+ bandwidth = mode->hdisplay * mode->vdisplay * drm_mode_vrefresh(mode);
+ if (bandwidth > 1920 * 1080 * 60)
+ return MODE_BAD;
+
+/*fixme: is this useful? */
+ mode = &crtc->mode;
+ if (mode->vrefresh == 0) {
+ drm_mode_set_name(mode);
+ mode->vrefresh = drm_mode_vrefresh(mode);
+ DRM_DEBUG_DRIVER("%s@%d\n", mode->name, mode->vrefresh);
+ }
+ return MODE_OK;
+}
+
+static int dove_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct dove_lcd *dove_lcd = to_dove_lcd(crtc);
+
+ DRM_DEBUG_DRIVER("\n");
+ dove_update_base(dove_lcd);
+ return 0;
+}
+
+/* mandatory drm function */
+static void dove_crtc_load_lut(struct drm_crtc *crtc)
+{
+ DRM_DEBUG_DRIVER("\n");
+}
+
+static const struct drm_crtc_funcs dove_crtc_funcs = {
+ .cursor_set = dove_cursor_set,
+ .cursor_move = dove_cursor_move,
+ .destroy = dove_crtc_destroy,
+ .set_config = drm_crtc_helper_set_config,
+ .page_flip = dove_crtc_page_flip,
+};
+
+static const struct drm_crtc_helper_funcs dove_crtc_helper_funcs = {
+ .dpms = dove_crtc_dpms,
+ .mode_fixup = dove_crtc_mode_fixup,
+ .prepare = dove_crtc_prepare,
+ .commit = dove_crtc_commit,
+ .mode_set = dove_crtc_mode_set,
+ .mode_set_base = dove_crtc_mode_set_base,
+ .load_lut = dove_crtc_load_lut,
+};
+
+void dove_crtc_cancel_page_flip(struct dove_lcd *dove_lcd,
+ struct drm_file *file)
+{
+ struct drm_pending_vblank_event *event;
+ struct drm_device *drm = dove_drm.drm;
+ unsigned long flags;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ /*
+ * Destroy the pending vertical blanking event associated with the
+ * pending page flip, if any, and disable vertical blanking interrupts.
+ */
+ spin_lock_irqsave(&drm->event_lock, flags);
+ event = dove_lcd->event;
+ if (event && event->base.file_priv == file) {
+ dove_lcd->event = NULL;
+ event->base.destroy(&event->base);
+ drm_vblank_put(drm, dove_lcd->num);
+ }
+ spin_unlock_irqrestore(&drm->event_lock, flags);
+}
+
+/* configure default register values */
+static void dove_set_defaults(struct dove_lcd *dove_lcd)
+{
+ u32 x;
+
+ /* set the default clock */
+ if (dove_lcd->clk[SCLK_SRC_PLLDIV])
+ dove_lcd->clk_src = SCLK_SRC_PLLDIV;
+ else
+ dove_lcd->clk_src = SCLK_SRC_AXI;
+ DRM_DEBUG_DRIVER("default clock %d\n", dove_lcd->clk_src);
+
+ x = SET_SCLK(dove_lcd->clk_src, 1, 0);
+ dove_write(dove_lcd, LCD_CFG_SCLK_DIV, x);
+ dove_write(dove_lcd, LCD_SPU_BLANKCOLOR, 0);
+
+ dove_write(dove_lcd, SPU_IOPAD_CONTROL, IOPAD_DUMB24);
+ dove_write(dove_lcd, LCD_CFG_GRA_START_ADDR1, 0);
+ dove_write(dove_lcd, LCD_SPU_GRA_OVSA_HPXL_VLN, 0);
+ dove_write(dove_lcd, LCD_SPU_SRAM_PARA0, 0);
+ dove_write(dove_lcd, LCD_SPU_SRAM_PARA1, CFG_CSB_256x32 |
+ CFG_CSB_256x24 |
+ CFG_CSB_256x8);
+ dove_write(dove_lcd, LCD_SPU_DMA_CTRL1, CFG_VSYNC_TRIG(2) |
+ CFG_GATED_ENA |
+ CFG_PWRDN_ENA |
+ CFG_ALPHA_MODE(2) |
+ CFG_ALPHA(0xff) |
+ CFG_PXLCMD(0x81));
+
+ /*
+ * Fix me: to avoid jiggling issue for high resolution in
+ * dual display, we set watermark to affect LCD AXI read
+ * from MC (default 0x80). Lower watermark means LCD will
+ * do DMA read more often.
+ */
+ x = dove_read(dove_lcd, LCD_CFG_RDREG4F);
+ x &= ~DMA_WATERMARK_MASK;
+ x |= DMA_WATERMARK(0x20);
+
+ /*
+ * Disable LCD SRAM Read Wait State to resolve HWC32 make
+ * system hang while use external clock.
+ */
+ x &= ~LCD_SRAM_WAIT;
+ dove_write(dove_lcd, LCD_CFG_RDREG4F, x);
+
+ /* prepare the hwc32 */
+ dove_set(dove_lcd, LCD_TV_CONTROL1, HWC32_ENABLE);
+
+ /* set hwc32 with 100% static alpha blending factor */
+ dove_write(dove_lcd, LCD_SPU_ALPHA_COLOR1,
+ HWC32_CFG_ALPHA(0xff));
+}
+
+static irqreturn_t dove_lcd_irq(int irq, void *dev_id)
+{
+ struct dove_lcd *dove_lcd = (struct dove_lcd *) dev_id;
+ struct drm_pending_vblank_event *event;
+ struct drm_device *drm = dove_drm.drm;
+ u32 isr;
+ unsigned long flags;
+
+ isr = dove_read(dove_lcd, SPU_IRQ_ISR);
+ dove_write(dove_lcd, SPU_IRQ_ISR, 0);
+
+ DRM_DEBUG_DRIVER("\n");
+
+ if (isr & IRQ_GRA_FRAME_DONE) {
+#ifdef HANDLE_INTERLACE
+ if (dove_lcd->v_sync0) {
+ u32 x;
+
+ x = dove_read(dove_lcd, LCD_TV_CONTROL1);
+ x &= ~(VSYNC_L_OFFSET_MASK | VSYNC_H_OFFSET_MASK);
+ if (isr & IRQ_GRA_FRAME0)
+ x |= dove_lcd->v_sync0;
+ else
+ x |= dove_lcd->v_sync1;
+ dove_write(dove_lcd, LCD_TV_CONTROL1, x);
+ }
+ if (dove_lcd->vblank_enabled)
+#endif
+ drm_handle_vblank(drm, dove_lcd->num);
+ spin_lock_irqsave(&drm->event_lock, flags);
+ event = dove_lcd->event;
+ dove_lcd->event = NULL;
+ if (event)
+ drm_send_vblank_event(drm, dove_lcd->num, event);
+ spin_unlock_irqrestore(&drm->event_lock, flags);
+ if (event)
+ drm_vblank_put(drm, dove_lcd->num);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* initialize a lcd */
+static int dove_crtc_init(struct dove_lcd *dove_lcd)
+{
+ struct drm_crtc *crtc = &dove_lcd->crtc;
+ int ret;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ dove_lcd->dpms = DRM_MODE_DPMS_OFF;
+
+ ret = drm_crtc_init(dove_drm.drm, crtc, &dove_crtc_funcs);
+ if (ret < 0)
+ goto fail;
+
+ dove_write(dove_lcd, SPU_IRQ_ENA, 0); /* disable interrupts */
+ ret = devm_request_irq(dove_lcd->dev, dove_lcd->irq, dove_lcd_irq, 0,
+ dove_lcd->name, dove_lcd);
+ if (ret < 0) {
+ dev_err(dove_lcd->dev, "unable to request irq %d\n",
+ dove_lcd->irq);
+ goto fail;
+ }
+
+ if (ret < 0) {
+ dev_err(dove_lcd->dev, "failed to install IRQ handler\n");
+ goto fail;
+ }
+
+ dove_set_defaults(dove_lcd);
+ set_dumb_panel_control(dove_lcd, 1);
+
+ drm_crtc_helper_add(crtc, &dove_crtc_helper_funcs);
+
+ return 0;
+
+fail:
+ dove_crtc_destroy(crtc);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Overlay plane
+ */
+
+static void plane_update_base(struct dove_lcd *dove_lcd,
+ int plane_num,
+ struct drm_framebuffer *fb,
+ int fmt,
+ int x, int y,
+ int w, int h)
+{
+ struct drm_gem_cma_object *gem;
+ unsigned int addr;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ gem = drm_fb_cma_get_gem_obj(fb, plane_num);
+
+ addr = gem->paddr + fb->offsets[0] + y * fb->pitches[0] + x;
+ dove_write(dove_lcd, LCD_SPU_DMA_START_ADDR_Y0, addr);
+
+ switch (fmt) {
+ case VMODE_YUV422PLANAR:
+ case VMODE_YUV420PLANAR:
+ addr += w * h / 2; /* planar */
+ break;
+ }
+ dove_write(dove_lcd, LCD_SPU_DMA_START_ADDR_U0, addr);
+
+ switch (fmt) {
+ case VMODE_YUV422PLANAR:
+ addr += w * h / 2;
+ break;
+ case VMODE_YUV420PLANAR:
+ addr += w * h / 4;
+ break;
+ }
+ dove_write(dove_lcd, LCD_SPU_DMA_START_ADDR_V0, addr);
+
+ switch (fb->pixel_format) {
+ case VMODE_YUV422PACKED:
+ dove_write(dove_lcd, LCD_SPU_DMA_PITCH_YC,
+ LCD_Y_C(w * 2, 0));
+ dove_write(dove_lcd, LCD_SPU_DMA_PITCH_UV, LCD_U_V(w, w));
+ break;
+ default:
+/* case VMODE_YUV422PLANAR: */
+/* case VMODE_YUV420PLANAR: */
+ dove_write(dove_lcd, LCD_SPU_DMA_PITCH_YC, LCD_Y_C(w, 0));
+ dove_write(dove_lcd, LCD_SPU_DMA_PITCH_UV,
+ LCD_U_V(w / 2, w / 2));
+ break;
+ }
+}
+
+static int dove_plane_update(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 dove_lcd *dove_lcd = to_dove_lcd(crtc);
+ u32 x, x_bk;
+ int fmt, rbswap;
+
+ DRM_DEBUG_DRIVER("%d\n", plane == &dove_lcd->planes[PLANE_VID]);
+
+ if (plane != &dove_lcd->planes[PLANE_VID])
+ return 0;
+ rbswap = 1; /* default */
+ switch (fb->pixel_format) {
+ case DRM_FORMAT_YVYU:
+ rbswap = 0;
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_UYVY:
+ fmt = VMODE_YUV422PACKED;
+ break;
+ case DRM_FORMAT_YVU422:
+ rbswap = 0;
+ case DRM_FORMAT_YUV422:
+ fmt = VMODE_YUV422PLANAR;
+ break;
+ case DRM_FORMAT_YVU420:
+ rbswap = 0;
+ default:
+/* case DRM_FORMAT_YUV420: */
+ fmt = VMODE_YUV420PLANAR;
+ break;
+ }
+
+ x_bk = x = dove_read(dove_lcd, LCD_SPU_DMA_CTRL0);
+ /* clear video layer's field */
+ x &= ~(CFG_YUV2RGB_DMA | CFG_DMA_SWAP_MASK |
+ CFG_DMA_TSTMODE | CFG_DMA_HSMOOTH | CFG_DMA_FTOGGLE |
+ CFG_DMAFORMAT_MASK | CFG_PALETTE_ENA);
+ x |= CFG_DMA_HSMOOTH; /* enable horizontal smooth scaling */
+ x |= CFG_DMAFORMAT(fmt); /* configure hardware pixel format */
+/*fixme: no RGB */
+ if (fb->pixel_format == DRM_FORMAT_UYVY) {
+ x |= CFG_YUV2RGB_DMA;
+ } else if (fmt == VMODE_YUV422PACKED) {
+ x |= CFG_YUV2RGB_DMA |
+ CFG_DMA_SWAPYU |
+ CFG_DMA_SWAPRB;
+ if (rbswap)
+ x |= CFG_DMA_SWAPUV;
+ } else { /* planar */
+ x |= CFG_YUV2RGB_DMA |
+ CFG_DMA_SWAPRB;
+ if (!rbswap)
+ x |= CFG_DMA_SWAPUV;
+ }
+ if (x != x_bk)
+ dove_write(dove_lcd, LCD_SPU_DMA_CTRL0, x);
+
+ /* set the dma addresses */
+ plane_update_base(dove_lcd, PLANE_VID,
+ fb, fmt, src_x, src_y, src_w, src_h);
+
+ /* original size */
+ dove_write(dove_lcd, LCD_SPU_DMA_HPXL_VLN,
+ LCD_H_V(src_w, src_h));
+
+ /* scaled size */
+ dove_write(dove_lcd, LCD_SPU_DZM_HPXL_VLN,
+ LCD_H_V(crtc_w, crtc_h));
+
+ /* update video position offset */
+ dove_write(dove_lcd, LCD_SPUT_DMA_OVSA_HPXL_VLN,
+ LCD_H_V(crtc_x, crtc_y));
+ return 0;
+}
+
+static int dove_plane_disable(struct drm_plane *plane)
+{
+ struct dove_lcd *dove_lcd = to_dove_lcd(plane->crtc);
+
+ DRM_DEBUG_DRIVER("%d\n",
+ plane == &dove_lcd->planes[PLANE_VID]);
+
+ if (plane != &dove_lcd->planes[PLANE_VID])
+ return 0;
+
+ dove_clear(dove_lcd, LCD_SPU_DMA_CTRL0, CFG_DMA_ENA);
+ return 0;
+}
+
+static void dove_plane_destroy(struct drm_plane *plane)
+{
+ dove_plane_disable(plane);
+ drm_plane_cleanup(plane);
+}
+
+static const struct drm_plane_funcs plane_funcs = {
+ .update_plane = dove_plane_update,
+ .disable_plane = dove_plane_disable,
+ .destroy = dove_plane_destroy,
+};
+static const uint32_t gfx_formats[] = {
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_YVYU,
+ DRM_FORMAT_YUYV,
+ DRM_FORMAT_YVU422,
+ DRM_FORMAT_YUV422,
+ DRM_FORMAT_YVU420,
+ DRM_FORMAT_YUV420,
+};
+static const uint32_t vid_formats[] = {
+ DRM_FORMAT_YVYU,
+ DRM_FORMAT_YUYV,
+ DRM_FORMAT_YVU422,
+ DRM_FORMAT_YUV422,
+ DRM_FORMAT_YVU420,
+ DRM_FORMAT_YUV420,
+ DRM_FORMAT_UYVY,
+};
+
+static int dove_planes_init(struct dove_lcd *dove_lcd)
+{
+ struct drm_device *drm = dove_drm.drm;
+ struct drm_plane *plane;
+ int ret;
+
+ if (false) {
+ plane = &dove_lcd->planes[PLANE_VID];
+ ret = drm_plane_init(drm, plane, 1 << dove_lcd->num,
+ &plane_funcs,
+ gfx_formats, ARRAY_SIZE(gfx_formats), true);
+ if (ret < 0)
+ return ret;
+ plane->crtc = &dove_lcd->crtc;
+ }
+ plane = &dove_lcd->planes[PLANE_VID];
+ ret = drm_plane_init(drm, plane, 1 << dove_lcd->num,
+ &plane_funcs,
+ vid_formats, ARRAY_SIZE(vid_formats), false);
+ if (ret < 0)
+ return ret;
+ plane->crtc = &dove_lcd->crtc;
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Initialization
+ */
+
+int dove_lcd_init(struct dove_lcd *dove_lcd)
+{
+ int ret;
+
+ ret = dove_crtc_init(dove_lcd);
+ if (ret < 0)
+ return ret;
+ ret = dove_planes_init(dove_lcd);
+ if (ret < 0)
+ dev_err(dove_lcd->dev, "failed to create the planes\n");
+
+ return ret;
+}
+
+/* at probe time, get the possible LCD clocks */
+static int get_lcd_clocks(struct dove_lcd *dove_lcd)
+{
+ struct device *dev = dove_lcd->dev;
+ struct device_node *np = dev->of_node;
+ struct of_phandle_args clkspec;
+ struct clk *clk;
+ int i, no_clock, ret;
+
+ no_clock = 1;
+ for (i = 0; i < MAX_CLK; i++) {
+
+ /* check first if there is a phandle to the clock */
+ ret = of_parse_phandle_with_args(np,
+ "clocks", "#clock-cells", i,
+ &clkspec);
+ if (ret)
+ continue; /* no defined clock here */
+ of_node_put(clkspec.np);
+
+ /* if no clock driver, ignore this clock */
+ clk = of_clk_get(np, i);
+ if (IS_ERR(clk)) {
+ if (!dove_drm.probe_defer) {
+ dove_drm.probe_defer = 1;
+ return -EPROBE_DEFER;
+ }
+ dev_err(dev, "no driver for clock %i\n", i);
+ continue;
+ }
+ DRM_DEBUG_DRIVER("clock %d ok\n", i);
+ clk_prepare_enable(clk);
+ dove_lcd->clk[i] = clk;
+ no_clock = 0;
+ }
+ if (no_clock) {
+ dev_err(dev, "no available clock\n");
+ return -EINVAL;
+ }
+ if (!dove_lcd->clk[SCLK_SRC_PLLDIV] && !dove_lcd->clk[SCLK_SRC_AXI]) {
+ dev_err(dev, "no fixed clock\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int dove_lcd_remove(struct platform_device *pdev)
+{
+ struct dove_lcd *dove_lcd = platform_get_drvdata(pdev);
+ struct clk *clk;
+ int i;
+
+ dove_write(dove_lcd, SPU_IRQ_ENA, 0); /* disable interrupts */
+
+ if (dove_drm.lcds[dove_lcd->num] == dove_lcd)
+ dove_drm.lcds[dove_lcd->num] = NULL;
+
+ for (i = 0; i < MAX_CLK; i++) {
+ clk = dove_lcd->clk[i];
+ if (clk) {
+ clk_disable_unprepare(clk);
+ clk_put(clk);
+ }
+ }
+
+ kfree(dove_lcd);
+ return 0;
+}
+
+static int dove_lcd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct dove_lcd *dove_lcd;
+ struct resource *res;
+ int ret;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ /* bail out early if no DT data */
+ if (!np) {
+ dev_err(dev, "no device-tree\n");
+ return -ENXIO;
+ }
+
+ dove_lcd = kzalloc(sizeof *dove_lcd, GFP_KERNEL);
+ if (!dove_lcd) {
+ dev_err(dev, "failed to allocate private data\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, dove_lcd);
+ dove_lcd->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get memory resource\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ dove_lcd->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(dove_lcd->mmio)) {
+ dev_err(dev, "failed map registers\n");
+ ret = PTR_ERR(dove_lcd->mmio);
+ dove_lcd->mmio = NULL;
+ goto fail;
+ }
+
+ switch (((u32) dove_lcd->mmio) & DOVE_LCD_REG_BASE_MASK) {
+ case DOVE_LCD0_REG_BASE:
+/* dove_lcd->num = 0; */
+ break;
+ case DOVE_LCD1_REG_BASE:
+ dove_lcd->num = 1;
+ break;
+ default:
+ dev_err(dev, "unknown lcd reg base %08x\n",
+ (u32) dove_lcd->mmio);
+ ret = -EINVAL;
+ goto fail;
+ }
+ snprintf(dove_lcd->name, sizeof dove_lcd->name, "dove-lcd%d",
+ dove_lcd->num);
+ dove_drm.lcds[dove_lcd->num] = dove_lcd;
+
+ dove_lcd->irq = irq_of_parse_and_map(np, 0);
+ if (dove_lcd->irq < 0 || dove_lcd->irq == NO_IRQ) {
+ dev_err(dev, "unable to get irq lcd %d\n", dove_lcd->num);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = get_lcd_clocks(dove_lcd);
+ if (ret < 0)
+ goto fail;
+
+ /* check the presence of a possible external slave encoder */
+ ret = dove_ec_probe(dove_lcd);
+ if (ret < 0)
+ goto fail;
+
+ /* init done, try to initialize the drm driver */
+ return dove_probed();
+
+fail:
+ dove_lcd_remove(pdev);
+ return ret;
+}
+
+static struct of_device_id dove_lcd_of_match[] = {
+ { .compatible = "marvell,dove-lcd" },
+ { },
+};
+struct platform_driver dove_lcd_platform_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "dove-lcd",
+ .of_match_table = dove_lcd_of_match,
+ },
+ .probe = dove_lcd_probe,
+ .remove = dove_lcd_remove,
+};
diff --git a/drivers/gpu/drm/dove/dove_dcon.h b/drivers/gpu/drm/dove/dove_dcon.h
new file mode 100644
index 0000000..da2b99c
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_dcon.h
@@ -0,0 +1,64 @@
+/*
+ * Display controller registers of Marvell DOVE
+ *
+ * Copyright (C) 2013
+ * Jean-Francois Moine <moinejf at free.fr>
+ * Sebastian Hesselbarth <sebastian.hesselbarth at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _DOVE_DCON_H_
+#define _DOVE_DCON_H_
+
+/* ------------< DCON register >------------ */
+
+#define DCON_CTL0 0x0000
+#define VGA_CLK_DISABLE BIT(25)
+#define DCON_CLK_DISABLE BIT(24)
+#define DCON_RST BIT(23)
+#define LCD_Disable BIT(17)
+#define Reverse_Scan BIT(10)
+#define LCD_Port_B_Select_MASK 0x00000300
+#define Port_B_Select_LCD1 0x00000000
+#define Port_B_Select_LCD0 0x00000100
+#define Port_B_Select_A_copy 0x00000300
+#define LCD_Port_A_Select_MASK 0x000000c0
+#define Port_A_Select_LCD 0x00000000
+#define Port_A_Select_OLPC 0x00000040
+#define Port_A_Select_Dual 0x00000080
+#define Port_A_Select_ext 0x000000c0
+#define LBUF_EN BIT(5)
+#define DCON_IRQ_CTL 0x0008
+#define IRQ_Control_MASK 0x00ff0000
+#define DITHER_REG_R 0x0050
+#define DITHER_REG_G 0x0054
+#define DITHER_REG_B 0x0058
+#define DCON_DITHER_PAT_RL 0x0060
+#define DCON_DITHER_PAT_RH 0x0064
+#define DCON_DITHER_PAT_GL 0x0068
+#define DCON_DITHER_PAT_GH 0x006c
+#define DCON_DITHER_PAT_BL 0x0070
+#define DCON_DITHER_PAT_BH 0x0074
+#define VGA_Global 0x0080
+#define VGA_CHA 0x0084
+#define VGA_CHB 0x0088
+#define VGA_CHC 0x008c
+#define VGA_CHA_STA 0x0090
+#define VGA_CHB_STA 0x0094
+#define VGA_CHC_STA 0x0098
+#define CT_LUT_INDEX 0x00a4
+#define CT_LUT_DATA 0x00a8
+#define FTDLL_CTL 0x00c0
+
+#endif /* _DOVE_DCON_H_ */
diff --git a/drivers/gpu/drm/dove/dove_drv.c b/drivers/gpu/drm/dove/dove_drv.c
new file mode 100644
index 0000000..e9e77ad
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_drv.c
@@ -0,0 +1,380 @@
+/*
+ * Marvell Dove DRM driver - main
+ *
+ * Copyright (C) 2013
+ * Jean-Francois Moine <moinejf at free.fr>
+ * Sebastian Hesselbarth <sebastian.hesselbarth at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+
+#include "dove_drv.h"
+
+#define DRIVER_NAME "dove-drm"
+#define DRIVER_DESC "Marvell Dove DRM"
+#define DRIVER_DATE "20130516"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+struct dove_drm dove_drm;
+static struct platform_device *g_pdev;
+static atomic_t probed;
+
+static struct drm_framebuffer *dove_fb_create(struct drm_device *drm,
+ struct drm_file *file_priv,
+ struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ DRM_DEBUG_DRIVER("fmt %.4s\n", (char *) &mode_cmd->pixel_format);
+
+ switch (mode_cmd->pixel_format) {
+ case DRM_FORMAT_BGR888:
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YUV420:
+ break;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+ return drm_fb_cma_create(drm, file_priv, mode_cmd);
+}
+
+static void dove_fb_output_poll_changed(struct drm_device *drm)
+{
+ DRM_DEBUG_DRIVER("fb:%d\n", dove_drm.fbdev != NULL);
+ if (dove_drm.fbdev)
+ drm_fbdev_cma_hotplug_event(dove_drm.fbdev);
+}
+
+static const struct drm_mode_config_funcs mode_config_funcs = {
+ .fb_create = dove_fb_create,
+ .output_poll_changed = dove_fb_output_poll_changed,
+};
+
+/*
+ * DRM operations:
+ */
+static int dove_unload(struct drm_device *drm)
+{
+ struct dove_lcd *dove_lcd;
+ int i;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ for (i = 0; i < MAX_DOVE_LCD; i++) {
+ dove_lcd = dove_drm.lcds[i];
+ if (dove_lcd) {
+ if (dove_lcd->planes[PLANE_VID].dev)
+ drm_plane_cleanup(&dove_lcd->planes[PLANE_VID]);
+ if (dove_lcd->planes[PLANE_GFX].dev)
+ drm_plane_cleanup(&dove_lcd->planes[PLANE_GFX]);
+ }
+ }
+ drm_kms_helper_poll_fini(drm);
+ drm_mode_config_cleanup(drm);
+ drm_vblank_cleanup(drm);
+
+ return 0;
+}
+
+/* this function is called when all LCDs and dcon have been probed */
+static int dove_load(struct drm_device *drm, unsigned long flags)
+{
+ struct platform_device *pdev = drm->platformdev;
+ struct dove_lcd *dove_lcd;
+ int i, ret;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ dove_drm.drm = drm;
+ platform_set_drvdata(pdev, &dove_drm);
+ drm->dev_private = &dove_drm;
+
+ drm_mode_config_init(drm);
+
+/* pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); */
+
+ for (i = 0; i < MAX_DOVE_LCD; i++) {
+ dove_lcd = dove_drm.lcds[i];
+ if (dove_lcd) {
+ ret = dove_lcd_init(dove_lcd);
+ if (ret < 0)
+ goto fail;
+ ret = dove_ec_init(dove_lcd);
+ if (ret < 0)
+ goto fail;
+ }
+ }
+
+ drm->mode_config.min_width = 0;
+ drm->mode_config.min_height = 0;
+ drm->mode_config.max_width = 2048;
+ drm->mode_config.max_height = 2048;
+ drm->mode_config.funcs = &mode_config_funcs;
+
+ ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+ if (ret < 0) {
+ dev_err(drm->dev, "failed to initialize vblank\n");
+ goto fail;
+ }
+
+ dove_drm.fbdev = drm_fbdev_cma_init(drm,
+ 32, /* bpp */
+ drm->mode_config.num_crtc,
+ drm->mode_config.num_connector);
+
+ drm_kms_helper_poll_init(drm);
+ return 0;
+fail:
+ dove_unload(drm);
+ return ret;
+}
+
+static void dove_preclose(struct drm_device *drm, struct drm_file *file)
+{
+ struct dove_lcd *dove_lcd;
+ int i;
+
+ for (i = 0; i < MAX_DOVE_LCD; i++) {
+ dove_lcd = dove_drm.lcds[i];
+ if (dove_lcd)
+ dove_crtc_cancel_page_flip(dove_lcd, file);
+ }
+}
+
+static void dove_lastclose(struct drm_device *drm)
+{
+ drm_fbdev_cma_restore_mode(dove_drm.fbdev);
+}
+
+static int dove_gem_cma_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ if (args->height * args->width * args->bpp == 0) {
+ dev_err(dev->dev, "dumb_create %dx%d bpp %d!\n",
+ args->height, args->width, args->bpp);
+ return -ENOMEM;
+ }
+ return drm_gem_cma_dumb_create(file_priv, dev, args);
+}
+
+static const struct file_operations fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+ .poll = drm_poll,
+ .read = drm_read,
+ .fasync = drm_fasync,
+ .llseek = no_llseek,
+ .mmap = drm_gem_cma_mmap,
+};
+
+static struct drm_driver dove_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_MODESET,
+ .load = dove_load,
+ .unload = dove_unload,
+ .preclose = dove_preclose,
+ .lastclose = dove_lastclose,
+ .get_vblank_counter = dove_vblank_count,
+ .enable_vblank = dove_enable_vblank,
+ .disable_vblank = dove_disable_vblank,
+ .gem_free_object = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+ .dumb_create = dove_gem_cma_dumb_create,
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+ .dumb_destroy = drm_gem_cma_dumb_destroy,
+#ifdef CONFIG_DEBUG_FS
+ .debugfs_init = dove_debugfs_init,
+ .debugfs_cleanup = dove_debugfs_cleanup,
+#endif
+ .fops = &fops,
+
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+};
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * Power management
+ */
+static int dove_pm_suspend(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct dove_lcd *dove_lcd;
+ int i;
+
+ drm_kms_helper_poll_disable(drm);
+ for (i = 0; i < MAX_DOVE_LCD; i++) {
+ dove_lcd = dove_drm.lcds[i];
+ if (dove_lcd)
+ dove_crtc_stop(dove_lcd);
+ }
+ return 0;
+}
+
+static int dove_pm_resume(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct dove_lcd *dove_lcd;
+ int i;
+
+ for (i = 0; i < MAX_DOVE_LCD; i++) {
+ dove_lcd = dove_drm.lcds[i];
+ if (dove_lcd
+ && dove_lcd->dpms == DRM_MODE_DPMS_ON)
+ dove_crtc_start(dove_lcd);
+ }
+ drm_kms_helper_poll_enable(drm);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops dove_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dove_pm_suspend, dove_pm_resume)
+};
+
+/*
+ * Platform driver
+ */
+
+/* count the number of awaited sub devices */
+static int dove_subdev_cnt(void)
+{
+ struct device_node *np;
+ unsigned int n;
+ static struct of_device_id dove_of_subdev[] = {
+ { .compatible = "marvell,dove-lcd" },
+ { .compatible = "marvell,dove-dcon" },
+ { },
+ };
+
+ n = 0;
+ np = NULL;
+ for (;;) {
+ np = of_find_matching_node_and_match(np,
+ dove_of_subdev, NULL);
+ if (!np)
+ break;
+ if (of_device_is_available(np))
+ n++;
+ }
+ return n;
+}
+
+int dove_probed(void)
+{
+ if (atomic_add_return(1, &probed) == 0)
+ return drm_platform_init(&dove_driver, g_pdev);
+ return 0;
+}
+
+static int dove_pdev_probe(struct platform_device *pdev)
+{
+ int awaited;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ g_pdev = pdev;
+
+ awaited = dove_subdev_cnt();
+ if (awaited == 0) {
+ dev_err(&pdev->dev, "no lcd nor dcon devices\n");
+ return -ENXIO;
+ }
+ if (atomic_sub_return(awaited, &probed) == 0)
+ return drm_platform_init(&dove_driver, pdev);
+ return 0;
+}
+
+static int dove_pdev_remove(struct platform_device *pdev)
+{
+ drm_platform_exit(&dove_driver, pdev);
+ return 0;
+}
+
+static struct of_device_id dove_of_match[] = {
+ { .compatible = "marvell,dove-video" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, dove_of_match);
+
+static struct platform_driver dove_platform_driver = {
+ .probe = dove_pdev_probe,
+ .remove = dove_pdev_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "dove-drm",
+ .pm = &dove_pm_ops,
+ .of_match_table = dove_of_match,
+ },
+};
+
+static int __init dove_drm_init(void)
+{
+ int ret;
+
+ /* wait for other drivers to be loaded (si5351, tda998x..) */
+ msleep(200);
+
+/* uncomment to activate the drm trace at startup time */
+/* drm_debug = DRM_UT_CORE | DRM_UT_DRIVER | DRM_UT_KMS; */
+
+ DRM_DEBUG_DRIVER("\n");
+
+ ret = platform_driver_register(&dove_lcd_platform_driver);
+ if (ret < 0)
+ return ret;
+ ret = platform_driver_register(&dove_dcon_platform_driver);
+ if (ret < 0)
+ goto out1;
+ ret = platform_driver_register(&dove_platform_driver);
+ if (ret < 0)
+ goto out2;
+ return 0;
+
+out2:
+ platform_driver_unregister(&dove_dcon_platform_driver);
+out1:
+ platform_driver_unregister(&dove_lcd_platform_driver);
+ return ret;
+}
+static void __exit dove_drm_fini(void)
+{
+ platform_driver_unregister(&dove_platform_driver);
+ platform_driver_unregister(&dove_dcon_platform_driver);
+ platform_driver_unregister(&dove_lcd_platform_driver);
+}
+module_init(dove_drm_init);
+module_exit(dove_drm_fini);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf at free.fr>");
+MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth at gmail.com>");
+MODULE_DESCRIPTION("Marvell Dove DRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/dove/dove_drv.h b/drivers/gpu/drm/dove/dove_drv.h
new file mode 100644
index 0000000..7488c7e
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_drv.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013 Jean-François Moine
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __DOVE_DRV_H__
+#define __DOVE_DRV_H__
+
+#include <linux/clk.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder_slave.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+/* (not tested) */
+/*#define HANDLE_INTERLACE 1*/
+
+#define MAX_DOVE_LCD 2 /* max number of dove lcd devices */
+#define MAX_CLK 4 /* max number of clocks per crtc */
+
+#define PLANE_GFX 0
+#define PLANE_VID 1
+#define NPLANES 2
+
+struct dove_lcd {
+ void __iomem *mmio;
+ struct device *dev;
+ struct drm_crtc crtc;
+
+ u8 num; /* index in dove_drm */
+ u8 dpms;
+
+#ifdef HANDLE_INTERLACE
+ u8 vblank_enabled;
+ u32 v_sync0;
+ u32 v_sync1;
+#endif
+
+ short clk_src; /* current clock source */
+ struct clk *clk[MAX_CLK];
+
+ int irq;
+ char name[16];
+
+ struct drm_pending_vblank_event *event;
+
+ struct drm_plane planes[NPLANES];
+
+ struct drm_connector connector;
+ struct drm_encoder_slave encoder_slave;
+};
+
+struct dove_drm {
+ struct drm_device *drm;
+ struct dove_lcd *lcds[MAX_DOVE_LCD];
+
+ struct drm_fbdev_cma *fbdev;
+ int probe_defer;
+};
+
+extern struct dove_drm dove_drm;
+int dove_probed(void);
+
+u32 dove_vblank_count(struct drm_device *dev, int crtc);
+int dove_enable_vblank(struct drm_device *dev, int crtc);
+void dove_disable_vblank(struct drm_device *dev, int crtc);
+int dove_lcd_init(struct dove_lcd *dove_lcd);
+void dove_crtc_cancel_page_flip(struct dove_lcd *dove_lcd,
+ struct drm_file *file);
+void dove_crtc_start(struct dove_lcd *dove_lcd);
+void dove_crtc_stop(struct dove_lcd *dove_lcd);
+#ifdef CONFIG_DEBUG_FS
+int dove_debugfs_init(struct drm_minor *minor);
+void dove_debugfs_cleanup(struct drm_minor *minor);
+#endif
+extern struct platform_driver dove_lcd_platform_driver;
+
+int dove_ec_probe(struct dove_lcd *dove_lcd);
+int dove_ec_init(struct dove_lcd *dove_lcd);
+extern struct platform_driver dove_dcon_platform_driver;
+#endif /* __DOVE_DRV_H__ */
diff --git a/drivers/gpu/drm/dove/dove_ec.c b/drivers/gpu/drm/dove/dove_ec.c
new file mode 100644
index 0000000..003b031
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_ec.c
@@ -0,0 +1,570 @@
+/*
+ * Marvell Dove DRM driver - encoder / connector and display controller
+ *
+ * Copyright (C) 2013
+ * Jean-Francois Moine <moinejf at free.fr>
+ * Sebastian Hesselbarth <sebastian.hesselbarth at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/i2c.h>
+#include <linux/of_i2c.h>
+#include <linux/module.h>
+
+#include "dove_drv.h"
+#include "dove_dcon.h"
+
+struct dove_dcon {
+ void __iomem *mmio;
+ struct device *dev;
+};
+static struct dove_dcon dove_dcon;
+
+/* -----------------------------------------------------------------------------
+ * Encoder
+ */
+/* LVDS and VGA/DAC functions */
+static void dove_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+static bool dove_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+static void dove_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+static void dove_encoder_commit(struct drm_encoder *encoder)
+{
+}
+static void dove_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+}
+
+static const struct drm_encoder_helper_funcs lvds_encoder_helper_funcs = {
+ .dpms = dove_encoder_dpms,
+ .mode_fixup = dove_encoder_mode_fixup,
+ .prepare = dove_encoder_prepare,
+ .commit = dove_encoder_commit,
+ .mode_set = dove_encoder_mode_set,
+};
+
+/* HDMI (i2c) functions */
+static const struct drm_encoder_helper_funcs hdmi_encoder_helper_funcs = {
+ .dpms = drm_i2c_encoder_dpms,
+ .mode_fixup = drm_i2c_encoder_mode_fixup,
+ .prepare = drm_i2c_encoder_prepare,
+ .commit = drm_i2c_encoder_commit,
+ .mode_set = drm_i2c_encoder_mode_set,
+};
+
+static void dove_drm_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct drm_encoder_slave *encoder_slave = to_encoder_slave(encoder);
+ struct i2c_client *i2c_client;
+ struct module *module;
+
+ if (encoder_slave->slave_funcs)
+ encoder_slave->slave_funcs->destroy(encoder);
+ i2c_client = encoder_slave->bus_priv;
+ if (i2c_client) {
+ module = i2c_client->driver->driver.owner;
+ module_put(module);
+ }
+ if (encoder->dev)
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs encoder_funcs = {
+ .destroy = dove_drm_encoder_destroy,
+};
+
+static int dove_encoder_get_hdmi(struct dove_lcd *dove_lcd)
+{
+ struct device *dev = dove_lcd->dev;
+ struct drm_device *drm = dove_drm.drm;
+ struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave;
+ struct drm_encoder *encoder = &encoder_slave->base;
+ struct drm_connector *connector = &dove_lcd->connector;
+ struct i2c_client *i2c_client;
+ struct module *module;
+ struct drm_i2c_encoder_driver *encoder_drv;
+ int ret;
+
+ i2c_client = encoder_slave->bus_priv;
+ if (!i2c_client) {
+ dev_err(dev, "no external-encoder for hdmi\n");
+ return -EINVAL;
+ }
+
+ encoder_drv = to_drm_i2c_encoder_driver(i2c_client->driver);
+ if (!encoder_drv || !encoder_drv->encoder_init) {
+ dev_err(dev, "no external encoder init\n");
+ return -EINVAL;
+ }
+
+ /* lock the external encoder module */
+ module = i2c_client->driver->driver.owner;
+ if (!module || !try_module_get(module)) {
+ dev_err(dev, "cannot get module %s\n", module->name);
+ return -EINVAL;
+ }
+
+ ret = encoder_drv->encoder_init(i2c_client, drm, encoder_slave);
+ if (ret < 0) {
+ dev_err(dev, "slave encoder init failed\n");
+ return ret;
+ }
+ encoder_slave->slave_funcs->create_resources(encoder, connector);
+ return ret;
+}
+
+static int dove_encoder_init(struct dove_lcd *dove_lcd,
+ int mode_encoder)
+{
+ struct drm_device *drm = dove_drm.drm;
+ struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave;
+ struct drm_encoder *encoder = &encoder_slave->base;
+ int ret;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ /* do early init in case of error */
+ ret = drm_encoder_init(drm, encoder, &encoder_funcs, mode_encoder);
+ if (ret < 0) {
+ dev_err(dove_lcd->dev, "drm encoder init failed\n");
+ return ret;
+ }
+
+ encoder->possible_crtcs = 1 << dove_lcd->num;
+
+ /*
+ * If the display controller is present,
+ * - the port A cannot be VGA/DAC,
+ * - the port B can be only VGA/DAC and may receive the lcd 0 output.
+ */
+ if (dove_dcon.mmio) {
+ if (dove_lcd->num == 0) {
+ if (mode_encoder == DRM_MODE_ENCODER_DAC) {
+ dev_err(dove_lcd->dev,
+ "bad lcd 0 port-type\n");
+ return -EINVAL;
+ }
+ } else {
+ if (mode_encoder != DRM_MODE_ENCODER_DAC) {
+ dev_err(dove_lcd->dev,
+ "bad lcd 1 port-type\n");
+ return -EINVAL;
+ }
+ encoder->possible_crtcs |= 1;
+
+ /* the port B may receive the LCD 0 output */
+ encoder->possible_clones = 1;
+ }
+ }
+
+ switch (mode_encoder) {
+ case DRM_MODE_ENCODER_DAC:
+/*fixme: to do */
+ case DRM_MODE_ENCODER_LVDS:
+ drm_encoder_helper_add(encoder, &lvds_encoder_helper_funcs);
+ ret = 0;
+ break;
+ case DRM_MODE_ENCODER_TMDS:
+ drm_encoder_helper_add(encoder, &hdmi_encoder_helper_funcs);
+ ret = dove_encoder_get_hdmi(dove_lcd);
+ break;
+ }
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Connector
+ */
+
+static int dove_lvds_get_modes(struct dove_lcd *dove_lcd)
+{
+ struct device *dev = dove_lcd->dev;
+ struct device_node *np = dev->of_node;
+ struct drm_connector *connector = &dove_lcd->connector;
+ struct drm_display_mode *mode;
+ int clock, hdisplay, vdisplay, hfp, hbp, vfp, vbp, hs, vs;
+ int w_mm, h_mm;
+ int ret;
+
+ /* same as of_videomode, but simpler! */
+ np = of_find_node_by_name(np, "display-timings");
+ if (!np) {
+ dev_err(dev, "no display-timings\n");
+ return -EINVAL;
+ }
+ np = of_get_next_child(np, NULL);
+ if (!np) {
+ dev_err(dev, "no 'mode' subnode in DT\n");
+ return -EINVAL;
+ }
+
+ ret = 0;
+ ret |= of_property_read_u32(np, "hactive", &hdisplay);
+ ret |= of_property_read_u32(np, "vactive", &vdisplay);
+ ret |= of_property_read_u32(np, "hfront-porch", &hfp);
+ ret |= of_property_read_u32(np, "hsync-len", &hs);
+ ret |= of_property_read_u32(np, "hback-porch", &hbp);
+ ret |= of_property_read_u32(np, "vfront-porch", &vfp);
+ ret |= of_property_read_u32(np, "vsync-len", &vs);
+ ret |= of_property_read_u32(np, "vback-porch", &vbp);
+ ret |= of_property_read_u32(np, "clock", &clock);
+ if (ret) {
+ dev_err(dev, "bad display-timings\n");
+ return -EINVAL;
+ }
+ if (clock < 15000 || clock > 150000) {
+ dev_err(dev, "bad clock\n");
+ return -EINVAL;
+ }
+
+ mode = drm_mode_create(dove_drm.drm);
+ if (!mode) {
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ mode->clock = clock;
+ mode->hdisplay = hdisplay;
+ mode->hsync_start = hdisplay + hfp;
+ mode->hsync_end = mode->hsync_start + hs;
+ mode->htotal = mode->hsync_end + hbp;
+ mode->vdisplay = vdisplay;
+ mode->vsync_start = vdisplay + vfp;
+ mode->vsync_end = mode->vsync_start + vs;
+ mode->vtotal = mode->vsync_end + vbp;
+
+ drm_mode_set_name(mode);
+ mode->vrefresh = drm_mode_vrefresh(mode);
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, mode);
+
+ /* optional display dimension */
+ ret = of_property_read_u32(np, "width-mm", &w_mm);
+ ret |= of_property_read_u32(np, "height-mm", &h_mm);
+ if (ret >= 0) {
+ connector->display_info.width_mm = w_mm;
+ connector->display_info.height_mm = h_mm;
+ }
+ return 1;
+}
+
+static int dove_drm_connector_get_modes(struct drm_connector *connector)
+{
+ struct dove_lcd *dove_lcd =
+ container_of(connector, struct dove_lcd, connector);
+ struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave;
+ int ret;
+
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_VGA:
+/*fixme:to do */
+ case DRM_MODE_CONNECTOR_LVDS:
+ ret = dove_lvds_get_modes(dove_lcd);
+ break;
+ default:
+/* case DRM_MODE_CONNECTOR_HDMIA: */
+/* case DRM_MODE_CONNECTOR_HDMIB: */
+ ret = encoder_slave->slave_funcs->get_modes(&encoder_slave->base,
+ connector);
+ break;
+ }
+ DRM_DEBUG_DRIVER("-> %d\n", ret);
+ return ret;
+}
+
+static int dove_drm_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct dove_lcd *dove_lcd =
+ container_of(connector, struct dove_lcd, connector);
+ struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave;
+
+ if (!encoder_slave->slave_funcs)
+ return MODE_OK;
+ return encoder_slave->slave_funcs->mode_valid(&encoder_slave->base,
+ mode);
+}
+
+static struct drm_encoder *
+dove_drm_connector_best_encoder(struct drm_connector *connector)
+{
+ struct dove_lcd *dove_lcd =
+ container_of(connector, struct dove_lcd, connector);
+
+ return &dove_lcd->encoder_slave.base;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+ .get_modes = dove_drm_connector_get_modes,
+ .mode_valid = dove_drm_connector_mode_valid,
+ .best_encoder = dove_drm_connector_best_encoder,
+};
+
+static void dove_drm_connector_destroy(struct drm_connector *connector)
+{
+ if (!connector->dev)
+ return;
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+dove_drm_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct dove_lcd *dove_lcd =
+ container_of(connector, struct dove_lcd, connector);
+ struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave;
+
+ DRM_DEBUG_DRIVER("\n");
+ if (encoder_slave->slave_funcs)
+ return encoder_slave->slave_funcs->detect(&encoder_slave->base,
+ connector);
+/*fixme: KO with VGA*/
+ return connector_status_connected;
+}
+
+static void dove_drm_connector_dpms(struct drm_connector *connector,
+ int mode)
+{
+ struct dove_lcd *dove_lcd =
+ container_of(connector, struct dove_lcd, connector);
+ struct drm_encoder *encoder = connector->encoder;
+ struct drm_encoder_slave *encoder_slave = to_encoder_slave(encoder);
+ struct dove_lcd *dove_lcd2 =
+ container_of(encoder_slave, struct dove_lcd, encoder_slave);
+ int modeA, modeB;
+ u32 reg;
+
+ if (mode == dove_lcd->connector.dpms)
+ return;
+
+ /* adjust the port B input */
+ if (dove_dcon.mmio) {
+ reg = readl(dove_dcon.mmio + DCON_CTL0);
+ reg &= ~LCD_Port_B_Select_MASK;
+ if (dove_lcd2 != dove_lcd) {
+ if (dove_lcd->num == 0) {
+ modeA = mode;
+ modeB = dove_lcd2->connector.dpms;
+ } else {
+ modeA = dove_lcd->connector.dpms;
+ modeB = mode;
+ }
+
+ if (modeB == DRM_MODE_DPMS_ON) {
+ if (modeA == DRM_MODE_DPMS_ON)
+ reg |= Port_B_Select_A_copy;
+ else
+ reg |= Port_B_Select_LCD0;
+ }
+ }
+ writel(reg, dove_dcon.mmio + DCON_CTL0);
+ DRM_DEBUG_DRIVER("port B select %08x\n", reg);
+ }
+
+ drm_helper_connector_dpms(connector, mode);
+}
+
+static int dove_connector_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t value)
+{
+ struct dove_lcd *dove_lcd =
+ container_of(connector, struct dove_lcd, connector);
+ struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave;
+
+ DRM_DEBUG_DRIVER("\n");
+ if (!encoder_slave->slave_funcs)
+ return 0;
+ return encoder_slave->slave_funcs->set_property(&encoder_slave->base,
+ connector,
+ property,
+ value);
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+ .destroy = dove_drm_connector_destroy,
+ .dpms = dove_drm_connector_dpms,
+ .detect = dove_drm_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .set_property = dove_connector_set_property,
+};
+
+/* initialize the couple connector-encoder of a LCD */
+int dove_ec_init(struct dove_lcd *dove_lcd)
+{
+ struct device *dev = dove_lcd->dev;
+ struct device_node *np = dev->of_node;
+ struct drm_device *drm = dove_drm.drm;
+ struct drm_connector *connector = &dove_lcd->connector;
+ struct drm_encoder_slave *encoder_slave = &dove_lcd->encoder_slave;
+ struct drm_encoder *encoder = &encoder_slave->base;
+ u32 port_type;
+ int mode_encoder, ret;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ /* get the port (connector) type */
+ if (of_property_read_u32(np, "marvell,port-type", &port_type)) {
+ dev_err(dev, "no port-type\n");
+ return -EINVAL;
+ }
+ switch (port_type) {
+ case DRM_MODE_CONNECTOR_VGA: /* 1 */
+ mode_encoder = DRM_MODE_ENCODER_DAC;
+ break;
+ case DRM_MODE_CONNECTOR_LVDS: /* 7 */
+ mode_encoder = DRM_MODE_ENCODER_LVDS;
+ break;
+ case DRM_MODE_CONNECTOR_HDMIA: /* 11 */
+ case DRM_MODE_CONNECTOR_HDMIB: /* 12 */
+ mode_encoder = DRM_MODE_ENCODER_TMDS;
+ break;
+ default:
+ dev_err(dev, "bad port type %d\n", port_type);
+ return -EINVAL;
+ }
+
+ ret = drm_connector_init(drm, connector, &connector_funcs, port_type);
+ if (ret < 0)
+ return ret;
+
+ drm_connector_helper_add(connector, &connector_helper_funcs);
+
+#ifdef HANDLE_INTERLACE
+ connector->interlace_allowed = true;
+#endif
+
+ ret = dove_encoder_init(dove_lcd, mode_encoder);
+ if (ret < 0)
+ goto err;
+
+ ret = drm_mode_connector_attach_encoder(connector, encoder);
+ if (ret < 0)
+ goto err;
+
+ connector->encoder = encoder;
+
+ ret = drm_sysfs_connector_add(connector);
+ if (ret < 0)
+ goto err;
+
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ ret = drm_object_property_set_value(&connector->base,
+ drm->mode_config.dpms_property,
+ DRM_MODE_DPMS_OFF);
+ if (ret < 0)
+ goto err;
+ return 0;
+
+err:
+ dev_err(dev, "dove_ec_init err %d\n", ret);
+ dove_drm_encoder_destroy(&encoder_slave->base);
+ drm_connector_cleanup(connector);
+ return ret;
+}
+
+/* at probe time, check the presence of a possible external slave encoder */
+int dove_ec_probe(struct dove_lcd *dove_lcd)
+{
+ struct device *dev = dove_lcd->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *i2c_node;
+ struct i2c_client *i2c_client;
+
+ /* get the optional external encoder */
+ i2c_node = of_parse_phandle(np, "marvell,external-encoder", 0);
+ if (!i2c_node)
+ return 0;
+
+ i2c_client = of_find_i2c_device_by_node(i2c_node);
+ of_node_put(i2c_node);
+ if (!i2c_client) {
+ dev_err(dev, "bad external-encoder\n");
+ return -EINVAL;
+ }
+
+ /* check if the slave-encoder module is initialized */
+ if (!i2c_client->driver) {
+ if (dove_drm.probe_defer) {
+ dev_err(dev, "cannot get the external-encoder\n");
+ return -EINVAL;
+ }
+ dove_drm.probe_defer = 1;
+ return -EPROBE_DEFER;
+ }
+
+ dove_lcd->encoder_slave.bus_priv = i2c_client;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Display controller
+ */
+
+static int dove_dcon_remove(struct platform_device *pdev)
+{
+ dove_dcon.mmio = NULL;
+ return 0;
+}
+
+static int dove_dcon_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *mmio;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get dcon resource\n");
+ return -EINVAL;
+ }
+
+ mmio = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mmio)) {
+ dev_err(&pdev->dev, "failed map dcon registers\n");
+ return PTR_ERR(mmio);
+ }
+
+ dove_dcon.mmio = mmio;
+ dove_dcon.dev = &pdev->dev;
+
+ /* init done, try to initialize the drm driver */
+ return dove_probed();
+}
+
+static struct of_device_id dove_dcon_of_match[] = {
+ { .compatible = "marvell,dove-dcon" },
+ { },
+};
+struct platform_driver dove_dcon_platform_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "dove-dcon",
+ .of_match_table = dove_dcon_of_match,
+ },
+ .probe = dove_dcon_probe,
+ .remove = dove_dcon_remove,
+};
diff --git a/drivers/gpu/drm/dove/dove_lcd.h b/drivers/gpu/drm/dove/dove_lcd.h
new file mode 100644
index 0000000..03b198b
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_lcd.h
@@ -0,0 +1,519 @@
+/*
+ * LCD controller registers of Marvell DOVE
+ *
+ * Copyright (C) 2013
+ * Jean-Francois Moine <moinejf at free.fr>
+ * Sebastian Hesselbarth <sebastian.hesselbarth at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _DOVE_LCD_H_
+#define _DOVE_LCD_H_
+
+/* ------------< LCD register >------------ */
+
+/* Video Frame 0&1 start address registers */
+#define LCD_TV_CONTROL1 0x0084
+#define VSYNC_L_OFFSET(o) ((o) << 20)
+#define VSYNC_L_OFFSET_MASK (0xfff << 20)
+#define HWC32_ENABLE BIT(13)
+#define VSYNC_OFFSET_EN BIT(12)
+#define VSYNC_H_OFFSET(o) (o)
+#define VSYNC_H_OFFSET_MASK 0xfff
+
+/* Video Frame 0&1 start address registers */
+#define LCD_SPU_DMA_START_ADDR_Y0 0x00c0
+#define LCD_SPU_DMA_START_ADDR_U0 0x00c4
+#define LCD_SPU_DMA_START_ADDR_V0 0x00c8
+#define LCD_CFG_DMA_START_ADDR_0 0x00cc /* Cmd address */
+#define LCD_SPU_DMA_START_ADDR_Y1 0x00d0
+#define LCD_SPU_DMA_START_ADDR_U1 0x00d4
+#define LCD_SPU_DMA_START_ADDR_V1 0x00d8
+#define LCD_CFG_DMA_START_ADDR_1 0x00dc /* Cmd address */
+
+/* YC & UV Pitch */
+#define LCD_SPU_DMA_PITCH_YC 0x00e0
+#define LCD_Y_C(y, c) (((c) << 16) | (y))
+#define LCD_SPU_DMA_PITCH_UV 0x00e4
+#define LCD_U_V(u, v) (((v) << 16) | (u))
+
+/* Video Starting Point on Screen Register */
+#define LCD_SPUT_DMA_OVSA_HPXL_VLN 0x00e8
+
+/* Video Size Register */
+#define LCD_SPU_DMA_HPXL_VLN 0x00ec
+
+/* Video Size After zooming Register */
+#define LCD_SPU_DZM_HPXL_VLN 0x00f0
+
+/* Graphic Frame 0&1 Starting Address Register */
+#define LCD_CFG_GRA_START_ADDR0 0x00f4
+#define LCD_CFG_GRA_START_ADDR1 0x00f8
+
+/* Graphic Frame Pitch */
+#define LCD_CFG_GRA_PITCH 0x00fc
+
+/* Graphic Starting Point on Screen Register */
+#define LCD_SPU_GRA_OVSA_HPXL_VLN 0x0100
+
+/* Graphic Size Register */
+#define LCD_SPU_GRA_HPXL_VLN 0x0104
+
+/* Graphic Size after Zooming Register */
+#define LCD_SPU_GZM_HPXL_VLN 0x0108
+
+/* HW Cursor Starting Point on Screen Register */
+#define LCD_SPU_HWC_OVSA_HPXL_VLN 0x010c
+
+/* HW Cursor Size */
+#define LCD_SPU_HWC_HPXL_VLN 0x0110
+
+/* Total Screen Size Register */
+#define LCD_SPUT_V_H_TOTAL 0x0114
+
+/* Total Screen Active Size Register */
+#define LCD_SPU_V_H_ACTIVE 0x0118
+#define LCD_H_V(h, v) (((v) << 16) | (h))
+#define H_LCD(x) ((x) & 0xffff)
+#define V_LCD(x) (((x) >> 16) & 0xffff)
+
+/* Screen H&V Porch Register */
+#define LCD_SPU_H_PORCH 0x011c
+#define LCD_SPU_V_PORCH 0x0120
+#define LCD_F_B(f, b) (((b) << 16) | (f))
+#define F_LCD(x) ((x) & 0xffff)
+#define B_LCD(x) (((x) >> 16) & 0xffff)
+
+/* Screen Blank Color Register */
+#define LCD_SPU_BLANKCOLOR 0x0124
+
+/* HW Cursor Color 1&2 Register */
+#define LCD_SPU_ALPHA_COLOR1 0x0128
+#define HWC32_CFG_ALPHA(alpha) ((alpha) << 24)
+#define LCD_SPU_ALPHA_COLOR2 0x012c
+#define COLOR_MASK 0x00ffffff
+#define COLOR_RGB(r, g, b) (((b) << 16) | ((g) << 8) | (r))
+
+/* Video YUV Color Key Control */
+#define LCD_SPU_COLORKEY_Y 0x0130
+#define CFG_CKEY_Y2(y2) ((y2) << 24)
+#define CFG_CKEY_Y2_MASK 0xff000000
+#define CFG_CKEY_Y1(y1) ((y1) << 16)
+#define CFG_CKEY_Y1_MASK 0x00ff0000
+#define CFG_CKEY_Y(y) ((y) << 8)
+#define CFG_CKEY_Y_MASK 0x0000ff00
+#define CFG_ALPHA_Y(y) (y)
+#define CFG_ALPHA_Y_MASK 0x000000ff
+#define LCD_SPU_COLORKEY_U 0x0134
+#define CFG_CKEY_U2(u2) ((u2) << 24)
+#define CFG_CKEY_U2_MASK 0xff000000
+#define CFG_CKEY_U1(u1) ((u1) << 16)
+#define CFG_CKEY_U1_MASK 0x00ff0000
+#define CFG_CKEY_U(u) ((u) << 8)
+#define CFG_CKEY_U_MASK 0x0000ff00
+#define CFG_ALPHA_U(u) (u)
+#define CFG_ALPHA_U_MASK 0x000000ff
+#define LCD_SPU_COLORKEY_V 0x0138
+#define CFG_CKEY_V2(v2) ((v2) << 24)
+#define CFG_CKEY_V2_MASK 0xff000000
+#define CFG_CKEY_V1(v1) ((v1) << 16)
+#define CFG_CKEY_V1_MASK 0x00ff0000
+#define CFG_CKEY_V(v) ((v) << 8)
+#define CFG_CKEY_V_MASK 0x0000ff00
+#define CFG_ALPHA_V(v) (v)
+#define CFG_ALPHA_V_MASK 0x000000ff
+
+/* LCD General Configuration Register */
+#define LCD_CFG_RDREG4F 0x013c
+#define LCD_SRAM_WAIT BIT(11)
+#define DMA_WATERMARK_MASK 0xff
+#define DMA_WATERMARK(m) (m)
+
+/* SPI Read Data Register */
+#define LCD_SPU_SPI_RXDATA 0x0140
+
+/* Smart Panel Read Data Register */
+#define LCD_SPU_ISA_RSDATA 0x0144
+#define ISA_RXDATA_16BIT_1_DATA_MASK 0x000000ff
+#define ISA_RXDATA_16BIT_2_DATA_MASK 0x0000ff00
+#define ISA_RXDATA_16BIT_3_DATA_MASK 0x00ff0000
+#define ISA_RXDATA_16BIT_4_DATA_MASK 0xff000000
+#define ISA_RXDATA_32BIT_1_DATA_MASK 0x00ffffff
+
+/* HWC SRAM Read Data Register */
+#define LCD_SPU_HWC_RDDAT 0x0158
+
+/* Gamma Table SRAM Read Data Register */
+#define LCD_SPU_GAMMA_RDDAT 0x015c
+#define GAMMA_RDDAT_MASK 0x000000ff
+
+/* Palette Table SRAM Read Data Register */
+#define LCD_SPU_PALETTE_RDDAT 0x0160
+#define PALETTE_RDDAT_MASK 0x00ffffff
+
+/* I/O Pads Input Read Only Register */
+#define LCD_SPU_IOPAD_IN 0x0178
+#define IOPAD_IN_MASK 0x0fffffff
+
+/* Reserved Read Only Registers */
+#define LCD_CFG_RDREG5F 0x017c
+#define IRE_FRAME_CNT_MASK 0x000000c0
+#define IPE_FRAME_CNT_MASK 0x00000030
+#define GRA_FRAME_CNT_MASK 0x0000000c /* Graphic */
+#define DMA_FRAME_CNT_MASK 0x00000003 /* Video */
+
+/* SPI Control Register. */
+#define LCD_SPU_SPI_CTRL 0x0180
+#define CFG_SCLKCNT(div) ((div) << 24)
+#define CFG_SCLKCNT_MASK 0xff000000
+#define CFG_RXBITS(rx) ((rx) << 16)
+#define CFG_RXBITS_MASK 0x00ff0000
+#define CFG_TXBITS(tx) ((tx) << 8)
+#define CFG_TXBITS_MASK 0x0000ff00
+#define CFG_CLKINV BIT(7)
+#define CFG_KEEPXFER BIT(6)
+#define CFG_RXBITSTO0 BIT(5)
+#define CFG_TXBITSTO0 BIT(4)
+#define CFG_SPI_ENA BIT(3)
+#define CFG_SPI_SEL BIT(2)
+#define CFG_SPI_3W4WB BIT(1)
+#define CFG_SPI_START BIT(0)
+
+/* SPI Tx Data Register */
+#define LCD_SPU_SPI_TXDATA 0x0184
+
+/*
+ * 1. Smart Pannel 8-bit Bus Control Register.
+ * 2. AHB Slave Path Data Port Register
+ */
+#define LCD_SPU_SMPN_CTRL 0x0188
+
+/* DMA Control 0 Register */
+#define LCD_SPU_DMA_CTRL0 0x0190
+#define CFG_NOBLENDING BIT(31)
+#define CFG_GAMMA_ENA BIT(30)
+#define CFG_CBSH_ENA BIT(29)
+#define CFG_PALETTE_ENA BIT(28)
+#define CFG_ARBFAST_ENA BIT(27)
+#define CFG_HWC_1BITMOD BIT(26)
+#define CFG_HWC_1BITENA BIT(25)
+#define CFG_HWC_ENA BIT(24)
+#define CFG_DMAFORMAT(dmaformat) ((dmaformat) << 20)
+#define CFG_DMAFORMAT_MASK 0x00f00000
+#define CFG_GRAFORMAT(graformat) ((graformat) << 16)
+#define CFG_GRAFORMAT_MASK 0x000f0000
+/* for graphic part */
+#define CFG_GRA_FTOGGLE BIT(15)
+#define CFG_GRA_HSMOOTH BIT(14)
+#define CFG_GRA_TSTMODE BIT(13)
+#define CFG_GRA_SWAPRB BIT(12)
+#define CFG_GRA_SWAPUV BIT(11)
+#define CFG_GRA_SWAPYU BIT(10)
+#define CFG_YUV2RGB_GRA BIT(9)
+#define CFG_GRA_ENA BIT(8)
+/* for video part */
+#define CFG_DMA_FTOGGLE BIT(7)
+#define CFG_DMA_HSMOOTH BIT(6)
+#define CFG_DMA_TSTMODE BIT(5)
+#define CFG_DMA_SWAPRB BIT(4)
+#define CFG_DMA_SWAPUV BIT(3)
+#define CFG_DMA_SWAPYU BIT(2)
+#define CFG_DMA_SWAP_MASK 0x0000001c
+#define CFG_YUV2RGB_DMA BIT(1)
+#define CFG_DMA_ENA BIT(0)
+
+/* DMA Control 1 Register */
+#define LCD_SPU_DMA_CTRL1 0x0194
+#define CFG_FRAME_TRIG BIT(31)
+#define CFG_VSYNC_TRIG(trig) ((trig) << 28)
+#define CFG_VSYNC_TRIG_MASK 0x70000000
+#define CFG_VSYNC_INV BIT(27)
+#define CFG_COLOR_KEY_MODE(cmode) ((cmode) << 24)
+#define CFG_COLOR_KEY_MASK 0x07000000
+#define CFG_CARRY BIT(23)
+#define CFG_GATED_ENA BIT(21)
+#define CFG_PWRDN_ENA BIT(20)
+#define CFG_DSCALE(dscale) ((dscale) << 18)
+#define CFG_DSCALE_MASK 0x000c0000
+#define CFG_ALPHA_MODE(amode) ((amode) << 16)
+#define CFG_ALPHA_MODE_MASK 0x00030000
+#define CFG_ALPHA(alpha) ((alpha) << 8)
+#define CFG_ALPHA_MASK 0x0000ff00
+#define CFG_PXLCMD(pxlcmd) (pxlcmd)
+#define CFG_PXLCMD_MASK 0x000000ff
+
+/* SRAM Control Register */
+#define LCD_SPU_SRAM_CTRL 0x0198
+#define CFG_SRAM_INIT_WR_RD(mode) ((mode) << 14)
+#define CFG_SRAM_INIT_WR_RD_MASK 0x0000c000
+#define CFG_SRAM_ADDR_LCDID(id) ((id) << 8)
+#define CFG_SRAM_ADDR_LCDID_MASK 0x00000f00
+#define CFG_SRAM_ADDR(addr) (addr)
+#define CFG_SRAM_ADDR_MASK 0x000000ff
+
+/* SRAM Write Data Register */
+#define LCD_SPU_SRAM_WRDAT 0x019c
+
+/* SRAM RTC/WTC Control Register */
+#define LCD_SPU_SRAM_PARA0 0x01a0
+
+/* SRAM Power Down Control Register */
+#define LCD_SPU_SRAM_PARA1 0x01a4
+#define CFG_CSB_256x32 BIT(15) /* HWC */
+#define CFG_CSB_256x24 BIT(14) /* Palette */
+#define CFG_CSB_256x8 BIT(13) /* Gamma */
+#define CFG_PDWN256x32 BIT(7) /* HWC */
+#define CFG_PDWN256x24 BIT(6) /* Palette */
+#define CFG_PDWN256x8 BIT(5) /* Gamma */
+#define CFG_PDWN32x32 BIT(3)
+#define CFG_PDWN16x66 BIT(2)
+#define CFG_PDWN32x66 BIT(1)
+#define CFG_PDWN64x66 BIT(0)
+
+/* Smart or Dumb Panel Clock Divider */
+#define LCD_CFG_SCLK_DIV 0x01a8
+#define SET_SCLK(src, div, frac) (((src) << 30) | ((frac) << 16 ) | (div))
+
+/* Video Contrast Register */
+#define LCD_SPU_CONTRAST 0x01ac
+#define CFG_BRIGHTNESS(bright) ((bright) << 16)
+#define CFG_BRIGHTNESS_MASK 0xffff0000
+#define CFG_CONTRAST(contrast) (contrast)
+#define CFG_CONTRAST_MASK 0x0000ffff
+
+/* Video Saturation Register */
+#define LCD_SPU_SATURATION 0x01b0
+#define CFG_C_MULTS(mult) ((mult) << 16)
+#define CFG_C_MULTS_MASK 0xffff0000
+#define CFG_SATURATION(sat) (sat)
+#define CFG_SATURATION_MASK 0x0000ffff
+
+/* Video Hue Adjust Register */
+#define LCD_SPU_CBSH_HUE 0x01b4
+#define CFG_SIN0(sin0) ((sin0) << 16)
+#define CFG_SIN0_MASK 0xffff0000
+#define CFG_COS0(con0) (con0)
+#define CFG_COS0_MASK 0x0000ffff
+
+/* Dump LCD Panel Control Register */
+#define LCD_SPU_DUMB_CTRL 0x01b8
+#define CFG_DUMBMODE(mode) ((mode) << 28)
+#define CFG_DUMBMODE_MASK 0xf0000000
+#define CFG_LCDGPIO_O(data) ((data) << 20)
+#define CFG_LCDGPIO_O_MASK 0x0ff00000
+#define CFG_LCDGPIO_ENA(gpio) ((gpio) << 12)
+#define CFG_LCDGPIO_ENA_MASK 0x000ff000
+#define CFG_BIAS_OUT BIT(8)
+#define CFG_REVERSE_RGB BIT(7)
+#define CFG_INV_COMPBLANK BIT(6)
+#define CFG_INV_COMPSYNC BIT(5)
+#define CFG_INV_HENA BIT(4)
+#define CFG_INV_VSYNC BIT(3)
+#define CFG_INV_HSYNC BIT(2)
+#define CFG_INV_PCLK BIT(1)
+#define CFG_DUMB_ENA BIT(0)
+
+/* LCD I/O Pads Control Register */
+#define SPU_IOPAD_CONTROL 0x01bc
+#define CFG_VSC_LINEAR(vm) ((vm) << 18) /* gfx */
+#define CFG_VSC_LINEAR_MASK 0x000c0000
+#define CFG_GRA_VM_ENA BIT(15) /* gfx */
+#define CFG_DMA_VM_ENA BIT(14) /* video */
+#define CFG_CMD_VM_ENA BIT(13)
+#define CFG_CSC(csc) ((csc) << 8)
+#define CFG_CSC_MASK 0x00000300
+#define CFG_AXICTRL(axi) ((axi) << 4)
+#define CFG_AXICTRL_MASK 0x000000f0
+#define CFG_IOPADMODE(iopad) (iopad)
+#define CFG_IOPADMODE_MASK 0x0000000f
+
+/* LCD Interrupt Control Register */
+#define SPU_IRQ_ENA 0x1c0
+/* LCD Interrupt Status Register */
+#define SPU_IRQ_ISR 0x1c4
+#define IRQ_DMA_FRAME0 BIT(31)
+#define IRQ_DMA_FRAME1 BIT(30)
+#define IRQ_DMA_FIFO_UNDERFLOW BIT(29)
+#define IRQ_GRA_FRAME0 BIT(27)
+#define IRQ_GRA_FRAME1 BIT(26)
+#define IRQ_GRA_FIFO_UNDERFLOW BIT(25)
+#define IRQ_SMART_VSYNC BIT(23)
+#define IRQ_DUMB_FRAME_DONE BIT(22)
+#define IRQ_SMART_FRAME_DONE BIT(21)
+#define IRQ_HWCURSOR_FRAME_DONE BIT(20)
+#define IRQ_AHB_CMD_EMPTY BIT(19)
+#define IRQ_SPI_TRANSFER_DONE BIT(18)
+#define IRQ_POWERDOWN BIT(17)
+#define IRQ_AXI_ERROR BIT(16)
+/* read only status */
+#define STA_DMA_FRAME0 BIT(15)
+#define STA_DMA_FRAME1 BIT(14)
+#define STA_DMA_FRAME_COUNT(x) (((x) & (BIT(13) | BIT(12))) >> 12)
+#define STA_GRA_FRAME0 BIT(11)
+#define STA_GRA_FRAME1 BIT(10)
+#define STA_GRA_FRAME_COUNT(x) (((x) & (BIT(9) | BIT(8))) >> 8)
+#define STA_SMART_VSYNC BIT(7)
+#define STA_DUMB_FRAME_DONE BIT(6)
+#define STA_SMART_FRAME_DONE BIT(5)
+#define STA_HWCURSOR_FRAME_DONE BIT(4)
+#define STA_AHB_CMD_EMPTY BIT(3)
+#define STA_DMA_FIFO_EMPTY BIT(2)
+#define STA_GRA_FIFO_EMPTY BIT(1)
+#define STA_POWERDOWN BIT(0)
+
+#define IRQ_DMA_FRAME_DONE (IRQ_DMA_FRAME0 | IRQ_DMA_FRAME1)
+#define IRQ_GRA_FRAME_DONE \
+ (IRQ_GRA_FRAME0 | IRQ_GRA_FRAME1 | IRQ_SMART_VSYNC)
+
+/*
+ * defined Video Memory Color format for DMA control 0 register
+ * DMA0 bit[23:20]
+ */
+#define VMODE_RGB565 0x0
+#define VMODE_RGB1555 0x1
+#define VMODE_RGB888PACKED 0x2
+#define VMODE_RGB888UNPACKED 0x3
+#define VMODE_RGBA888 0x4
+#define VMODE_YUV422PACKED 0x5
+#define VMODE_YUV422PLANAR 0x6
+#define VMODE_YUV420PLANAR 0x7
+#define VMODE_SMPNCMD 0x8
+#define VMODE_PALETTE4BIT 0x9
+#define VMODE_PALETTE8BIT 0xa
+#define VMODE_RESERVED 0xb
+
+/*
+ * defined Graphic Memory Color format for DMA control 0 register
+ * DMA0 bit[19:16]
+ */
+#define GMODE_RGB565 0x0
+#define GMODE_RGB1555 0x1
+#define GMODE_RGB888PACKED 0x2
+#define GMODE_RGB888UNPACKED 0x3
+#define GMODE_RGBA888 0x4
+#define GMODE_YUV422PACKED 0x5
+#define GMODE_YUV422PLANAR 0x6
+#define GMODE_YUV420PLANAR 0x7
+#define GMODE_SMPNCMD 0x8
+#define GMODE_PALETTE4BIT 0x9
+#define GMODE_PALETTE8BIT 0xa
+#define GMODE_RESERVED 0xb
+
+/*
+ * define for DMA control 1 register
+ */
+#define DMA1_FRAME_TRIG 31 /* bit location */
+#define DMA1_VSYNC_MODE 28
+#define DMA1_VSYNC_INV 27
+#define DMA1_CKEY 24
+#define DMA1_CARRY 23
+#define DMA1_LNBUF_ENA 22
+#define DMA1_GATED_ENA 21
+#define DMA1_PWRDN_ENA 20
+#define DMA1_DSCALE 18
+#define DMA1_ALPHA_MODE 16
+#define DMA1_ALPHA 8
+#define DMA1_PXLCMD 0
+
+/*
+ * defined for Configure Dumb Mode
+ * DUMB LCD Panel bit[31:28]
+ */
+#define DUMB16_RGB565_0 0x0
+#define DUMB16_RGB565_1 0x1
+#define DUMB18_RGB666_0 0x2
+#define DUMB18_RGB666_1 0x3
+#define DUMB12_RGB444_0 0x4
+#define DUMB12_RGB444_1 0x5
+#define DUMB24_RGB888_0 0x6
+#define DUMB_BLANK 0x7
+
+/*
+ * defined for Configure I/O Pin Allocation Mode
+ * LCD LCD I/O Pads control register bit[3:0]
+ */
+#define IOPAD_DUMB24 0x0
+#define IOPAD_DUMB18SPI 0x1
+#define IOPAD_DUMB18GPIO 0x2
+#define IOPAD_DUMB16SPI 0x3
+#define IOPAD_DUMB16GPIO 0x4
+#define IOPAD_DUMB12 0x5
+#define IOPAD_SMART18SPI 0x6
+#define IOPAD_SMART16SPI 0x7
+#define IOPAD_SMART8BOTH 0x8
+
+/*
+ * clock source SCLK_Source bit[31:30]
+ */
+#define SCLK_SRC_AXI 0
+#define SCLK_SRC_EXTCLK0 1
+#define SCLK_SRC_PLLDIV 2
+#define SCLK_SRC_EXTCLK1 3
+
+/*
+ * defined Dumb Panel Clock Divider register
+ * SCLK_Source bit[31]
+ */
+#define AXI_BUS_SEL 0x80000000 /* 0: PLL clock select*/
+#define CCD_CLK_SEL 0x40000000
+#define DCON_CLK_SEL 0x20000000
+#define IDLE_CLK_INT_DIV 0x1 /* idle Integer Divider */
+#define DIS_CLK_INT_DIV 0x0 /* Disable Integer Divider */
+
+/* SRAM ID */
+#define SRAMID_GAMMA_YR 0x0
+#define SRAMID_GAMMA_UG 0x1
+#define SRAMID_GAMMA_VB 0x2
+#define SRAMID_PALETTE 0x3
+#define SRAMID_HWC32_RAM1 0xc
+#define SRAMID_HWC32_RAM2 0xd
+#define SRAMID_HWC32_RAM3 0xe
+#define SRAMID_HWC 0xf
+
+/* SRAM INIT Read/Write */
+#define SRAMID_INIT_READ 0x0
+#define SRAMID_INIT_WRITE 0x2
+#define SRAMID_INIT_DEFAULT 0x3
+
+/*
+ * defined VSYNC selection mode for DMA control 1 register
+ * DMA1 bit[30:28]
+ */
+#define VMODE_SMPN 0x0
+#define VMODE_SMPNIRQ 0x1
+#define VMODE_DUMB 0x2
+#define VMODE_IPE 0x3
+#define VMODE_IRE 0x4
+
+/*
+ * defined Configure Alpha and Alpha mode for DMA control 1 register
+ * DMA1 bit[15:08](alpha) / bit[17:16](alpha mode)
+ */
+/* ALPHA mode */
+#define MODE_ALPHA_DMA 0xa0
+#define MODE_ALPHA_GRA 0x1
+#define MODE_ALPHA_CFG 0x2
+
+/* alpha value */
+#define ALPHA_NOGRAPHIC 0xff /* all video, no graphic */
+#define ALPHA_NOVIDEO 0x00 /* all graphic, no video */
+#define ALPHA_GRAPHnVIDEO 0x0f /* Selects graphic & video */
+
+/*
+ * defined Pixel Command for DMA control 1 register
+ * DMA1 bit[07:00]
+ */
+#define PIXEL_CMD 0x81
+
+#endif /* _DOVE_LCD_H_ */
--
Ken ar c'hentañ | ** Breizh ha Linux atav! **
Jef | http://moinejf.free.fr/
More information about the linux-arm-kernel
mailing list