Message ID | 20130516132822.09b883cf@armhf (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thu, May 16, 2013 at 01:28:22PM +0200, Jean-Francois Moine wrote: > 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@free.fr> I want to review this as I have a competing driver which offers full support for the Dove cubox including a fully accelerated X server, and overlay support, page flipping and so forth.
Jean-François, Please run ./scripts/get_maintainer.pl on your patches before submitting. In this case: $ ./scripts/get_maintainer.pl -f drivers/gpu/drm/ David Airlie <airlied@linux.ie> (maintainer:DRM DRIVERS) dri-devel@lists.freedesktop.org (open list:DRM DRIVERS) linux-kernel@vger.kernel.org (open list) I've added them to the CC: thx, Jason. On Thu, May 16, 2013 at 01:28:22PM +0200, Jean-Francois Moine wrote: > 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@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@free.fr> > + * Sebastian Hesselbarth <sebastian.hesselbarth@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@free.fr> > + * Sebastian Hesselbarth <sebastian.hesselbarth@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@free.fr> > + * Sebastian Hesselbarth <sebastian.hesselbarth@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@free.fr>"); > +MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@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@free.fr> > + * Sebastian Hesselbarth <sebastian.hesselbarth@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@free.fr> > + * Sebastian Hesselbarth <sebastian.hesselbarth@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/ > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
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@free.fr> + * Sebastian Hesselbarth <sebastian.hesselbarth@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@free.fr> + * Sebastian Hesselbarth <sebastian.hesselbarth@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@free.fr> + * Sebastian Hesselbarth <sebastian.hesselbarth@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@free.fr>"); +MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@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@free.fr> + * Sebastian Hesselbarth <sebastian.hesselbarth@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@free.fr> + * Sebastian Hesselbarth <sebastian.hesselbarth@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_ */
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@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(+)