From: Tomi V. <tom...@no...> - 2009-01-12 12:35:47
|
New Display Subsystem for OMAP2/3 --------------------------------- This patch set implements new Display Subsystem (DSS) for OMAP2/3 processors. The DSS is still under work and these patches are for review. This is an updated version of the patch set I sent back in november: http://marc.info/?l=linux-fbdev-devel&m=122581504222406&w=2 The first patch is a doc file that tries to explain a bit how the drivers work. The patch set is based on the current linux-omap tree. You can find the patches also from a git tree at http://www.bat.org/~tomba/git/linux-omap-dss.git/ --- Tomi Valkeinen (12): DSS: Hacked N810 support DSS: Support for OMAP3 EVM board DSS: Support for OMAP3 SDP board DSS: Sharp LS037V7DW01 LCD Panel driver DSS: support for Beagle Board DSS: Add generic DVI panel DSS: OMAPFB: fb driver for new display subsystem DSS: DSI support DSS: VENC support DSS: RFBI support DSS: Display subsystem for OMAP2/3 DSS: Documentation for DSS2 Documentation/arm/OMAP/DSS | 270 ++ arch/arm/configs/dss_omap3_beagle_defconfig | 1437 +++++++++++ arch/arm/configs/dss_omap_3430sdp_defconfig | 1603 +++++++++++++ arch/arm/mach-omap2/board-3430sdp.c | 215 ++ arch/arm/mach-omap2/board-n800.c | 214 +- arch/arm/mach-omap2/board-omap3beagle.c | 101 + arch/arm/mach-omap2/board-omap3evm.c | 203 ++ arch/arm/plat-omap/Kconfig | 2 arch/arm/plat-omap/Makefile | 4 arch/arm/plat-omap/dss/Kconfig | 71 + arch/arm/plat-omap/dss/Makefile | 6 arch/arm/plat-omap/dss/dispc.c | 2113 +++++++++++++++++ arch/arm/plat-omap/dss/display.c | 787 ++++++ arch/arm/plat-omap/dss/dpi.c | 344 +++ arch/arm/plat-omap/dss/dsi.c | 3187 +++++++++++++++++++++++++ arch/arm/plat-omap/dss/dss.c | 774 ++++++ arch/arm/plat-omap/dss/dss.h | 274 ++ arch/arm/plat-omap/dss/rfbi.c | 1262 ++++++++++ arch/arm/plat-omap/dss/sdi.c | 174 + arch/arm/plat-omap/dss/venc.c | 506 ++++ arch/arm/plat-omap/fb-vram.c | 646 +++++ arch/arm/plat-omap/fb.c | 22 arch/arm/plat-omap/include/mach/display.h | 462 ++++ arch/arm/plat-omap/include/mach/omapfb.h | 14 drivers/video/Kconfig | 1 drivers/video/Makefile | 1 drivers/video/omap/Kconfig | 5 drivers/video/omap2/Kconfig | 62 drivers/video/omap2/Makefile | 8 drivers/video/omap2/ctrl-blizzard.c | 279 ++ drivers/video/omap2/omapfb-ioctl.c | 460 ++++ drivers/video/omap2/omapfb-main.c | 1442 +++++++++++ drivers/video/omap2/omapfb-sysfs.c | 901 +++++++ drivers/video/omap2/omapfb.h | 115 + drivers/video/omap2/panel-generic.c | 97 + drivers/video/omap2/panel-n800.c | 437 +++ drivers/video/omap2/panel-sharp-ls037v7dw01.c | 109 + 37 files changed, 18537 insertions(+), 71 deletions(-) create mode 100644 Documentation/arm/OMAP/DSS create mode 100644 arch/arm/configs/dss_omap3_beagle_defconfig create mode 100644 arch/arm/configs/dss_omap_3430sdp_defconfig create mode 100644 arch/arm/plat-omap/dss/Kconfig create mode 100644 arch/arm/plat-omap/dss/Makefile create mode 100644 arch/arm/plat-omap/dss/dispc.c create mode 100644 arch/arm/plat-omap/dss/display.c create mode 100644 arch/arm/plat-omap/dss/dpi.c create mode 100644 arch/arm/plat-omap/dss/dsi.c create mode 100644 arch/arm/plat-omap/dss/dss.c create mode 100644 arch/arm/plat-omap/dss/dss.h create mode 100644 arch/arm/plat-omap/dss/rfbi.c create mode 100644 arch/arm/plat-omap/dss/sdi.c create mode 100644 arch/arm/plat-omap/dss/venc.c create mode 100644 arch/arm/plat-omap/fb-vram.c create mode 100644 arch/arm/plat-omap/include/mach/display.h create mode 100644 drivers/video/omap2/Kconfig create mode 100644 drivers/video/omap2/Makefile create mode 100644 drivers/video/omap2/ctrl-blizzard.c create mode 100644 drivers/video/omap2/omapfb-ioctl.c create mode 100644 drivers/video/omap2/omapfb-main.c create mode 100644 drivers/video/omap2/omapfb-sysfs.c create mode 100644 drivers/video/omap2/omapfb.h create mode 100644 drivers/video/omap2/panel-generic.c create mode 100644 drivers/video/omap2/panel-n800.c create mode 100644 drivers/video/omap2/panel-sharp-ls037v7dw01.c -- Tomi Valkeinen |
From: Tomi V. <tom...@no...> - 2008-11-04 16:10:15
|
New Display Subsystem for OMAP2/3 --------------------------------- This patch set implements new Display Subsystem (DSS) for OMAP2/3 processors. The DSS is still under work and these patches are for review. Note that there is also another new DSS implementation from TI. The DSS has been tested on OMAP3 SDP board, Beagle Board and on two unreleased boards with DSI and SDI displays. The DSS used to work on OMAP2 board also, but it's been a while since I was able to test with OMAP2 board. The first patch is a doc file that tries to explain a bit how the drivers work. The patch set is based on the current linux-omap tree. You can find the patches also from a git tree at http://www.bat.org/~tomba/git/linux-omap-dss.git/ Note that you also need two patches from Mans Rullgard to make things work. These are needed to be able to reconfigure DSS functional clock. http://git.mansr.com/?p=linux-omap;a=commit;h=e2de5e5578fbaa9b4b75074796da0608fc93e6ae http://git.mansr.com/?p=linux-omap;a=commit;h=2b7b958dc79e51127d7a4ecf88ce12dbc6c31426 Questions/problems ------------------ Perhaps the biggest problems I have is the interface to user space. OMAP hardware doesn't quite fit in to the framebuffer framework. The problem is that a framebuffer can go to multiple overlays, and also the target display, to which a framebuffer goes, can change. In addition, the framebuffer size is used as overlay size, not display resolution. So a framebuffer != display. I believe DRM modesetting could be of help here, at least partially, but I haven't tried that approach yet. But DRM modesetting wouldn't solve all the problems. For example, we still need to configure overlays and overlay managers, and they don't quite belong to either the framebuffer side or the display side. Currently you configure them via a hackish sysfs interface, but I've been wondering if a /dev/omap-dss device with ioctls would be a better choice. There's currently no V4L2 support, but I have been thinking about that. I don't want to make any hardcoded configuration for those, because sometimes you want to use framebuffer devices for video overlays. So what I'd like to have is a way, compile time or run time, to configure which overlays go to framebuffer devices and which go to V4L2 devices. --- Tomi Valkeinen (9): DSS: support for OMAP3 SDP board DSS: support for Beagle Board DSS: Add generic DVI panel DSS: OMAPFB: fb driver for new display subsystem DSS: DSI support for OMAP2/3 DSS DSS: TV-out support for OMAP2/3 DSS DSS: RFBI support for OMAP2/3 DSS DSS: New display subsystem driver for OMAP2/3 DSS: Documentation for OMAP2/3 display subsystem Documentation/arm/OMAP/DSS | 240 ++ arch/arm/mach-omap2/board-3430sdp.c | 234 ++ arch/arm/mach-omap2/board-omap3beagle.c | 121 + arch/arm/plat-omap/Kconfig | 2 arch/arm/plat-omap/Makefile | 2 arch/arm/plat-omap/dss/Kconfig | 66 + arch/arm/plat-omap/dss/Makefile | 6 arch/arm/plat-omap/dss/dispc.c | 1667 ++++++++++++++++ arch/arm/plat-omap/dss/display.c | 781 +++++++ arch/arm/plat-omap/dss/dpi.c | 303 +++ arch/arm/plat-omap/dss/dsi.c | 3027 +++++++++++++++++++++++++++++ arch/arm/plat-omap/dss/dss.c | 547 +++++ arch/arm/plat-omap/dss/dss.h | 240 ++ arch/arm/plat-omap/dss/rfbi.c | 1234 ++++++++++++ arch/arm/plat-omap/dss/sdi.c | 154 + arch/arm/plat-omap/dss/venc.c | 499 +++++ arch/arm/plat-omap/fb.c | 9 arch/arm/plat-omap/include/mach/display.h | 458 ++++ arch/arm/plat-omap/include/mach/omapfb.h | 7 drivers/video/Kconfig | 1 drivers/video/Makefile | 1 drivers/video/omap/Kconfig | 5 drivers/video/omap2/Kconfig | 54 + drivers/video/omap2/Makefile | 5 drivers/video/omap2/omapfb-ioctl.c | 428 ++++ drivers/video/omap2/omapfb-main.c | 1247 ++++++++++++ drivers/video/omap2/omapfb-sysfs.c | 833 ++++++++ drivers/video/omap2/omapfb.h | 104 + drivers/video/omap2/panel-dvi.c | 121 + drivers/video/omap2/panel-sdp3430.c | 110 + 30 files changed, 12479 insertions(+), 27 deletions(-) create mode 100644 Documentation/arm/OMAP/DSS create mode 100644 arch/arm/plat-omap/dss/Kconfig create mode 100644 arch/arm/plat-omap/dss/Makefile create mode 100644 arch/arm/plat-omap/dss/dispc.c create mode 100644 arch/arm/plat-omap/dss/display.c create mode 100644 arch/arm/plat-omap/dss/dpi.c create mode 100644 arch/arm/plat-omap/dss/dsi.c create mode 100644 arch/arm/plat-omap/dss/dss.c create mode 100644 arch/arm/plat-omap/dss/dss.h create mode 100644 arch/arm/plat-omap/dss/rfbi.c create mode 100644 arch/arm/plat-omap/dss/sdi.c create mode 100644 arch/arm/plat-omap/dss/venc.c create mode 100644 arch/arm/plat-omap/include/mach/display.h create mode 100644 drivers/video/omap2/Kconfig create mode 100644 drivers/video/omap2/Makefile create mode 100644 drivers/video/omap2/omapfb-ioctl.c create mode 100644 drivers/video/omap2/omapfb-main.c create mode 100644 drivers/video/omap2/omapfb-sysfs.c create mode 100644 drivers/video/omap2/omapfb.h create mode 100644 drivers/video/omap2/panel-dvi.c create mode 100644 drivers/video/omap2/panel-sdp3430.c -- Tomi Valkeinen |
From: Tomi V. <tom...@no...> - 2008-11-04 16:10:21
|
DSI, RFBI and VENC are separate patches Signed-off-by: Tomi Valkeinen <tom...@no...> --- arch/arm/plat-omap/Kconfig | 2 arch/arm/plat-omap/Makefile | 2 arch/arm/plat-omap/dss/Kconfig | 66 + arch/arm/plat-omap/dss/Makefile | 6 arch/arm/plat-omap/dss/dispc.c | 1667 +++++++++++++++++++++++++++++ arch/arm/plat-omap/dss/display.c | 781 ++++++++++++++ arch/arm/plat-omap/dss/dpi.c | 303 +++++ arch/arm/plat-omap/dss/dss.c | 547 ++++++++++ arch/arm/plat-omap/dss/dss.h | 240 ++++ arch/arm/plat-omap/dss/sdi.c | 154 +++ arch/arm/plat-omap/include/mach/display.h | 458 ++++++++ 11 files changed, 4226 insertions(+), 0 deletions(-) create mode 100644 arch/arm/plat-omap/dss/Kconfig create mode 100644 arch/arm/plat-omap/dss/Makefile create mode 100644 arch/arm/plat-omap/dss/dispc.c create mode 100644 arch/arm/plat-omap/dss/display.c create mode 100644 arch/arm/plat-omap/dss/dpi.c create mode 100644 arch/arm/plat-omap/dss/dss.c create mode 100644 arch/arm/plat-omap/dss/dss.h create mode 100644 arch/arm/plat-omap/dss/sdi.c create mode 100644 arch/arm/plat-omap/include/mach/display.h diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index 960c13f..4e90667 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig @@ -245,6 +245,8 @@ config OMAP_SERIAL_WAKE to data on the serial RX line. This allows you to wake the system from serial console. +source "arch/arm/plat-omap/dss/Kconfig" + endmenu endif diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 1259846..2740497 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -29,3 +29,5 @@ obj-$(CONFIG_OMAP_MMU_FWK) += mmu.o # OMAP mailbox framework obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox.o +# OMAP2/3 Display Subsystem +obj-y += dss/ diff --git a/arch/arm/plat-omap/dss/Kconfig b/arch/arm/plat-omap/dss/Kconfig new file mode 100644 index 0000000..150cd24 --- /dev/null +++ b/arch/arm/plat-omap/dss/Kconfig @@ -0,0 +1,66 @@ +config OMAP2_DSS + tristate "OMAP2/3 Display Subsystem support (EXPERIMENTAL)" + depends on ARCH_OMAP2 || ARCH_OMAP3 + help + OMAP2/3 Display Subsystem support. + +if OMAP2_DSS + +config OMAP2_DSS_DEBUG + bool "Debug output" + default n + +config OMAP2_DSS_RFBI + bool "RFBI support" + default y + +config OMAP2_DSS_VENC + bool "VENC support" + default y + +if ARCH_OMAP3 + +config OMAP2_DSS_SDI + bool "SDI support" + default y + +config OMAP2_DSS_DSI + bool "DSI support" + default y + +endif + +config OMAP2_DSS_USE_DSI_PLL + bool "Use DSI PLL for PCLK (EXPERIMENTAL)" + default n + depends on OMAP2_DSS_DSI + help + Use DSI PLL to generate pixel clock. + Currently only for DPI output. + +config OMAP2_DSS_FAKE_VSYNC + bool "Fake VSYNC irq from manual update displays" + default n + help + If this is selected, DSI will fake a DISPC VSYNC interrupt + when DSI has sent a frame. + +config OMAP2_DSS_MIN_FCK_PER_PCK + int "Minimum FCK/PCK ratio (for scaling)" + range 1 32 + default 4 + help + This can be used to adjust the minimum FCK/PCK ratio. + + With this you can make sure that DISPC FCK is at least + n x PCK. Video plane scaling requires higher FCK than + normally. + + If this is set to 1, there's no extra constraint on the + DISPC FCK. However, the FCK will at minimum be + 2xPCK (if active matrix) or 3xPCK (if passive matrix). + + Max FCK is 173MHz, so this doesn't work if your PCK + is very high. + +endif diff --git a/arch/arm/plat-omap/dss/Makefile b/arch/arm/plat-omap/dss/Makefile new file mode 100644 index 0000000..e98c6c1 --- /dev/null +++ b/arch/arm/plat-omap/dss/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_OMAP2_DSS) += omap-dss.o +omap-dss-y := dss.o display.o dispc.o dpi.o +omap-dss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o +omap-dss-$(CONFIG_OMAP2_DSS_VENC) += venc.o +omap-dss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o +omap-dss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o diff --git a/arch/arm/plat-omap/dss/dispc.c b/arch/arm/plat-omap/dss/dispc.c new file mode 100644 index 0000000..8f5da2d --- /dev/null +++ b/arch/arm/plat-omap/dss/dispc.c @@ -0,0 +1,1667 @@ +/* + * linux/arch/arm/plat-omap/dss/dispc.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tom...@no...> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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/>. + */ + +#define DSS_SUBSYS_NAME "DISPC" + +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/jiffies.h> + +#include <mach/sram.h> +#include <mach/board.h> +#include <mach/clock.h> + +#include <mach/display.h> + +#include "dss.h" + +/* DISPC */ +#define DISPC_BASE 0x48050400 + +struct dispc_reg { u16 idx; }; + +#define DISPC_REG(idx) ((const struct dispc_reg) { idx }) + +/* DISPC common */ +#define DISPC_REVISION DISPC_REG(0x0000) +#define DISPC_SYSCONFIG DISPC_REG(0x0010) +#define DISPC_SYSSTATUS DISPC_REG(0x0014) +#define DISPC_IRQSTATUS DISPC_REG(0x0018) +#define DISPC_IRQENABLE DISPC_REG(0x001C) +#define DISPC_CONTROL DISPC_REG(0x0040) +#define DISPC_CONFIG DISPC_REG(0x0044) +#define DISPC_CAPABLE DISPC_REG(0x0048) +#define DISPC_DEFAULT_COLOR0 DISPC_REG(0x004C) +#define DISPC_DEFAULT_COLOR1 DISPC_REG(0x0050) +#define DISPC_TRANS_COLOR0 DISPC_REG(0x0054) +#define DISPC_TRANS_COLOR1 DISPC_REG(0x0058) +#define DISPC_LINE_STATUS DISPC_REG(0x005C) +#define DISPC_LINE_NUMBER DISPC_REG(0x0060) +#define DISPC_TIMING_H DISPC_REG(0x0064) +#define DISPC_TIMING_V DISPC_REG(0x0068) +#define DISPC_POL_FREQ DISPC_REG(0x006C) +#define DISPC_DIVISOR DISPC_REG(0x0070) +#define DISPC_SIZE_DIG DISPC_REG(0x0078) +#define DISPC_SIZE_LCD DISPC_REG(0x007C) + +#define DISPC_DATA_CYCLE1 DISPC_REG(0x01D4) +#define DISPC_DATA_CYCLE2 DISPC_REG(0x01D8) +#define DISPC_DATA_CYCLE3 DISPC_REG(0x01DC) + +/* DISPC GFX plane */ +#define DISPC_GFX_BA0 DISPC_REG(0x0080) +#define DISPC_GFX_BA1 DISPC_REG(0x0084) +#define DISPC_GFX_POSITION DISPC_REG(0x0088) +#define DISPC_GFX_SIZE DISPC_REG(0x008C) +#define DISPC_GFX_ATTRIBUTES DISPC_REG(0x00A0) +#define DISPC_GFX_FIFO_THRESHOLD DISPC_REG(0x00A4) +#define DISPC_GFX_FIFO_SIZE_STATUS DISPC_REG(0x00A8) +#define DISPC_GFX_ROW_INC DISPC_REG(0x00AC) +#define DISPC_GFX_PIXEL_INC DISPC_REG(0x00B0) +#define DISPC_GFX_WINDOW_SKIP DISPC_REG(0x00B4) +#define DISPC_GFX_TABLE_BA DISPC_REG(0x00B8) + +/* DISPC Video plane, n = 0 for VID1 and n = 1 for VID2 */ +#define DISPC_VID_REG(n, idx) DISPC_REG(0x00BC + (n)*0x90 + idx) + +#define DISPC_VID_BA0(n) DISPC_VID_REG(n, 0x0000) +#define DISPC_VID_BA1(n) DISPC_VID_REG(n, 0x0004) +#define DISPC_VID_POSITION(n) DISPC_VID_REG(n, 0x0008) +#define DISPC_VID_SIZE(n) DISPC_VID_REG(n, 0x000C) +#define DISPC_VID_ATTRIBUTES(n) DISPC_VID_REG(n, 0x0010) +#define DISPC_VID_FIFO_THRESHOLD(n) DISPC_VID_REG(n, 0x0014) +#define DISPC_VID_FIFO_SIZE_STATUS(n) DISPC_VID_REG(n, 0x0018) +#define DISPC_VID_ROW_INC(n) DISPC_VID_REG(n, 0x001C) +#define DISPC_VID_PIXEL_INC(n) DISPC_VID_REG(n, 0x0020) +#define DISPC_VID_FIR(n) DISPC_VID_REG(n, 0x0024) +#define DISPC_VID_PICTURE_SIZE(n) DISPC_VID_REG(n, 0x0028) +#define DISPC_VID_ACCU0(n) DISPC_VID_REG(n, 0x002C) +#define DISPC_VID_ACCU1(n) DISPC_VID_REG(n, 0x0030) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_H(n, i) DISPC_REG(0x00F0 + (n)*0x90 + (i)*0x8) +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_HV(n, i) DISPC_REG(0x00F4 + (n)*0x90 + (i)*0x8) +/* coef index i = {0, 1, 2, 3, 4} */ +#define DISPC_VID_CONV_COEF(n, i) DISPC_REG(0x0130 + (n)*0x90 + (i)*0x4) + +#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ + DISPC_IRQ_OCP_ERR | \ + DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ + DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ + DISPC_IRQ_SYNC_LOST) +/*DISPC_IRQ_SYNC_LOST_DIGIT*/ + +#define DISPC_MAX_NR_ISRS 8 + +static struct { + omap_dispc_isr_t isr; + void *arg; + u32 mask; +} registered_isr[DISPC_MAX_NR_ISRS]; + +#define REG_GET(idx, start, end) \ + FLD_GET(dispc_read_reg(idx), start, end) + +#define REG_FLD_MOD(idx, val, start, end) \ + dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end)) + +static const struct dispc_reg dispc_reg_att[] = { DISPC_GFX_ATTRIBUTES, + DISPC_VID_ATTRIBUTES(0), + DISPC_VID_ATTRIBUTES(1) }; + +static struct { + void __iomem *base; + + struct clk *dss_ick; + struct clk *dss1_fck; + struct clk *dss_54m_fck; + struct clk *dpll4_m4_ck; +} dispc; + +static spinlock_t dss_lock; + +static inline void enable_clocks(int enable) +{ + if (enable) { + clk_enable(dispc.dss_ick); + clk_enable(dispc.dss1_fck); + } else { + clk_disable(dispc.dss1_fck); + clk_disable(dispc.dss_ick); + } +} + +static inline void dispc_write_reg(const struct dispc_reg idx, u32 val) +{ + __raw_writel(val, dispc.base + idx.idx); +} + +static inline u32 dispc_read_reg(const struct dispc_reg idx) +{ + return __raw_readl(dispc.base + idx.idx); +} + +void dispc_go(enum omap_channel channel) +{ + int bit; + unsigned long tmo; + + enable_clocks(1); + + if (channel == OMAP_DSS_CHANNEL_LCD) + bit = 0; /* LCDENABLE */ + else + bit = 1; /* DIGITALENABLE */ + + /* if the channel is not enabled, we don't need GO */ + if (REG_GET(DISPC_CONTROL, bit, bit) == 0) + goto end; + + if (channel == OMAP_DSS_CHANNEL_LCD) + bit = 5; /* GOLCD */ + else + bit = 6; /* GODIGIT */ + + tmo = jiffies + msecs_to_jiffies(200); + while (REG_GET(DISPC_CONTROL, bit, bit) == 1) { + if (time_after(jiffies, tmo)) { + DSSERR("timeout waiting GO flag\n"); + goto end; + } + cpu_relax(); + } + + DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : "DIGIT"); + + REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); +end: + enable_clocks(0); +} + +static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(DISPC_VID_FIR_COEF_H(plane-1, reg), value); +} + +static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(DISPC_VID_FIR_COEF_HV(plane-1, reg), value); +} + + +static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, + int vscaleup) +{ + /* Coefficients for horizontal up-sampling */ + const u32 coef_hup[8] = { + 0x00800000, + 0x0D7CF800, + 0x1E70F5FF, + 0x335FF5FE, + 0xF74949F7, + 0xF55F33FB, + 0xF5701EFE, + 0xF87C0DFF, + }; + + /* Coefficients for horizontal down-sampling */ + const u32 coef_hdown[8] = { + 0x24382400, + 0x28371FFE, + 0x2C361BFB, + 0x303516F9, + 0x11343311, + 0x1635300C, + 0x1B362C08, + 0x1F372804, + }; + + /* Coefficients for horizontal and vertical up-sampling */ + const u32 coef_hvup[8] = { + 0x00800000, + 0x037B02FF, + 0x0C6F05FE, + 0x205907FB, + 0x00404000, + 0x075920FE, + 0x056F0CFF, + 0x027B0300, + }; + + /* Coefficients for horizontal and vertical down-sampling */ + const u32 coef_hvdown[8] = { + 0x24382400, + 0x28391F04, + 0x2D381B08, + 0x3237170C, + 0x123737F7, + 0x173732F9, + 0x1B382DFB, + 0x1F3928FE, + }; + + const u32 *h_coef; + const u32 *hv_coef; + const u32 *hv_coef_mod; + int i; + + if (hscaleup) + h_coef = coef_hup; + else + h_coef = coef_hdown; + + if (vscaleup) { + hv_coef = coef_hvup; + + if (hscaleup) + hv_coef_mod = NULL; + else + hv_coef_mod = coef_hvdown; + } else { + hv_coef = coef_hvdown; + + if (hscaleup) + hv_coef_mod = coef_hvup; + else + hv_coef_mod = NULL; + } + + for (i = 0; i < 8; i++) { + u32 h, hv; + + h = h_coef[i]; + + hv = hv_coef[i]; + + if (hv_coef_mod) { + hv &= 0xffffff00; + hv |= (hv_coef_mod[i] & 0xff); + } + + _dispc_write_firh_reg(plane, i, h); + _dispc_write_firhv_reg(plane, i, hv); + } +} + +static void _dispc_setup_color_conv_coef(void) +{ + const struct color_conv_coef { + int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb; + int full_range; + } ctbl_bt601_5 = { + 298, 409, 0, 298, -208, -100, 298, 0, 517, 0, + }; + + const struct color_conv_coef *ct; + +#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0)) + + ct = &ctbl_bt601_5; + + dispc_write_reg(DISPC_VID_CONV_COEF(0, 0), CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 1), CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 2), CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 3), CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 4), CVAL(0, ct->bcb)); + + dispc_write_reg(DISPC_VID_CONV_COEF(1, 0), CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 1), CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 2), CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 3), CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 4), CVAL(0, ct->bcb)); + +#undef CVAL + + REG_FLD_MOD(DISPC_VID_ATTRIBUTES(0), ct->full_range, 11, 11); + REG_FLD_MOD(DISPC_VID_ATTRIBUTES(1), ct->full_range, 11, 11); +} + + +static void _dispc_set_plane_ba0(enum omap_plane plane, u32 paddr) +{ + const struct dispc_reg ba0_reg[] = { DISPC_GFX_BA0, + DISPC_VID_BA0(0), + DISPC_VID_BA0(1) }; + + dispc_write_reg(ba0_reg[plane], paddr); +} + +static void _dispc_set_plane_ba1(enum omap_plane plane, u32 paddr) +{ + const struct dispc_reg ba1_reg[] = { DISPC_GFX_BA1, + DISPC_VID_BA1(0), + DISPC_VID_BA1(1) }; + + dispc_write_reg(ba1_reg[plane], paddr); +} + +static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y) +{ + const struct dispc_reg pos_reg[] = { DISPC_GFX_POSITION, + DISPC_VID_POSITION(0), + DISPC_VID_POSITION(1) }; + + u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0); + dispc_write_reg(pos_reg[plane], val); +} + +static void _dispc_set_pic_size(enum omap_plane plane, int width, int height) +{ + const struct dispc_reg siz_reg[] = { DISPC_GFX_SIZE, + DISPC_VID_PICTURE_SIZE(0), + DISPC_VID_PICTURE_SIZE(1) }; + u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + dispc_write_reg(siz_reg[plane], val); +} + +static void _dispc_set_vid_size(enum omap_plane plane, int width, int height) +{ + u32 val; + const struct dispc_reg vsi_reg[] = { DISPC_VID_SIZE(0), + DISPC_VID_SIZE(1) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + dispc_write_reg(vsi_reg[plane-1], val); +} + +static void _dispc_set_row_inc(enum omap_plane plane, int inc) +{ + const struct dispc_reg ri_reg[] = { DISPC_GFX_ROW_INC, + DISPC_VID_ROW_INC(0), + DISPC_VID_ROW_INC(1) }; + + dispc_write_reg(ri_reg[plane], inc); +} + +static void _dispc_set_color_mode(enum omap_plane plane, + enum omap_color_mode color_mode) +{ + u32 m = 0; + + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + m = 0x0; break; + case OMAP_DSS_COLOR_CLUT2: + m = 0x1; break; + case OMAP_DSS_COLOR_CLUT4: + m = 0x2; break; + case OMAP_DSS_COLOR_CLUT8: + m = 0x3; break; + case OMAP_DSS_COLOR_RGB12U: + m = 0x4; break; + case OMAP_DSS_COLOR_ARGB16: + m = 0x5; break; + case OMAP_DSS_COLOR_RGB16: + m = 0x6; break; + case OMAP_DSS_COLOR_RGB24U: + m = 0x8; break; + case OMAP_DSS_COLOR_RGB24P: + m = 0x9; break; + case OMAP_DSS_COLOR_YUV2: + m = 0xa; break; + case OMAP_DSS_COLOR_UYVY: + m = 0xb; break; + case OMAP_DSS_COLOR_ARGB32: + m = 0xc; break; + case OMAP_DSS_COLOR_RGBA32: + m = 0xd; break; + case OMAP_DSS_COLOR_RGBX32: + m = 0xe; break; + default: + BUG(); break; + } + + REG_FLD_MOD(dispc_reg_att[plane], m, 4, 1); +} + +static void _dispc_set_channel_out(enum omap_plane plane, + enum omap_channel channel) +{ + int shift; + u32 val; + + switch (plane) { + case OMAP_DSS_GFX: + shift = 8; + break; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + shift = 16; + break; + default: + BUG(); + return; + } + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, channel, shift, shift); + dispc_write_reg(dispc_reg_att[plane], val); +} + +static void _dispc_set_burst_size(enum omap_plane plane, + enum omap_burst_size burst_size) +{ + int shift; + u32 val; + + switch (plane) { + case OMAP_DSS_GFX: + shift = 6; + break; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + shift = 14; + break; + default: + BUG(); + return; + } + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, burst_size, shift+1, shift); + dispc_write_reg(dispc_reg_att[plane], val); +} + +static void _dispc_set_vid_color_conv(enum omap_plane plane, int enable) +{ + u32 val; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, enable, 9, 9); + dispc_write_reg(dispc_reg_att[plane], val); +} + +void dispc_set_lcd_size(int width, int height) +{ + u32 val; + BUG_ON((width > (1 << 11)) || (height > (1 << 11))); + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + enable_clocks(1); + dispc_write_reg(DISPC_SIZE_LCD, val); + enable_clocks(0); +} + +void dispc_set_digit_size(int width, int height) +{ + u32 val; + BUG_ON((width > (1 << 11)) || (height > (1 << 11))); + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + enable_clocks(1); + dispc_write_reg(DISPC_SIZE_DIG, val); + enable_clocks(0); +} + +void dispc_setup_plane_fifo(enum omap_plane plane, int ext_mode) +{ + const struct dispc_reg ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD, + DISPC_VID_FIFO_THRESHOLD(0), + DISPC_VID_FIFO_THRESHOLD(1) }; + const struct dispc_reg fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS, + DISPC_VID_FIFO_SIZE_STATUS(0), + DISPC_VID_FIFO_SIZE_STATUS(1) }; + int low, high; + u32 size; + + enable_clocks(1); + + if (cpu_is_omap24xx()) + size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 8, 0); + else if (cpu_is_omap34xx()) + size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 10, 0); + else + BUG(); + + if (ext_mode) { + low = size * 3 / 4; + high = size; + } else { + low = size / 4; + high = size * 3 / 4; + } + + if (cpu_is_omap24xx()) + dispc_write_reg(ftrs_reg[plane], + FLD_VAL(high, 24, 16) | FLD_VAL(low, 8, 0)); + else + dispc_write_reg(ftrs_reg[plane], + FLD_VAL(high, 27, 16) | FLD_VAL(low, 11, 0)); + + enable_clocks(0); +} + +static void _dispc_set_fir(enum omap_plane plane, int hinc, int vinc) +{ + u32 val; + const struct dispc_reg fir_reg[] = { DISPC_VID_FIR(0), + DISPC_VID_FIR(1) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(vinc, 27, 16) | FLD_VAL(hinc, 11, 0); + dispc_write_reg(fir_reg[plane-1], val); +} + +static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + const struct dispc_reg ac0_reg[] = { DISPC_VID_ACCU0(0), + DISPC_VID_ACCU0(1) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); + dispc_write_reg(ac0_reg[plane-1], val); +} + +static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + const struct dispc_reg ac1_reg[] = { DISPC_VID_ACCU1(0), + DISPC_VID_ACCU1(1) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); + dispc_write_reg(ac1_reg[plane-1], val); +} + + +static void _dispc_set_scaling(enum omap_plane plane, + int orig_width, int orig_height, + int out_width, int out_height, + int ilace) +{ + int fir_hinc; + int fir_vinc; + int hscaleup, vscaleup; + int fieldmode = 0; + int accu0 = 0; + int accu1 = 0; + u32 l; + + BUG_ON(plane == OMAP_DSS_GFX); + + hscaleup = orig_width <= out_width; + vscaleup = orig_height <= out_height; + + _dispc_set_scale_coef(plane, hscaleup, vscaleup); + + if (!orig_width || orig_width == out_width) + fir_hinc = 0; + else + fir_hinc = 1024 * orig_width / out_width; + + if (!orig_height || orig_height == out_height) + fir_vinc = 0; + else + fir_vinc = 1024 * orig_height / out_height; + + _dispc_set_fir(plane, fir_hinc, fir_vinc); + + l = dispc_read_reg(dispc_reg_att[plane]); + l &= ~(0x0f << 5); + + l |= fir_hinc ? (1 << 5) : 0; + l |= fir_vinc ? (1 << 6) : 0; + + l |= hscaleup ? 0 : (1 << 7); + l |= vscaleup ? 0 : (1 << 8); + + dispc_write_reg(dispc_reg_att[plane], l); + + if (ilace) { + if (fieldmode) { + accu0 = fir_vinc / 2; + accu1 = 0; + } else { + accu0 = 0; + accu1 = fir_vinc / 2; + if (accu1 >= 1024/2) { + accu0 = 1024/2; + accu1 -= accu0; + } + } + } + + _dispc_set_vid_accu0(plane, 0, accu0); + _dispc_set_vid_accu1(plane, 0, accu1); +} + +static int _dispc_setup_plane(enum omap_plane plane, + enum omap_channel channel_out, + u32 paddr, int screen_width, + int pos_x, int pos_y, + int width, int height, + int out_width, int out_height, + enum omap_color_mode color_mode, + int ilace) +{ + int fieldmode = 0; + int bpp; + int cconv; + int scaling = 0; + + if (plane == OMAP_DSS_GFX) { + if (width != out_width || height != out_height) + return -EINVAL; + } else { + /* video plane */ + if (width != out_width || height != out_height) + scaling = 1; + + if (out_width < width/2 || + out_width > width*8) + return -EINVAL; + + if (out_height < height/2 || + out_height > height*8) + return -EINVAL; + } + + + switch (color_mode) { + case OMAP_DSS_COLOR_RGB16: + bpp = 16; + cconv = 0; + break; + + case OMAP_DSS_COLOR_RGB24P: + bpp = 24; + cconv = 0; + break; + + case OMAP_DSS_COLOR_RGB24U: + bpp = 32; + cconv = 0; + break; + + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + BUG_ON(plane == OMAP_DSS_GFX); + bpp = 16; + cconv = 1; + break; + + default: + BUG(); + return 1; + } + + if (ilace) { + if (height == out_height || height > out_height) + fieldmode = 1; + } + + if (fieldmode) + height /= 2; + + if (ilace) + out_height /= 2; + + if (plane != OMAP_DSS_GFX) + _dispc_set_scaling(plane, width, height, + out_width, out_height, + ilace); + + /* attributes */ + _dispc_set_channel_out(plane, channel_out); + _dispc_set_color_mode(plane, color_mode); + if (plane != OMAP_DSS_GFX) + _dispc_set_vid_color_conv(plane, cconv); + + /* */ + + _dispc_set_plane_ba0(plane, paddr); + + if (fieldmode) + _dispc_set_plane_ba1(plane, paddr + screen_width * bpp/8); + else + _dispc_set_plane_ba1(plane, paddr); + + + _dispc_set_plane_pos(plane, pos_x, pos_y); + + _dispc_set_pic_size(plane, width, height); + + if (plane != OMAP_DSS_GFX) + _dispc_set_vid_size(plane, out_width, out_height); + + _dispc_set_row_inc(plane, + (screen_width - width) * bpp / 8 + + (fieldmode ? screen_width * bpp / 8 : 0) + + 1); + + return 0; +} + +static void _dispc_enable_plane(enum omap_plane plane, int enable) +{ + REG_FLD_MOD(dispc_reg_att[plane], enable ? 1 : 0, 0, 0); +} + + +void dispc_enable_lcd_out(int enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0); + enable_clocks(0); +} + +void dispc_enable_digit_out(int enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1); + enable_clocks(0); +} + +void dispc_lcd_enable_signal_polarity(int act_high) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29); + enable_clocks(0); +} + +void dispc_lcd_enable_signal(int enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28); + enable_clocks(0); +} + +void dispc_pck_free_enable(int enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27); + enable_clocks(0); +} + +void dispc_enable_fifohandcheck(int enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16); + enable_clocks(0); +} + + +static inline void get_dss_clocks(void) +{ + dispc.dss_ick = get_dss_ick(); + dispc.dss1_fck = get_dss1_fck(); + dispc.dss_54m_fck = get_tv_fck(); +} + +void dispc_set_lcd_display_type(enum omap_lcd_display_type type) +{ + int mode; + + switch (type) { + case OMAP_DSS_LCD_DISPLAY_STN: + mode = 0; + break; + + case OMAP_DSS_LCD_DISPLAY_TFT: + mode = 1; + break; + + default: + BUG(); + return; + } + + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3); + enable_clocks(0); +} + +void dispc_set_loadmode(enum omap_dss_load_mode mode) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1); + enable_clocks(0); +} + + +void omap_dispc_set_default_color(enum omap_channel channel, u32 color) +{ + const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0, + DISPC_DEFAULT_COLOR1 }; + + enable_clocks(1); + dispc_write_reg(def_reg[channel], color); + enable_clocks(0); +} + +void omap_dispc_set_trans_key(enum omap_channel ch, + enum omap_dss_color_key_type type, + u32 trans_key) +{ + const struct dispc_reg tr_reg[] = { + DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 }; + + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + REG_FLD_MOD(DISPC_CONFIG, type, 11, 11); + else /* OMAP_DSS_CHANNEL_DIGIT */ + REG_FLD_MOD(DISPC_CONFIG, type, 13, 13); + + dispc_write_reg(tr_reg[ch], trans_key); + enable_clocks(0); +} + +void omap_dispc_enable_trans_key(enum omap_channel ch, int enable) +{ + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10); + else /* OMAP_DSS_CHANNEL_DIGIT */ + REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12); + enable_clocks(0); +} + +void dispc_set_tft_data_lines(int data_lines) +{ + int code; + + switch (data_lines) { + case 12: + code = 0; + break; + case 16: + code = 1; + break; + case 18: + code = 2; + break; + case 24: + code = 3; + break; + default: + BUG(); + return; + } + + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, code, 9, 8); + enable_clocks(0); +} + +void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode) +{ + u32 l; + int stallmode; + int gpout0 = 1; + int gpout1; + + switch (mode) { + case OMAP_DSS_PARALLELMODE_BYPASS: + stallmode = 0; + gpout1 = 1; + break; + + case OMAP_DSS_PARALLELMODE_RFBI: + stallmode = 1; + gpout1 = 0; + break; + + case OMAP_DSS_PARALLELMODE_DSI: + stallmode = 1; + gpout1 = 1; + break; + + default: + BUG(); + return; + } + + enable_clocks(1); + + l = dispc_read_reg(DISPC_CONTROL); + + l = FLD_MOD(l, stallmode, 11, 11); + l = FLD_MOD(l, gpout0, 15, 15); + l = FLD_MOD(l, gpout1, 16, 16); + + dispc_write_reg(DISPC_CONTROL, l); + + enable_clocks(0); +} + +static void _dispc_set_lcd_timings(int hsw, int hfp, int hbp, + int vsw, int vfp, int vbp) +{ + u32 timing_h, timing_v; + + BUG_ON(hsw < 1 || hsw > 64); + BUG_ON(hfp < 1 || hfp > 256); + BUG_ON(hbp < 1 || hbp > 256); + + BUG_ON(vsw < 1 || vsw > 64); + BUG_ON(vfp < 0 || vfp > 255); + BUG_ON(vbp < 0 || vbp > 255); + + timing_h = FLD_VAL(hsw-1, 5, 0) | FLD_VAL(hfp-1, 15, 8) | + FLD_VAL(hbp-1, 27, 20); + + timing_v = FLD_VAL(vsw-1, 5, 0) | FLD_VAL(vfp, 15, 8) | + FLD_VAL(vbp, 27, 20); + + enable_clocks(1); + dispc_write_reg(DISPC_TIMING_H, timing_h); + dispc_write_reg(DISPC_TIMING_V, timing_v); + enable_clocks(0); +} + +void dispc_set_lcd_timings(struct omap_video_timings *timings) +{ + _dispc_set_lcd_timings(timings->hsw, timings->hfp, timings->hbp, + timings->vsw, timings->vfp, timings->vbp); +} + +void dispc_set_lcd_divisor(int lck_div, int pck_div) +{ + BUG_ON(lck_div < 1); + BUG_ON(pck_div < 2); + + enable_clocks(1); + dispc_write_reg(DISPC_DIVISOR, + FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); + enable_clocks(0); +} + +static void dispc_get_lcd_divisor(int *lck_div, int *pck_div) +{ + u32 l; + l = dispc_read_reg(DISPC_DIVISOR); + *lck_div = FLD_GET(l, 23, 16); + *pck_div = FLD_GET(l, 7, 0); +} + +unsigned long dispc_fclk_rate(void) +{ + unsigned long r = 0; + + if (dss_get_dispc_clk_source() == 0) + r = clk_get_rate(dispc.dss1_fck); + else +#ifdef CONFIG_OMAP2_DSS_DSI + r = dsi_get_dsi1_pll_rate(); +#else + BUG(); +#endif + return r; +} + +unsigned long dispc_pclk_rate(void) +{ + int lcd, pcd; + unsigned long r; + u32 l; + + l = dispc_read_reg(DISPC_DIVISOR); + + lcd = FLD_GET(l, 23, 16); + pcd = FLD_GET(l, 7, 0); + + r = dispc_fclk_rate(); + + return r / lcd / pcd; +} + +ssize_t dispc_print_clocks(char *buf, ssize_t size) +{ + ssize_t l = 0; + int lcd, pcd; + + enable_clocks(1); + + dispc_get_lcd_divisor(&lcd, &pcd); + + l += snprintf(buf + l, size - l, "- dispc -\n"); + + l += snprintf(buf + l, size - l, "dispc fclk source = %s\n", + dss_get_dispc_clk_source() == 0 ? + "dss1_alwon_fclk" : "dsi1_pll_fclk"); + + l += snprintf(buf + l, size - l, + "pixel clk = %lu / %d / %d = %lu\n", + dispc_fclk_rate(), + lcd, pcd, + dispc_pclk_rate()); + + enable_clocks(0); + + return l; +} + +static void _dispc_set_pol_freq(int onoff, int rf, int ieo, int ipc, + int ihs, int ivs, int acbi, int acb) +{ + u32 l = 0; + + DSSDBG("polfreq ihs %d, ivs %d, acb %d\n", ihs, ivs, acb); + + l |= FLD_VAL(onoff, 17, 17); + l |= FLD_VAL(rf, 16, 16); + l |= FLD_VAL(ieo, 15, 15); + l |= FLD_VAL(ipc, 14, 14); + l |= FLD_VAL(ihs, 13, 13); + l |= FLD_VAL(ivs, 12, 12); + l |= FLD_VAL(acbi, 11, 8); + l |= FLD_VAL(acb, 7, 0); + + enable_clocks(1); + dispc_write_reg(DISPC_POL_FREQ, l); + enable_clocks(0); +} + +void dispc_set_pol_freq(struct omap_panel *panel) +{ + _dispc_set_pol_freq((panel->config & OMAP_DSS_LCD_ONOFF) != 0, + (panel->config & OMAP_DSS_LCD_RF) != 0, + (panel->config & OMAP_DSS_LCD_IEO) != 0, + (panel->config & OMAP_DSS_LCD_IPC) != 0, + (panel->config & OMAP_DSS_LCD_IHS) != 0, + (panel->config & OMAP_DSS_LCD_IVS) != 0, + panel->acbi, panel->acb); +} + +unsigned long dispc_calc_clock_div(int is_tft, int pck, int *fck_div, + int *lck_div, int *pck_div) +{ + unsigned long prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); + unsigned long pcd_min = is_tft ? 2 : 3; + unsigned long best_pck = 0; + int best_fd = 9, best_ld = 1, best_pd = 2; + int fd, ld, pd; + + for (fd = 16; fd > 0; --fd) { + unsigned long fck = prate / fd * 2; + + if (fck > DISPC_MAX_FCK) + continue; + +#ifdef CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK + if (fck < pck * CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK) + continue; +#endif + for (ld = 1; ld <= 255; ++ld) { + unsigned long lck = fck / ld; + + for (pd = pcd_min; pd <= 255; ++pd) { + int p = lck / pd; + + if (abs(p - pck) < abs(best_pck - pck)) { + best_pck = p; + best_fd = fd; + best_ld = ld; + best_pd = pd; + } + + if (p == pck) + goto found; + + if (p < pck) + break; + } + + if (lck / pcd_min < pck) + break; + } + } + +found: + *fck_div = best_fd; + *lck_div = best_ld; + *pck_div = best_pd; + + return prate / best_fd * 2; +} + +void dispc_set_clock_div(int fck_div, int lck_div, int pck_div) +{ + unsigned long prate; + + prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); + + clk_set_rate(dispc.dpll4_m4_ck, prate / fck_div); + dispc_set_lcd_divisor(lck_div, pck_div); + +#ifdef DEBUG + { + unsigned long fck, lck, pck; + fck = prate / fck_div * 2; + lck = fck / lck_div; + pck = lck / pck_div; + + DSSDBG("dpll4_m4 = %ld\n", prate); + DSSDBG("fck = %ld (%d)\n", fck, fck_div); + DSSDBG("lck = %ld (%d)\n", lck, lck_div); + DSSDBG("pck = %ld (%d)\n", pck, pck_div); + } +#endif +} + +int dispc_pixel_clock_valid(int pixel_clock) +{ + int fck_div, lck_div, pck_div; + unsigned long fck; + + fck = dispc_calc_clock_div(1, pixel_clock * 1000, + &fck_div, &lck_div, &pck_div); + + return fck > 0; +} + +int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask) +{ + int i; + int ret = -EBUSY; + unsigned long flags; + u32 new_mask = 0; + + if (isr == NULL) + return -EINVAL; + + spin_lock_irqsave(&dss_lock, flags); + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + if (registered_isr[i].isr == isr) { + ret = -EINVAL; + break; + } + + if (registered_isr[i].isr != NULL) + continue; + + registered_isr[i].isr = isr; + registered_isr[i].arg = arg; + registered_isr[i].mask = mask; + + enable_clocks(1); + new_mask = dispc_read_reg(DISPC_IRQENABLE); + new_mask |= mask; + dispc_write_reg(DISPC_IRQENABLE, new_mask); + enable_clocks(0); + + ret = 0; + break; + } + + spin_unlock_irqrestore(&dss_lock, flags); + + return ret; +} +EXPORT_SYMBOL(omap_dispc_register_isr); + +int omap_dispc_unregister_isr(omap_dispc_isr_t isr) +{ + int i, j; + unsigned long flags; + u32 new_mask = DISPC_IRQ_MASK_ERROR; + int ret = -EINVAL; + + spin_lock_irqsave(&dss_lock, flags); + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + if (registered_isr[i].isr != isr) + continue; + + registered_isr[i].isr = NULL; + registered_isr[i].arg = NULL; + registered_isr[i].mask = 0; + + for (j = 0; j < DISPC_MAX_NR_ISRS; j++) + new_mask |= registered_isr[j].mask; + + enable_clocks(1); + dispc_write_reg(DISPC_IRQENABLE, new_mask); + enable_clocks(0); + + ret = 0; + break; + } + + spin_unlock_irqrestore(&dss_lock, flags); + + return ret; +} +EXPORT_SYMBOL(omap_dispc_unregister_isr); + +#ifdef DEBUG +static void print_irq_status(u32 status) +{ + if ((status & DISPC_IRQ_MASK_ERROR) == 0) + return; + + printk(KERN_DEBUG "DISPC IRQ: 0x%x: ", status); + +#define PIS(x) \ + if (status & DISPC_IRQ_##x) \ + printk(#x " "); + PIS(GFX_FIFO_UNDERFLOW); + PIS(OCP_ERR); + PIS(VID1_FIFO_UNDERFLOW); + PIS(VID2_FIFO_UNDERFLOW); + PIS(SYNC_LOST); + PIS(SYNC_LOST_DIGIT); +#undef PIS + + printk("\n"); +} +#endif + +/* called from dss */ +void dispc_irq_handler(void) +{ + int i; + u32 irqstatus = dispc_read_reg(DISPC_IRQSTATUS); + static int errors; + + if (irqstatus & DISPC_IRQ_MASK_ERROR) { + if (printk_ratelimit()) { + DSSERR("dispc irq error status %04x\n", + irqstatus); + } + if (errors++ > 100) { + DSSERR("Excessive DISPC errors\n" + "Turning off lcd and digit\n"); + dispc_enable_lcd_out(0); + dispc_enable_digit_out(0); + } + } +#ifdef DEBUG + print_irq_status(irqstatus); +#endif + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + if (!registered_isr[i].isr) + continue; + if (registered_isr[i].mask & irqstatus) + registered_isr[i].isr(registered_isr[i].arg, + irqstatus); + } + + /* ack the interrupt */ + dispc_write_reg(DISPC_IRQSTATUS, irqstatus); +} + +#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC +void dispc_fake_vsync_irq(void) +{ + u32 irqstatus = DISPC_IRQ_VSYNC; + int i; + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + if (!registered_isr[i].isr) + continue; + if (registered_isr[i].mask & irqstatus) + registered_isr[i].isr(registered_isr[i].arg, + irqstatus); + } +} +#endif + +static void _omap_dispc_initialize_irq(void) +{ + memset(registered_isr, 0, sizeof(registered_isr)); + + /* We'll handle these always */ + dispc_write_reg(DISPC_IRQENABLE, DISPC_IRQ_MASK_ERROR); +} + +static void _omap_dispc_initial_config(void) +{ + u32 l; + + l = dispc_read_reg(DISPC_SYSCONFIG); + l = FLD_MOD(l, 2, 13, 12); /* MIDLEMODE: smart standby */ + l = FLD_MOD(l, 2, 4, 3); /* SIDLEMODE: smart idle */ + l = FLD_MOD(l, 1, 2, 2); /* ENWAKEUP */ + l = FLD_MOD(l, 1, 1, 1); /* AUTOIDLE */ + dispc_write_reg(DISPC_SYSCONFIG, l); + + /* FUNCGATED */ + REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); + + /* L3 firewall setting: enable access to OCM RAM */ + __raw_writel(0x402000b0, IO_ADDRESS(0x680050a0)); + + _dispc_set_burst_size(OMAP_DSS_GFX, OMAP_DSS_BURST_16x32); + _dispc_set_burst_size(OMAP_DSS_VIDEO1, OMAP_DSS_BURST_16x32); + _dispc_set_burst_size(OMAP_DSS_VIDEO2, OMAP_DSS_BURST_16x32); + + _dispc_setup_color_conv_coef(); + + dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY); + + /* Set logic clock to fck, pixel clock to fck/2 for now */ + dispc_set_lcd_divisor(1, 2); +} + +int dispc_init(void) +{ + u32 rev; + + spin_lock_init(&dss_lock); + + dispc.base = ioremap(DISPC_BASE, SZ_1K); + if (!dispc.base) { + DSSERR("can't ioremap DISPC\n"); + return -ENOMEM; + } + + get_dss_clocks(); + dispc.dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck"); + if (IS_ERR(dispc.dpll4_m4_ck)) + DSSERR("Failed to get dpll4_m4_ck\n"); + + enable_clocks(1); + + _omap_dispc_initial_config(); + + _omap_dispc_initialize_irq(); + + rev = dispc_read_reg(DISPC_REVISION); + printk(KERN_INFO "OMAP DISPC rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + enable_clocks(0); + + return 0; +} + +void dispc_exit(void) +{ + clk_put(dispc.dpll4_m4_ck); + iounmap(dispc.base); +} + +int dispc_enable_plane(enum omap_plane plane, int enable) +{ + DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); + + enable_clocks(1); + _dispc_enable_plane(plane, enable); + enable_clocks(0); + + return 0; +} + +int dispc_setup_plane(enum omap_plane plane, enum omap_channel channel_out, + u32 paddr, int screen_width, + int pos_x, int pos_y, + int width, int height, + int out_width, int out_height, + enum omap_color_mode color_mode, + int ilace) +{ + int r = 0; + + DSSDBG("dispc_setup_plane %d, %x, sw %d, %d,%d, %dx%d -> " + "%dx%d, (ilace %d)\n", + plane, paddr, screen_width, pos_x, pos_y, + width, height, + out_width, out_height, + ilace); + + enable_clocks(1); + + r = _dispc_setup_plane(plane, channel_out, + paddr, screen_width, + pos_x, pos_y, + width, height, + out_width, out_height, + color_mode, ilace); + + enable_clocks(0); + + return r; +} + +static int dispc_is_intersecting(int x1, int y1, int w1, int h1, + int x2, int y2, int w2, int h2) +{ + if (x1 >= (x2+w2)) + return 0; + + if ((x1+w1) <= x2) + return 0; + + if (y1 >= (y2+h2)) + return 0; + + if ((y1+h1) <= y2) + return 0; + + return 1; +} + +static int dispc_is_overlay_scaled(struct omap_overlay_info *pi) +{ + if (pi->width != pi->out_width) + return 1; + + if (pi->height != pi->out_height) + return 1; + + return 0; +} + +/* returns the area that needs updating */ +void dispc_setup_partial_planes(struct omap_display *display, + int *xi, int *yi, int *wi, int *hi) +{ + struct omap_overlay_manager *mgr; + int i; + + int x, y, w, h; + + x = *xi; + y = *yi; + w = *wi; + h = *hi; + + DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n", + *xi, *yi, *wi, *hi); + + + mgr = display->manager; + + if (!mgr) { + DSSDBG("no manager\n"); + return; + } + + for (i = 0; i < mgr->num_overlays; i++) { + struct omap_overlay *ovl; + struct omap_overlay_info *pi; + ovl = &mgr->overlays[i]; + + if (ovl->manager != mgr) + continue; + + if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) + continue; + + pi = &ovl->info; + + if (!pi->enabled) + continue; + /* + * If the plane is intersecting and scaled, we + * enlarge the update region to accomodate the + * whole area + */ + + if (dispc_is_intersecting(x, y, w, h, + pi->pos_x, pi->pos_y, + pi->out_width, pi->out_height)) { + if (dispc_is_overlay_scaled(pi)) { + + int x1, y1, x2, y2; + + if (x > pi->pos_x) + x1 = pi->pos_x; + else + x1 = x; + + if (y > pi->pos_y) + y1 = pi->pos_y; + else + y1 = y; + + if ((x + w) < (pi->pos_x + pi->out_width)) + x2 = pi->pos_x + pi->out_width; + else + x2 = x + w; + + if ((y + h) < (pi->pos_y + pi->out_height)) + y2 = pi->pos_y + pi->out_height; + else + y2 = y + h; + + x = x1; + y = y1; + w = x2 - x1; + h = y2 - y1; + + DSSDBG("Update area after enlarge due to " + "scaling %d, %d %dx%d\n", + x, y, w, h); + } + } + } + + for (i = 0; i < mgr->num_overlays; i++) { + struct omap_overlay *ovl = &mgr->overlays[i]; + struct omap_overlay_info *pi = &ovl->info; + + int px = pi->pos_x; + int py = pi->pos_y; + int pw = pi->width; + int ph = pi->height; + int pow = pi->out_width; + int poh = pi->out_height; + u32 pa = pi->paddr; + int psw = pi->screen_width; + int bpp; + + if (ovl->manager != mgr) + continue; + + /* + * If plane is not enabled or the update region + * does not intersect with the plane in question, + * we really disable the plane from hardware + */ + + if (!pi->enabled || + !dispc_is_intersecting(x, y, w, h, + px, py, pow, poh)) { + dispc_enable_plane(ovl->id, 0); + continue; + } + + switch (pi->color_mode) { + case OMAP_DSS_COLOR_RGB16: + bpp = 16; + break; + + case OMAP_DSS_COLOR_RGB24P: + bpp = 24; + break; + + case OMAP_DSS_COLOR_RGB24U: + bpp = 32; + break; + + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + bpp = 16; + break; + + default: + BUG(); + return; + } + + if (x > pi->pos_x) { + px = 0; + pw -= (x - pi->pos_x); + pa += (x - pi->pos_x) * bpp / 8; + } else { + px = pi->pos_x - x; + } + + if (y > pi->pos_y) { + py = 0; + ph -= (y - pi->pos_y); + pa += (y - pi->pos_y) * psw * bpp / 8; + } else { + py = pi->pos_y - y; + } + + if (w < (px+pw)) + pw -= (px+pw) - (w); + + if (h < (py+ph)) + ph -= (py+ph) - (h); + + /* Can't scale the GFX plane */ + if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 || + dispc_is_overlay_scaled(pi) == 0) { + pow = pw; + poh = ph; + } + + DSSDBG("calc plane %d, %x, sw %d, %d,%d, %dx%d -> %dx%d\n", + ovl->id, pa, psw, px, py, pw, ph, pow, poh); + + dispc_setup_plane(ovl->id, mgr->id, + pa, psw, + px, py, + pw, ph, + pow, poh, + pi->color_mode, 0); + + dispc_enable_plane(ovl->id, 1); + } + + *xi = x; + *yi = y; + *wi = w; + *hi = h; + +} + diff --git a/arch/arm/plat-omap/dss/display.c b/arch/arm/plat-omap/dss/display.c new file mode 100644 index 0000000..86f7d39 --- /dev/null +++ b/arch/arm/plat-omap/dss/display.c @@ -0,0 +1,781 @@ +/* + * linux/arch/arm/plat-omap/dss/display.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tom...@no...> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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/>. + */ + +#define DSS_SUBSYS_NAME "DISPLAY" + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/clk.h> + +#include <mach/display.h> +#include <mach/clock.h> +#include "dss.h" + +#define DSS_MAX_DISPLAYS 8 + +static int num_displays; +static struct omap_display displays[DSS_MAX_DISPLAYS]; + +static ssize_t show_clk(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct clk *clocks[5]; + int i; + ssize_t l, size = PAGE_SIZE; + + clocks[0] = get_dss_ick(); + clocks[1] = get_dss1_fck(); + clocks[2] = get_dss2_fck(); + clocks[3] = get_tv_fck(); + clocks[4] = get_96m_fck(); + + l = 0; + + l += snprintf(buf + l, size - l, "- dss -\n"); + + for (i = 0; i < 5; i++) { + l += snprintf(buf + l, size - l, "%-15s\t%lu\t%d\n", + clocks[i]->name, + clk_get_rate(clocks[i]), + clk_get_usecount(clocks[i])); + } + + l += dispc_print_clocks(buf + l, size - l); +#ifdef CONFIG_OMAP2_DSS_DSI + l += dsi_print_clocks(buf + l, size - l); +#endif + return l; +} + +static DEVICE_ATTR(clk, S_IRUGO, show_clk, NULL); + +int initialize_sysfs(struct device *dev) +{ + int r; + + r = device_create_file(dev, &dev_attr_clk); + if (r) + DSSERR("failed to create sysfs clk file\n"); + + return r; +} + +void uninitialize_sysfs(struct device *dev) +{ + device_remove_file(dev, &dev_attr_clk); +} + +void initialize_displays(struct omap_dss_platform_data *pdata) +{ + int i; + + num_displays = 0; + + BUG_ON(pdata->num_displays > DSS_MAX_DISPLAYS); + + for (i = 0; i < pdata->num_displays; ++i) { + struct omap_display *display = &displays[i]; + + /*atomic_set(&display->ref_count, 0);*/ + display->ref_count = 0; + + display->hw_config = *pdata->displays[i]; + display->type = pdata->displays[i]->type; + display->name = pdata->displays[i]->name; + + switch (display->type) { + + case OMAP_DISPLAY_TYPE_DPI: + dpi_init_display(display); + break; +#ifdef CONFIG_OMAP2_DSS_RFBI + case OMAP_DISPLAY_TYPE_DBI: + rfbi_init_display(display); + break; +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + case OMAP_DISPLAY_TYPE_VENC: + venc_init_display(display); + break; +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + case OMAP_DISPLAY_TYPE_SDI: + sdi_init_display(display); + break; +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + case OMAP_DISPLAY_TYPE_DSI: + dsi_init_display(display); + break; +#endif + + default: + DSSERR("Support for display '%s' not compiled in.\n", + display->name); + continue; + } + + num_displays++; + } +} + +static int check_overlay(struct omap_overlay *ovl, + struct omap_display *display) +{ + struct omap_overlay_info *info; + int outw, outh; + + if (!display) + return 0; + + if (!ovl->info.enabled) + return 0; + + info = &ovl->info; + + if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { + outw = info->width; + outh = info->height; + } else { + if (info->out_width == 0) + outw = info->width; + else + outw = info->out_width; + + if (info->out_height == 0) + outh = info->height; + else + outh = info->out_height; + } + + if (display->x_res < info->pos_x + outw) + return -EINVAL; + + if (display->y_res < info->pos_y + outh) + return -EINVAL; + + return 0; +} + + +static int omap_dss_set_manager(struct omap_overlay *ovl, + struct omap_overlay_manager *mgr) +{ + int r; + + if (ovl->manager) { + DSSERR("overlay '%s' already has a manager '%s'\n", + ovl->name, ovl->manager->name); + } + + r = check_overlay(ovl, mgr->display); + if (r) + return r; + + ovl->manager = mgr; + + return 0; +} + +static int omap_dss_unset_manager(struct omap_overlay *ovl) +{ + if (!ovl->manager) { + DSSERR("failed to detach overlay: manager not set\n"); + return -EINVAL; + } + + ovl->manager = NULL; + + return 0; +} + +static int omap_dss_set_display(struct omap_overlay_manager *mgr, + struct omap_display *display) +{ + int i; + int r; + + if (display->manager) { + DSSERR("display '%s' already has a manager '%s'\n", + display->name, display->manager->name); + return -EINVAL; + } + + if ((mgr->supported_displays & display->type) == 0) { + DSSERR("display '%s' does not support manager '%s'\n", + display->name, mgr->name); + return -EINVAL; + } + + for (i = 0; i < mgr->num_overlays; i++) { + struct omap_overlay *ovl = &mgr->overlays[i]; + + if (ovl->manager != mgr || !ovl->info.enabled) + continue; + + r = check_overlay(ovl, display); + if (r) + return r; + } + + display->manager = mgr; + mgr->display = display; + + return 0; +} + +static int omap_dss_unset_display(struct omap_overlay_manager *mgr) +{ + if (!mgr->display) { + DSSERR("failed to unset display, display not set.\n"); + return -EINVAL; + } + + mgr->display->manager = NULL; + mgr->display = NULL; + + return 0; +} + +static int omap_dss_setup_overlay_input(struct omap_overlay *ovl, + u32 paddr, void *vaddr, int screen_width, + int width, int height, + enum omap_color_mode color_mode) +{ + int r; + struct omap_overlay_info old_info; + + if ((ovl->supported_modes & color_mode) == 0) { + DSSERR("overlay doesn't support mode %d\n", color_mode); + return -EINVAL; + } + + old_info = ovl->info; + + ovl->info.paddr = paddr; + ovl->info.vaddr = vaddr; + ovl->info.screen_width = screen_width; + + ovl->info.width = width; + ovl->info.height = height; + ovl->info.color_mode = color_mode; + + if (ovl->manager) { + r = check_overlay(ovl, ovl->manager->display); + if (r) { + ovl->info = old_info; + return r; + } + } + + return 0; +} + +static int omap_dss_setup_overlay_output(struct omap_overlay *ovl, + int pos_x, int pos_y, + int out_width, int out_height) +{ + int r; + struct omap_overlay_info old_info; + + old_info = ovl->info; + + ovl->info.pos_x = pos_x; + ovl->info.pos_y = pos_y; + ovl->info.out_width = out_width; + ovl->info.out_height = out_height; + + if (ovl->manager) { + r = check_overlay(ovl, ovl->manager->display); + if (r) { + ovl->info = old_info; + return r; + } + } + + return 0; +} + +static int omap_dss_enable_overlay(struct omap_overlay *ovl, int enable) +{ + struct omap_overlay_info old_info; + int r; + + old_info = ovl->info; + + ovl->info.enabled = enable ? 1 : 0; + + if (ovl->manager) { + r = check_overlay(ovl, ovl->manager->display); + if (r) { + ovl->info = old_info; + return r; + } + } + + return 0; +} + + +static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) +{ + int i; + int r; + + DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name); + + if (!mgr->display) { + DSSDBG("no display, aborting apply\n"); + return 0; + } + + /* on a manual update display update() handles configuring + * planes */ + if (mgr->display->get_update_mode) { + enum omap_dss_update_mode mode; + mode = mgr->display->get_update_mode(mgr->display); + if (mode == OMAP_DSS_UPDATE_MANUAL) + return 0; + } + + for (i = 0; i < mgr->num_overlays; i++) { + int ilace = 0; + int outw, outh; + + struct omap_overlay *ovl = &mgr->overlays[i]; + + if (!ovl->manager) { + dispc_enable_plane(ovl->id, 0); + continue; + } + + if (ovl->manager != mgr) + continue; + + if (!ovl->info.enabled) { + dispc_enable_plane(ovl->id, 0); + continue; + } + + if (mgr->display->type == OMAP_DISPLAY_TYPE_VENC) + ilace = 1; + + if (ovl->info.out_width == 0) + outw = ovl->info.width; + else + outw = ovl->info.out_width; + + if (ovl->info.out_height == 0) + outh = ovl->info.height; + else + outh = ovl->info.out_height; + + r = dispc_setup_plane(ovl->id, ovl->manager->id, + ovl->info.paddr, + ovl->info.screen_width, + ovl->info.pos_x, + ovl->info.pos_y, + ovl->info.width, + ovl->info.height, + outw, + outh, + ovl->info.color_mode, + ilace); + + if (r) { + DSSERR("dispc_setup_plane failed\n"); + return r; + } + + dispc_enable_plane(ovl->id, 1); + } + + /* XXX if autoidle is enabled, we have to wait here a bit. + * Otherwise if we issue GOLCD too soon after lcd enable, + * we get sync lost. Why? */ + mdelay(100); + + dispc_go(mgr->id); + + return 0; +} + +static struct omap_overlay dispc_overlays[] = { + { + .name = "gfx", + .id = OMAP_DSS_GFX, + .set_manager = &omap_dss_set_manager, + .unset_manager = &omap_dss_unset_manager, + .setup_input = &omap_dss_setup_overlay_input, + .setup_output = &omap_dss_setup_overlay_output, + .enable = &omap_dss_enable_overlay, + .supported_modes = OMAP_DSS_COLOR_GFX_OMAP3, + }, + { + .name = "vid1", + .id = OMAP_DSS_VIDEO1, + .set_manager = &omap_dss_set_manager, + .unset_manager = &omap_dss_unset_manager, + .setup_input = &omap_dss_setup_overlay_input, + .setup_output = &omap_dss_setup_overlay_output, + .enable = &omap_dss_enable_overlay, + .supported_modes = OMAP_DSS_COLOR_VID_OMAP3, + .caps = OMAP_DSS_OVL_CAP_SCALE, + }, + { + .name = "vid2", + .id = OMAP_DSS_VIDEO2, + .set_manager = &omap_dss_set_manager, + .unset_manager = &omap_dss_unset_manager, + .setup_input = &omap_dss_setup_overlay_input, + .setup_output = &omap_dss_setup_overlay_output, + .enable = &omap_dss_enable_overlay, + .supported_modes = OMAP_DSS_COLOR_VID_OMAP3, + .caps = OMAP_DSS_OVL_CAP_SCALE, + }, +}; + +static struct omap_overlay_manager dispc_overlay_managers[] = +{ + [OMAP_DSS_OVL_MGR_LCD] = { + .name = "lcd", + .id = OMAP_DSS_CHANNEL_LCD, + .num_overlays = 3, + .overlays = dispc_overlays, + .set_display = &omap_dss_set_display, + .unset_display = &omap_dss_unset_display, + .apply = &omap_dss_mgr_apply, + .caps = OMAP_DSS_OVL_MGR_CAP_DISPC, + .supported_displays = + OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | + OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI, + }, + [OMAP_DSS_OVL_MGR_TV] = { + .name = "tv", + .id = OMAP_DSS_CHANNEL_DIGIT, + .num_overlays = 3, + .overlays = dispc_overlays, + .set_display = &omap_dss_set_display, + .unset_display = &omap_dss_unset_display, + .apply = &omap_dss_mgr_apply, + .caps = OMAP_DSS_OVL_MGR_CAP_DISPC, + .supported_displays = OMAP_DISPLAY_TYPE_VENC, + }, +}; + +static int num_overlays = 3; + +static struct omap_overlay *omap_dss_overlays[10] = { + &dispc_overlays[0], + &dispc_overlays[1], + &dispc_overlays[2], +}; + +static int num_overlay_managers = 2; + +static struct omap_overlay_manager *omap_dss_overlay_managers[10] = { + &dispc_overlay_managers[0], + &dispc_overlay_managers[1], +}; + + +static void omap_dss_add_overlay(struct omap_overlay *overlay) +{ + int i = num_overlays++; + + omap_dss_overlays[i] = overlay; +} + +static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager) +{ + int i = num_overlay_managers++; + omap_dss_overlay_managers[i] = manager; +} + +int omap_dss_get_num_overlays(void) +{ + return num_overlays; +} +EXPORT_SYMBOL(omap_dss_get_num_overlays); + +struct omap_overlay *omap_dss_get_overlay(int num) +{ + BUG_ON(num >= num_overlays); + return omap_dss_overlays[num]; +} +EXPORT_SYMBOL(omap_dss_get_overlay); + +int omap_dss_get_num_overlay_managers(void) +{ + return num_overlay_managers; +} +EXPORT_SYMBOL(omap_dss_get_num_overlay_managers); + +struct omap_overlay_manager *omap_dss_get_overlay_manager(int num) +{ + BUG_ON(num >= num_overlay_managers); + return omap_dss_overlay_managers[num]; +} +EXPORT_SYMBOL(omap_dss_get_overlay_manager); + +static int ovl_mgr_apply_l4(struct omap_overlay_manager *mgr) +{ + DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name); + + return 0; +} + +void initialize_overlays(void) +{ + int i; + struct omap_overlay_manager *lcd_mgr; + struct omap_overlay_manager *tv_mgr; + struct omap_overlay_manager *def_mgr = NULL; + + lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD); + tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV); + + /* connect lcd manager to first non-VENC display found */ + for (i = 0; i < num_displays; i++) { + if (displays[i].type != OMAP_DISPLAY_TYPE_VENC) { + struct omap_display *display = &displays[i]; + omap_dss_set_display(lcd_mgr, display); + + def_mgr = lcd_mgr; + + break; + } + } + + /* connect tv manager to first VENC display found */ + for (i = 0; i < num_displays; i++) { + if (displays[i].type == OMAP_DISPLAY_TYPE_VENC) { + struct omap_display *display = &displays[i]; + omap_dss_set_display(tv_mgr, display); + + if (!def_mgr) + def_mgr = tv_mgr; + + break; + } + } + + /* connect all dispc overlays to def_mgr */ + if (def_mgr) { + for (i = 0; i < 3; i++) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + omap_dss_set_manager(ovl, def_mgr); + } + } + + /* setup L4 overlay as an example */ + { + static struct omap_overlay ovl = { + .name = "l4-ovl", + .supported_modes = OMAP_DSS_COLOR_RGB24U, + .set_manager = &omap_dss_set_manager, + .unset_manager = &omap_dss_unset_manager, + .setup_input = &omap_dss_setup_overlay_input, + .setup_output = &omap_dss_setup_overlay_output, + .enable = &omap_dss_enable_overlay, + }; + + static struct omap_overlay_manager mgr = { + .name = "l4", + .num_overlays = 1, + .overlays = &ovl, + .set_display = &omap_dss_set_display, + .unset_display = &omap_dss_unset_display, + .apply = &ovl_mgr_apply_l4, + .supported_displays = + OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI, + }; + + omap_dss_add_overlay(&ovl); + omap_dss_add_overlay_manager(&mgr); + omap_dss_set_manager(&ovl, &mgr); + } + +} + + +int omap_dss_get_num_displays(void) +{ + return num_displays; +} +EXPORT_SYMBOL(omap_dss_get_num_displays); + +struct omap_display *omap_dss_get_display(int no) +{ + struct omap_display *display; + + if (no >= num_displays) + return NULL; + + display = &displays[no]; + + switch (display->type) { + case OMAP_DISPLAY_TYPE_VENC: + break; + + case OMAP_DISPLAY_TYPE_DPI: + case OMAP_DISPLAY_TYPE_SDI: + if (display->panel == NULL) + return NULL; + break; + + case OMAP_DISPLAY_TYPE_DBI: + case OMAP_DISPLAY_TYPE_DSI: + if (display->panel == NULL || display->ctrl == NULL) + return NULL; + break; + + default: + return NULL; + } + + if (display->panel) { + if (!try_module_get(display->panel->owner)) + goto err0; + + if (display->panel->init) + if (display->panel->init(display) != 0) + goto err1; + } + + if (display->ctrl) { + if (!try_module_get(display->ctrl->owner)) + goto err2; + + if (display->ctrl->init) + if (display->ctrl->init(display) != 0) + goto err3; + } + + display->ref_count++; + /* + if (atomic_cmpxchg(&display->ref_count, 0, 1) != 0) + return 0; +*/ + if (display->panel) { + display->x_res = display->panel->x_res; + display->y_res = display->panel->y_res; + display->bpp = display->panel->bpp; + } + + return display; +err3: + if (display->ctrl) + module_put(display->ctrl->owner); +err2: + if (display->panel && display->panel->init) + display->panel->cleanup(display); +err1: + if (display->panel) + module_put(display->panel->owner); +err0: + return NULL; +} +EXPORT_SYMBOL(omap_dss_get_display); + +void omap_dss_put_display(struct omap_display *display) +{ + if (--display->ref_count > 0) + return; +/* + if (atomic_cmpxchg(&display->ref_count, 1, 0) != 1) + return; +*/ + if (display->ctrl) { + if (display->ctrl->cleanup) + display->ctrl->cleanup(display); + module_put(display->ctrl->owner); + } + + if (display->panel) { + if (display->panel->cleanup) + display->panel->cleanup(display); + module_put(display->panel->owner); + } +} +EXPORT_SYMBOL(omap_dss_put_display); + +void omap_dss_register_ctrl(struct omap_ctrl *ctrl) +{ + int i; + + for (i = 0; i < num_displays; i++) { + struct omap_display *display = &displays[i]; + if (display->hw_config.ctrl_name && + strcmp(display->hw_config.ctrl_name, ctrl->name) == 0) { + display->ctrl = ctrl; + DSSDBG("ctrl '%s' registered\n", ctrl->name); + } + } +} +EXPORT_SYMBOL(omap_dss_register_ctrl); + +void omap_dss_register_panel(struct omap_panel *panel) +{ + int i; + + for (i = 0; i < num_displays; i++) { + struct omap_display *display = &displays[i]; + if (display->hw_config.panel_name && + strcmp(display->hw_config.panel_name, panel->name) == 0) { + display->panel = panel; + DSSDBG("panel '%s' registered\n", panel->name); + } + } +} +EXPORT_SYMBOL(omap_dss_register_panel); + +void omap_dss_unregister_ctrl(struct omap_ctrl *ctrl) +{ + int i; + + for (i = 0; i < num_displays; i++) { + struct omap_display *display = &displays[i]; + if (display->hw_config.ctrl_name && + strcmp(display->hw_config.ctrl_name, ctrl->name) == 0) + display->ctrl = NULL; + } +} +EXPORT_SYMBOL(omap_dss_unregister_ctrl); + +void omap_dss_unregister_panel(struct omap_panel *panel) +{ + int i; + + for (i = 0; i < num_displays; i++) { + struct omap_display *display = &displays[i]; + if (display->hw_config.panel_name && + strcmp(display->hw_config.panel_name, panel->name) == 0) + display->panel = NULL; + } +} +EXPORT_SYMBOL(omap_dss_unregister_panel); diff --git a/arch/arm/plat-omap/dss/dpi.c b/arch/arm/plat-omap/dss/dpi.c new file mode 100644 index 0000000..d121b52 --- /dev/null +++ b/arch/arm/plat-omap/dss/dpi.c @@ -0,0 +1,303 @@ +/* + * linux/arch/arm/plat-omap/dss/dpi.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tom...@no...> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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/kernel.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/errn... [truncated message content] |
From: Tomi V. <tom...@no...> - 2008-11-04 16:10:28
|
Signed-off-by: Tomi Valkeinen <tom...@no...> --- arch/arm/plat-omap/dss/rfbi.c | 1234 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 1234 insertions(+), 0 deletions(-) create mode 100644 arch/arm/plat-omap/dss/rfbi.c diff --git a/arch/arm/plat-omap/dss/rfbi.c b/arch/arm/plat-omap/dss/rfbi.c new file mode 100644 index 0000000..31ddd24 --- /dev/null +++ b/arch/arm/plat-omap/dss/rfbi.c @@ -0,0 +1,1234 @@ +/* + * linux/arch/arm/plat-omap/dss/rfbi.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tom...@no...> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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/>. + */ + +#define DSS_SUBSYS_NAME "RFBI" + +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/kfifo.h> +#include <linux/ktime.h> + +#include <mach/board.h> +#include <mach/display.h> +#include "dss.h" + +/*#define MEASURE_PERF*/ + +#define RFBI_BASE 0x48050800 + +struct rfbi_reg { u16 idx; }; + +#define RFBI_REG(idx) ((const struct rfbi_reg) { idx }) + +#define RFBI_REVISION RFBI_REG(0x0000) +#define RFBI_SYSCONFIG RFBI_REG(0x0010) +#define RFBI_SYSSTATUS RFBI_REG(0x0014) +#define RFBI_CONTROL RFBI_REG(0x0040) +#define RFBI_PIXEL_CNT RFBI_REG(0x0044) +#define RFBI_LINE_NUMBER RFBI_REG(0x0048) +#define RFBI_CMD RFBI_REG(0x004c) +#define RFBI_PARAM RFBI_REG(0x0050) +#define RFBI_DATA RFBI_REG(0x0054) +#define RFBI_READ RFBI_REG(0x0058) +#define RFBI_STATUS RFBI_REG(0x005c) + +#define RFBI_CONFIG(n) RFBI_REG(0x0060 + (n)*0x18) +#define RFBI_ONOFF_TIME(n) RFBI_REG(0x0064 + (n)*0x18) +#define RFBI_CYCLE_TIME(n) RFBI_REG(0x0068 + (n)*0x18) +#define RFBI_DATA_CYCLE1(n) RFBI_REG(0x006c + (n)*0x18) +#define RFBI_DATA_CYCLE2(n) RFBI_REG(0x0070 + (n)*0x18) +#define RFBI_DATA_CYCLE3(n) RFBI_REG(0x0074 + (n)*0x18) + +#define RFBI_VSYNC_WIDTH RFBI_REG(0x0090) +#define RFBI_HSYNC_WIDTH RFBI_REG(0x0094) + +#define RFBI_CMD_FIFO_LEN_BYTES (16 * sizeof(struct update_param)) + +#define REG_FLD_MOD(idx, val, start, end) \ + rfbi_write_reg(idx, FLD_MOD(rfbi_read_reg(idx), val, start, end)) + +/* To work around an RFBI transfer rate limitation */ +#define OMAP_RFBI_RATE_LIMIT 1 + +enum omap_rfbi_cycleformat { + OMAP_DSS_RFBI_CYCLEFORMAT_1_1 = 0, + OMAP_DSS_RFBI_CYCLEFORMAT_2_1 = 1, + OMAP_DSS_RFBI_CYCLEFORMAT_3_1 = 2, + OMAP_DSS_RFBI_CYCLEFORMAT_3_2 = 3, +}; + +enum omap_rfbi_datatype { + OMAP_DSS_RFBI_DATATYPE_12 = 0, + OMAP_DSS_RFBI_DATATYPE_16 = 1, + OMAP_DSS_RFBI_DATATYPE_18 = 2, + OMAP_DSS_RFBI_DATATYPE_24 = 3, +}; + +enum omap_rfbi_parallelmode { + OMAP_DSS_RFBI_PARALLELMODE_8 = 0, + OMAP_DSS_RFBI_PARALLELMODE_9 = 1, + OMAP_DSS_RFBI_PARALLELMODE_12 = 2, + OMAP_DSS_RFBI_PARALLELMODE_16 = 3, +}; + +enum update_cmd { + RFBI_CMD_UPDATE = 0, + RFBI_CMD_SYNC = 1, +}; + +static int rfbi_convert_timings(struct rfbi_timings *t); +static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div); +static void process_cmd_fifo(void); + +static struct { + void __iomem *base; + + struct clk *dss_ick; + struct clk *dss1_fck; + + unsigned long l4_khz; + + enum omap_rfbi_datatype datatype; + enum omap_rfbi_parallelmode parallelmode; + + enum omap_rfbi_te_mode te_mode; + int te_enabled; + + void (*framedone_callback)(void *data); + void *framedone_callback_data; + + struct omap_display *display[2]; + + struct kfifo *cmd_fifo; + spinlock_t cmd_lock; + struct completion cmd_done; + atomic_t cmd_fifo_full; + atomic_t cmd_pending; +#ifdef MEASURE_PERF + ktime_t perf_time; +#endif +} rfbi; + +struct update_region { + u16 x; + u16 y; + u16 w; + u16 h; +}; + +struct update_param { + u8 rfbi_module; + u8 cmd; + + union { + struct update_region r; + struct completion *sync; + } par; +}; + +static inline void rfbi_write_reg(const struct rfbi_reg idx, u32 val) +{ + __raw_writel(val, rfbi.base + idx.idx); +} + +static inline u32 rfbi_read_reg(const struct rfbi_reg idx) +{ + return __raw_readl(rfbi.base + idx.idx); +} + +static void rfbi_enable_clocks(int enable) +{ + if (enable) { + clk_enable(rfbi.dss_ick); + clk_enable(rfbi.dss1_fck); + } else { + clk_disable(rfbi.dss1_fck); + clk_disable(rfbi.dss_ick); + } +} + +void omap_rfbi_write_command(const void *buf, u32 len) +{ + rfbi_enable_clocks(1); + switch (rfbi.parallelmode) { + case OMAP_DSS_RFBI_PARALLELMODE_8: + { + const u8 *b = buf; + for (; len; len--) + rfbi_write_reg(RFBI_CMD, *b++); + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_16: + { + const u16 *w = buf; + BUG_ON(len & 1); + for (; len; len -= 2) + rfbi_write_reg(RFBI_CMD, *w++); + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_9: + case OMAP_DSS_RFBI_PARALLELMODE_12: + default: + BUG(); + } + rfbi_enable_clocks(0); +} +EXPORT_SYMBOL(omap_rfbi_write_command); + +void omap_rfbi_read_data(void *buf, u32 len) +{ + rfbi_enable_clocks(1); + switch (rfbi.parallelmode) { + case OMAP_DSS_RFBI_PARALLELMODE_8: + { + u8 *b = buf; + for (; len; len--) { + rfbi_write_reg(RFBI_READ, 0); + *b++ = rfbi_read_reg(RFBI_READ); + } + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_16: + { + u16 *w = buf; + BUG_ON(len & ~1); + for (; len; len -= 2) { + rfbi_write_reg(RFBI_READ, 0); + *w++ = rfbi_read_reg(RFBI_READ); + } + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_9: + case OMAP_DSS_RFBI_PARALLELMODE_12: + default: + BUG(); + } + rfbi_enable_clocks(0); +} +EXPORT_SYMBOL(omap_rfbi_read_data); + +void omap_rfbi_write_data(const void *buf, u32 len) +{ + rfbi_enable_clocks(1); + switch (rfbi.parallelmode) { + case OMAP_DSS_RFBI_PARALLELMODE_8: + { + const u8 *b = buf; + for (; len; len--) + rfbi_write_reg(RFBI_PARAM, *b++); + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_16: + { + const u16 *w = buf; + BUG_ON(len & 1); + for (; len; len -= 2) + rfbi_write_reg(RFBI_PARAM, *w++); + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_9: + case OMAP_DSS_RFBI_PARALLELMODE_12: + default: + BUG(); + + } + rfbi_enable_clocks(0); +} +EXPORT_SYMBOL(omap_rfbi_write_data); + +void omap_rfbi_write_pixels(const void *buf, int scr_width, int x, int y, + int w, int h) +{ + int start_offset = scr_width * y + x; + int horiz_offset = scr_width - w; + int i; + + rfbi_enable_clocks(1); + + if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && + rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { + const u16 *pd = buf; + pd += start_offset; + + for (; h; --h) { + for (i = 0; i < w; ++i) { + const u8 *b = (const u8 *)pd; + rfbi_write_reg(RFBI_PARAM, *(b+1)); + rfbi_write_reg(RFBI_PARAM, *(b+0)); + ++pd; + } + pd += horiz_offset; + } + } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_24 && + rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { + const u32 *pd = buf; + pd += start_offset; + + for (; h; --h) { + for (i = 0; i < w; ++i) { + const u8 *b = (const u8 *)pd; + rfbi_write_reg(RFBI_PARAM, *(b+2)); + rfbi_write_reg(RFBI_PARAM, *(b+1)); + rfbi_write_reg(RFBI_PARAM, *(b+0)); + ++pd; + } + pd += horiz_offset; + } + } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && + rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_16) { + const u16 *pd = buf; + pd += start_offset; + + for (; h; --h) { + for (i = 0; i < w; ++i) { + rfbi_write_reg(RFBI_PARAM, *pd); + ++pd; + } + pd += horiz_offset; + } + } else { + BUG(); + } + + rfbi_enable_clocks(0); +} +EXPORT_SYMBOL(omap_rfbi_write_pixels); + +void rfbi_transfer_area(int width, int height, + void (callback)(void *data), void *data) +{ + u32 l; + + /*BUG_ON(callback == 0);*/ + BUG_ON(rfbi.framedone_callback != NULL); + + DSSDBG("rfbi_transfer_area %dx%d\n", width, height); + + dispc_set_lcd_size(width, height); + + dispc_enable_lcd_out(1); + + rfbi.framedone_callback = callback; + rfbi.framedone_callback_data = data; + + rfbi_enable_clocks(1); + +#ifdef MEASURE_PERF + rfbi.perf_time = ktime_get(); +#endif + rfbi_write_reg(RFBI_PIXEL_CNT, width * height); + + l = rfbi_read_reg(RFBI_CONTROL); + l = FLD_MOD(l, 1, 0, 0); /* enable */ + if (!rfbi.te_enabled) + l = FLD_MOD(l, 1, 4, 4); /* ITE */ + + rfbi_write_reg(RFBI_CONTROL, l); +} + +static void framedone_callback(void *data, u32 mask) +{ + void (*callback)(void *data); + +#ifdef MEASURE_PERF + { + ktime_t t = ktime_get(); + t = ktime_sub(t, rfbi.perf_time); + DSSDBG("FRAMEDONE in %lld ns\n", ktime_to_ns(t)); + } +#else + DSSDBG("FRAMEDONE\n"); +#endif + + REG_FLD_MOD(RFBI_CONTROL, 0, 0, 0); + + rfbi_enable_clocks(0); + + callback = rfbi.framedone_callback; + rfbi.framedone_callback = NULL; + + /*callback(rfbi.framedone_callback_data);*/ + + atomic_set(&rfbi.cmd_pending, 0); + + process_cmd_fifo(); +} + +#if 1 /* VERBOSE */ +static void rfbi_print_timings(void) +{ + u32 l; + u32 time; + + l = rfbi_read_reg(RFBI_CONFIG(0)); + time = 1000000000 / rfbi.l4_khz; + if (l & (1 << 4)) + time *= 2; + + DSSDBG("Tick time %u ps\n", time); + l = rfbi_read_reg(RFBI_ONOFF_TIME(0)); + DSSDBG("CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, " + "REONTIME %d, REOFFTIME %d\n", + l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f, + (l >> 20) & 0x0f, (l >> 24) & 0x3f); + + l = rfbi_read_reg(RFBI_CYCLE_TIME(0)); + DSSDBG("WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, " + "ACCESSTIME %d\n", + (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, + (l >> 22) & 0x3f); +} +#else +static void rfbi_print_timings(void) {} +#endif + + + + +static u32 extif_clk_period; + +static inline unsigned long round_to_extif_ticks(unsigned long ps, int div) +{ + int bus_tick = extif_clk_period * div; + return (ps + bus_tick - 1) / bus_tick * bus_tick; +} + +static int calc_reg_timing(struct rfbi_timings *t, int div) +{ + t->clk_div = div; + + t->cs_on_time = round_to_extif_ticks(t->cs_on_time, div); + + t->we_on_time = round_to_extif_ticks(t->we_on_time, div); + t->we_off_time = round_to_extif_ticks(t->we_off_time, div); + t->we_cycle_time = round_to_extif_ticks(t->we_cycle_time, div); + + t->re_on_time = round_to_extif_ticks(t->re_on_time, div); + t->re_off_time = round_to_extif_ticks(t->re_off_time, div); + t->re_cycle_time = round_to_extif_ticks(t->re_cycle_time, div); + + t->access_time = round_to_extif_ticks(t->access_time, div); + t->cs_off_time = round_to_extif_ticks(t->cs_off_time, div); + t->cs_pulse_width = round_to_extif_ticks(t->cs_pulse_width, div); + + DSSDBG("[reg]cson %d csoff %d reon %d reoff %d\n", + t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); + DSSDBG("[reg]weon %d weoff %d recyc %d wecyc %d\n", + t->we_on_time, t->we_off_time, t->re_cycle_time, + t->we_cycle_time); + DSSDBG("[reg]rdaccess %d cspulse %d\n", + t->access_time, t->cs_pulse_width); + + return rfbi_convert_timings(t); +} + +static int calc_extif_timings(struct rfbi_timings *t) +{ + u32 max_clk_div; + int div; + + rfbi_get_clk_info(&extif_clk_period, &max_clk_div); + for (div = 1; div <= max_clk_div; div++) { + if (calc_reg_timing(t, div) == 0) + break; + } + + if (div <= max_clk_div) + return 0; + + DSSERR("can't setup timings\n"); + return -1; +} + + +void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) +{ + int r; + + if (!t->converted) { + r = calc_extif_timings(t); + if (r < 0) + DSSERR("Failed to calc timings\n"); + } + + BUG_ON(!t->converted); + + rfbi_enable_clocks(1); + rfbi_write_reg(RFBI_ONOFF_TIME(rfbi_module), t->tim[0]); + rfbi_write_reg(RFBI_CYCLE_TIME(rfbi_module), t->tim[1]); + + /* TIMEGRANULARITY */ + REG_FLD_MOD(RFBI_CONFIG(rfbi_module), + (t->tim[2] ? 1 : 0), 4, 4); + + rfbi_print_timings(); + rfbi_enable_clocks(0); +} + +static int ps_to_rfbi_ticks(int time, int div) +{ + unsigned long tick_ps; + int ret; + + /* Calculate in picosecs to yield more exact results */ + tick_ps = 1000000000 / (rfbi.l4_khz) * div; + + ret = (time + tick_ps - 1) / tick_ps; + + return ret; +} + +#ifdef OMAP_RFBI_RATE_LIMIT +unsigned long rfbi_get_max_tx_rate(void) +{ + unsigned long l4_rate, dss1_rate; + int min_l4_ticks = 0; + int i; + + /* According to TI this can't be calculated so make the + * adjustments for a couple of known frequencies and warn for + * others. + */ + static const struct { + unsigned long l4_clk; /* HZ */ + unsigned long dss1_clk; /* HZ */ + unsigned long min_l4_ticks; + } ftab[] = { + { 55, 132, 7, }, /* 7.86 MPix/s */ + { 110, 110, 12, }, /* 9.16 MPix/s */ + { 110, 132, 10, }, /* 11 Mpix/s */ + { 120, 120, 10, }, /* 12 Mpix/s */ + { 133, 133, 10, }, /* 13.3 Mpix/s */ + }; + + l4_rate = rfbi.l4_khz / 1000; + dss1_rate = clk_get_rate(rfbi.dss1_fck) / 1000000; + + for (i = 0; i < ARRAY_SIZE(ftab); i++) { + /* Use a window instead of an exact match, to account + * for different DPLL multiplier / divider pairs. + */ + if (abs(ftab[i].l4_clk - l4_rate) < 3 && + abs(ftab[i].dss1_clk - dss1_rate) < 3) { + min_l4_ticks = ftab[i].min_l4_ticks; + break; + } + } + if (i == ARRAY_SIZE(ftab)) { + /* Can't be sure, return anyway the maximum not + * rate-limited. This might cause a problem only for the + * tearing synchronisation. + */ + DSSERR("can't determine maximum RFBI transfer rate\n"); + return rfbi.l4_khz * 1000; + } + return rfbi.l4_khz * 1000 / min_l4_ticks; +} +#else +int rfbi_get_max_tx_rate(void) +{ + return rfbi.l4_khz * 1000; +} +#endif + +static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) +{ + *clk_period = 1000000000 / rfbi.l4_khz; + *max_clk_div = 2; +} + +static int rfbi_convert_timings(struct rfbi_timings *t) +{ + u32 l; + int reon, reoff, weon, weoff, cson, csoff, cs_pulse; + int actim, recyc, wecyc; + int div = t->clk_div; + + if (div <= 0 || div > 2) + return -1; + + /* Make sure that after conversion it still holds that: + * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff, + * csoff > cson, csoff >= max(weoff, reoff), actim > reon + */ + weon = ps_to_rfbi_ticks(t->we_on_time, div); + weoff = ps_to_rfbi_ticks(t->we_off_time, div); + if (weoff <= weon) + weoff = weon + 1; + if (weon > 0x0f) + return -1; + if (weoff > 0x3f) + return -1; + + reon = ps_to_rfbi_ticks(t->re_on_time, div); + reoff = ps_to_rfbi_ticks(t->re_off_time, div); + if (reoff <= reon) + reoff = reon + 1; + if (reon > 0x0f) + return -1; + if (reoff > 0x3f) + return -1; + + cson = ps_to_rfbi_ticks(t->cs_on_time, div); + csoff = ps_to_rfbi_ticks(t->cs_off_time, div); + if (csoff <= cson) + csoff = cson + 1; + if (csoff < max(weoff, reoff)) + csoff = max(weoff, reoff); + if (cson > 0x0f) + return -1; + if (csoff > 0x3f) + return -1; + + l = cson; + l |= csoff << 4; + l |= weon << 10; + l |= weoff << 14; + l |= reon << 20; + l |= reoff << 24; + + t->tim[0] = l; + + actim = ps_to_rfbi_ticks(t->access_time, div); + if (actim <= reon) + actim = reon + 1; + if (actim > 0x3f) + return -1; + + wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div); + if (wecyc < weoff) + wecyc = weoff; + if (wecyc > 0x3f) + return -1; + + recyc = ps_to_rfbi_ticks(t->re_cycle_time, div); + if (recyc < reoff) + recyc = reoff; + if (recyc > 0x3f) + return -1; + + cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div); + if (cs_pulse > 0x3f) + return -1; + + l = wecyc; + l |= recyc << 6; + l |= cs_pulse << 12; + l |= actim << 22; + + t->tim[1] = l; + + t->tim[2] = div - 1; + + t->converted = 1; + + return 0; +} + +/* xxx FIX module selection missing */ +int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode, + unsigned hs_pulse_time, unsigned vs_pulse_time, + int hs_pol_inv, int vs_pol_inv, int extif_div) +{ + int hs, vs; + int min; + u32 l; + + hs = ps_to_rfbi_ticks(hs_pulse_time, 1); + vs = ps_to_rfbi_ticks(vs_pulse_time, 1); + if (hs < 2) + return -EDOM; + if (mode == OMAP_DSS_RFBI_TE_MODE_2) + min = 2; + else /* OMAP_DSS_RFBI_TE_MODE_1 */ + min = 4; + if (vs < min) + return -EDOM; + if (vs == hs) + return -EINVAL; + rfbi.te_mode = mode; + DSSDBG("setup_te: mode %d hs %d vs %d hs_inv %d vs_inv %d\n", + mode, hs, vs, hs_pol_inv, vs_pol_inv); + + rfbi_enable_clocks(1); + rfbi_write_reg(RFBI_HSYNC_WIDTH, hs); + rfbi_write_reg(RFBI_VSYNC_WIDTH, vs); + + l = rfbi_read_reg(RFBI_CONFIG(0)); + if (hs_pol_inv) + l &= ~(1 << 21); + else + l |= 1 << 21; + if (vs_pol_inv) + l &= ~(1 << 20); + else + l |= 1 << 20; + rfbi_enable_clocks(0); + + return 0; +} +EXPORT_SYMBOL(omap_rfbi_setup_te); + +/* xxx FIX module selection missing */ +int omap_rfbi_enable_te(int enable, unsigned line) +{ + u32 l; + + DSSDBG("te %d line %d mode %d\n", enable, line, rfbi.te_mode); + if (line > (1 << 11) - 1) + return -EINVAL; + + rfbi_enable_clocks(1); + l = rfbi_read_reg(RFBI_CONFIG(0)); + l &= ~(0x3 << 2); + if (enable) { + rfbi.te_enabled = 1; + l |= rfbi.te_mode << 2; + } else + rfbi.te_enabled = 0; + rfbi_write_reg(RFBI_CONFIG(0), l); + rfbi_write_reg(RFBI_LINE_NUMBER, line); + rfbi_enable_clocks(0); + + return 0; +} +EXPORT_SYMBOL(omap_rfbi_enable_te); + +#if 0 +static void rfbi_enable_config(int enable1, int enable2) +{ + u32 l; + int cs = 0; + + if (enable1) + cs |= 1<<0; + if (enable2) + cs |= 1<<1; + + rfbi_enable_clocks(1); + + l = rfbi_read_reg(RFBI_CONTROL); + + l = FLD_MOD(l, cs, 3, 2); + l = FLD_MOD(l, 0, 1, 1); + + rfbi_write_reg(RFBI_CONTROL, l); + + + l = rfbi_read_reg(RFBI_CONFIG(0)); + l = FLD_MOD(l, 0, 3, 2); /* TRIGGERMODE: ITE */ + /*l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */ + /*l |= FLD_VAL(0, 8, 7); */ /* L4FORMAT, 1pix/L4 */ + + l = FLD_MOD(l, 0, 16, 16); /* A0POLARITY */ + l = FLD_MOD(l, 1, 20, 20); /* TE_VSYNC_POLARITY */ + l = FLD_MOD(l, 1, 21, 21); /* HSYNCPOLARITY */ + + l = FLD_MOD(l, OMAP_DSS_RFBI_PARALLELMODE_8, 1, 0); + rfbi_write_reg(RFBI_CONFIG(0), l); + + rfbi_enable_clocks(0); +} +#endif + +int rfbi_configure(int rfbi_module, int bpp, int lines) +{ + u32 l; + int cycle1 = 0, cycle2 = 0, cycle3 = 0; + enum omap_rfbi_cycleformat cycleformat; + enum omap_rfbi_datatype datatype; + enum omap_rfbi_parallelmode parallelmode; + + switch (bpp) { + case 12: + datatype = OMAP_DSS_RFBI_DATATYPE_12; + break; + case 16: + datatype = OMAP_DSS_RFBI_DATATYPE_16; + break; + case 18: + datatype = OMAP_DSS_RFBI_DATATYPE_18; + break; + case 24: + datatype = OMAP_DSS_RFBI_DATATYPE_24; + break; + default: + BUG(); + return 1; + } + rfbi.datatype = datatype; + + switch (lines) { + case 8: + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_8; + break; + case 9: + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_9; + break; + case 12: + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_12; + break; + case 16: + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_16; + break; + default: + BUG(); + return 1; + } + rfbi.parallelmode = parallelmode; + + if ((bpp % lines) == 0) { + switch (bpp / lines) { + case 1: + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_1_1; + break; + case 2: + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_2_1; + break; + case 3: + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_1; + break; + default: + BUG(); + return 1; + } + } else if ((2 * bpp % lines) == 0) { + if ((2 * bpp / lines) == 3) + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_2; + else { + BUG(); + return 1; + } + } else { + BUG(); + return 1; + } + + switch (cycleformat) { + case OMAP_DSS_RFBI_CYCLEFORMAT_1_1: + cycle1 = lines; + break; + + case OMAP_DSS_RFBI_CYCLEFORMAT_2_1: + cycle1 = lines; + cycle2 = lines; + break; + + case OMAP_DSS_RFBI_CYCLEFORMAT_3_1: + cycle1 = lines; + cycle2 = lines; + cycle3 = lines; + break; + + case OMAP_DSS_RFBI_CYCLEFORMAT_3_2: + cycle1 = lines; + cycle2 = (lines / 2) | ((lines / 2) << 16); + cycle3 = (lines << 16); + break; + } + + rfbi_enable_clocks(1); + + REG_FLD_MOD(RFBI_CONTROL, 0, 3, 2); /* clear CS */ + + l = 0; + l |= FLD_VAL(parallelmode, 1, 0); + l |= FLD_VAL(0, 3, 2); /* TRIGGERMODE: ITE */ + l |= FLD_VAL(0, 4, 4); /* TIMEGRANULARITY */ + l |= FLD_VAL(datatype, 6, 5); + /* l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */ + l |= FLD_VAL(0, 8, 7); /* L4FORMAT, 1pix/L4 */ + l |= FLD_VAL(cycleformat, 10, 9); + l |= FLD_VAL(0, 12, 11); /* UNUSEDBITS */ + l |= FLD_VAL(0, 16, 16); /* A0POLARITY */ + l |= FLD_VAL(0, 17, 17); /* REPOLARITY */ + l |= FLD_VAL(0, 18, 18); /* WEPOLARITY */ + l |= FLD_VAL(0, 19, 19); /* CSPOLARITY */ + l |= FLD_VAL(1, 20, 20); /* TE_VSYNC_POLARITY */ + l |= FLD_VAL(1, 21, 21); /* HSYNCPOLARITY */ + rfbi_write_reg(RFBI_CONFIG(rfbi_module), l); + + rfbi_write_reg(RFBI_DATA_CYCLE1(rfbi_module), cycle1); + rfbi_write_reg(RFBI_DATA_CYCLE2(rfbi_module), cycle2); + rfbi_write_reg(RFBI_DATA_CYCLE3(rfbi_module), cycle3); + + + l = rfbi_read_reg(RFBI_CONTROL); + l = FLD_MOD(l, rfbi_module+1, 3, 2); /* Select CSx */ + l = FLD_MOD(l, 0, 1, 1); /* clear bypass */ + rfbi_write_reg(RFBI_CONTROL, l); + + + DSSDBG("RFBI config: bpp %d, lines %d, cycles: 0x%x 0x%x 0x%x\n", + bpp, lines, cycle1, cycle2, cycle3); + + rfbi_enable_clocks(0); + + return 0; +} + +static int rfbi_find_display(struct omap_display *disp) +{ + if (disp == rfbi.display[0]) + return 0; + + if (disp == rfbi.display[1]) + return 1; + + BUG(); + return -1; +} + + +static void signal_fifo_waiters(void) +{ + if (atomic_read(&rfbi.cmd_fifo_full) > 0) { + /* DSSDBG("SIGNALING: Fifo not full for waiter!\n"); */ + complete(&rfbi.cmd_done); + atomic_dec(&rfbi.cmd_fifo_full); + } +} + +/* returns 1 for async op, and 0 for sync op */ +static int do_update(struct omap_display *display, struct update_region *upd) +{ + int x = upd->x; + int y = upd->y; + int w = upd->w; + int h = upd->h; + + if (display->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { + /*display->ctrl->enable_te(display, 1); */ + + dispc_setup_partial_planes(display, &x, &y, &w, &h); + + display->ctrl->setup_update(display, x, y, w, h); + + rfbi_transfer_area(w, h, NULL, NULL); + + return 1; + } else { + struct omap_overlay *ovl; + void *addr; + int scr_width; +#ifdef MEASURE_PERF + ktime_t t1, t2; +#endif + ovl = &display->manager->overlays[0]; + scr_width = ovl->info.screen_width; + addr = ovl->info.vaddr; + + display->ctrl->setup_update(display, x, y, w, h); + +#ifdef MEASURE_PERF + t1 = ktime_get(); +#endif + omap_rfbi_write_pixels(addr, scr_width, + x, y, w, h); +#ifdef MEASURE_PERF + t2 = ktime_get(); + t1 = ktime_sub(t2, t1); + DSSDBG("L4 FRAMEDONE in %lld ns\n", + ktime_to_ns(t1)); +#endif + return 0; + } +} + +static void process_cmd_fifo(void) +{ + int len; + struct update_param p; + struct omap_display *display; + unsigned long flags; + + if (atomic_inc_return(&rfbi.cmd_pending) != 1) + return; + + while (true) { + spin_lock_irqsave(rfbi.cmd_fifo->lock, flags); + + len = __kfifo_get(rfbi.cmd_fifo, (unsigned char *)&p, + sizeof(struct update_param)); + if (len == 0) { + DSSDBG("nothing more in fifo\n"); + atomic_set(&rfbi.cmd_pending, 0); + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); + break; + } + + /* DSSDBG("fifo full %d\n", rfbi.cmd_fifo_full.counter);*/ + + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); + + BUG_ON(len != sizeof(struct update_param)); + BUG_ON(p.rfbi_module > 1); + + display = rfbi.display[p.rfbi_module]; + + if (p.cmd == RFBI_CMD_UPDATE) { + if (do_update(display, &p.par.r)) + break; /* async op */ + } else if (p.cmd == RFBI_CMD_SYNC) { + DSSDBG("Signaling SYNC done!\n"); + complete(p.par.sync); + } else + BUG(); + } + + signal_fifo_waiters(); +} + +static void rfbi_push_cmd(struct update_param *p) +{ + int ret; + + while (1) { + unsigned long flags; + int available; + + spin_lock_irqsave(rfbi.cmd_fifo->lock, flags); + available = RFBI_CMD_FIFO_LEN_BYTES - + __kfifo_len(rfbi.cmd_fifo); + +/* DSSDBG("%d bytes left in fifo\n", available); */ + if (available < sizeof(struct update_param)) { + DSSDBG("Going to wait because FIFO FULL..\n"); + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); + atomic_inc(&rfbi.cmd_fifo_full); + wait_for_completion(&rfbi.cmd_done); + /*DSSDBG("Woke up because fifo not full anymore\n");*/ + continue; + } + + ret = __kfifo_put(rfbi.cmd_fifo, (unsigned char *)p, + sizeof(struct update_param)); +/* DSSDBG("pushed %d bytes\n", ret);*/ + + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); + + BUG_ON(ret != sizeof(struct update_param)); + + break; + } +} + +static void rfbi_push_update(int rfbi_module, int x, int y, int w, int h) +{ + struct update_param p; + + p.rfbi_module = rfbi_module; + p.cmd = RFBI_CMD_UPDATE; + + p.par.r.x = x; + p.par.r.y = y; + p.par.r.w = w; + p.par.r.h = h; + + DSSDBG("RFBI pushed %d,%d %dx%d\n", x, y, w, h); + + rfbi_push_cmd(&p); + + process_cmd_fifo(); +} + +static void rfbi_push_sync(int rfbi_module, struct completion *sync_comp) +{ + struct update_param p; + + p.rfbi_module = rfbi_module; + p.cmd = RFBI_CMD_SYNC; + p.par.sync = sync_comp; + + rfbi_push_cmd(&p); + + DSSDBG("RFBI sync pushed to cmd fifo\n"); + + process_cmd_fifo(); +} + +int rfbi_init(void) +{ + u32 rev; + u32 l; + + spin_lock_init(&rfbi.cmd_lock); + rfbi.cmd_fifo = kfifo_alloc(RFBI_CMD_FIFO_LEN_BYTES, GFP_KERNEL, + &rfbi.cmd_lock); + if (IS_ERR(rfbi.cmd_fifo)) + return -ENOMEM; + + init_completion(&rfbi.cmd_done); + atomic_set(&rfbi.cmd_fifo_full, 0); + atomic_set(&rfbi.cmd_pending, 0); + + rfbi.base = ioremap(RFBI_BASE, SZ_256); + if (!rfbi.base) { + DSSERR("can't ioremap RFBI\n"); + return -ENOMEM; + } + + rfbi.dss_ick = get_dss_ick(); + rfbi.dss1_fck = get_dss1_fck(); + + rfbi_enable_clocks(1); + + msleep(10); + + rfbi.l4_khz = clk_get_rate(rfbi.dss_ick) / 1000; + + /* Enable autoidle and smart-idle */ + l = rfbi_read_reg(RFBI_SYSCONFIG); + l |= (1 << 0) | (2 << 3); + rfbi_write_reg(RFBI_SYSCONFIG, l); + + rev = rfbi_read_reg(RFBI_REVISION); + printk(KERN_INFO "OMAP RFBI rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + rfbi_enable_clocks(0); + + return 0; +} + +void rfbi_exit(void) +{ + DSSDBG("rfbi_exit\n"); + + kfifo_free(rfbi.cmd_fifo); + + iounmap(rfbi.base); +} + +/* struct omap_display support */ +static void rfbi_display_set_mode(struct omap_display *display, + int x_res, int y_res, int bpp) +{ + display->bpp = bpp; + + dispc_set_tft_data_lines(display->bpp); + + if (rfbi_configure(display->hw_config.u.rfbi.channel, + display->bpp, + display->hw_config.u.rfbi.data_lines) != 0) { + DSSERR("can't configure rfbi\n"); + } + + display->ctrl->set_mode(display, x_res, y_res, bpp); +} + + +static int rfbi_display_update(struct omap_display *display, + int x, int y, int w, int h) +{ + int rfbi_module; + + if (w == 0 || h == 0) + return 0; + + rfbi_module = rfbi_find_display(display); + + rfbi_push_update(rfbi_module, x, y, w, h); + + return 0; +} + +static int rfbi_display_sync(struct omap_display *display) +{ + struct completion sync_comp; + int rfbi_module; + + rfbi_module = rfbi_find_display(display); + + init_completion(&sync_comp); + rfbi_push_sync(rfbi_module, &sync_comp); + DSSDBG("Waiting for SYNC to happen...\n"); + wait_for_completion(&sync_comp); + DSSDBG("Released from SYNC\n"); + return 0; +} + +static int rfbi_display_enable_te(struct omap_display *display, int enable) +{ + display->ctrl->enable_te(display, enable); + return 0; +} + +static int rfbi_display_enable(struct omap_display *display) +{ + int r; + + BUG_ON(display->panel == NULL || display->ctrl == NULL); + + r = omap_dispc_register_isr(framedone_callback, NULL, + DISPC_IRQ_FRAMEDONE); + if (r) { + DSSERR("can't get FRAMEDONE irq\n"); + return r; + } + + dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); + + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_RFBI); + + /* FIX select 16bpp as default */ + rfbi_configure(display->hw_config.u.rfbi.channel, + 16, + display->hw_config.u.rfbi.data_lines); + + rfbi_set_timings(display->hw_config.u.rfbi.channel, + &display->ctrl->timings); + + display->ctrl->enable(display); + + return 0; +} + +static void rfbi_display_disable(struct omap_display *display) +{ + display->ctrl->disable(display); + omap_dispc_unregister_isr(framedone_callback); +} + +void rfbi_init_display(struct omap_display *display) +{ + display->enable = rfbi_display_enable; + display->disable = rfbi_display_disable; + display->set_mode = rfbi_display_set_mode; + display->update = rfbi_display_update; + display->sync = rfbi_display_sync; + display->enable_te = rfbi_display_enable_te; + + rfbi.display[display->hw_config.u.rfbi.channel] = display; + + display->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; +} |
From: Tomi V. <tom...@no...> - 2008-11-04 16:10:28
|
Signed-off-by: Tomi Valkeinen <tom...@no...> --- Documentation/arm/OMAP/DSS | 240 ++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 240 insertions(+), 0 deletions(-) create mode 100644 Documentation/arm/OMAP/DSS diff --git a/Documentation/arm/OMAP/DSS b/Documentation/arm/OMAP/DSS new file mode 100644 index 0000000..992ee76 --- /dev/null +++ b/Documentation/arm/OMAP/DSS @@ -0,0 +1,240 @@ +OMAP2/3 Display Subsystem +------------------------- + +This is an almost total rewrite of the OMAP FB driver in drivers/video/omap +(let's call it DSS1). The main differences between DSS1 and DSS2 are DSI, +TV-out and multiple display support. + +The DSS2 driver (omap-dss module) is in arch/arm/plat-omap/dss/, and the FB, +panel and controller drivers are in drivers/video/omap2/. DSS1 and DSS2 live +currently side by side, you can choose which one to use. + +Features +-------- + +Working and tested features include: + +- MIPI DPI (parallel) output +- MIPI DSI output in command mode +- MIPI DBI (RFBI) output (not tested for a while, might've gotten broken) +- SDI output +- TV output +- All pieces can be compiled as a module or inside kernel +- Use DISPC to update any of the outputs +- Use CPU to update RFBI or DSI output +- OMAP DISPC planes +- RGB16, RGB24 packed, RGB24 unpacked +- YUV2, UYVY +- Scaling +- Adjusting DSS FCK to find a good pixel clock +- Use DSI DPLL to create DSS FCK + +omap-dss driver +------------ + +The DSS driver does not itself have any support for Linux framebuffer, V4L or +such like the current ones, but it has an internal kernel API that upper level +drivers can use. + +The DSS driver models OMAP's overlays, overlay managers and displays in a +flexible way to enable non-common multi-display configuration. In addition to +modelling the hardware overlays, omap-dss supports virtual overlays and overlay +managers. These can be used when updating a display with CPU or system DMA, +instead of DISPC. + +Panel and controller drivers +---------------------------- + +The drivers implement panel or controller specific functionality and are not +visible to users except through omapfb driver. They register themselves to the +DSS driver. + +omapfb driver +------------- + +The omapfb driver implements arbitrary number of standard linux framebuffers. +These framebuffers can be routed flexibly to any overlays, thus allowing very +dynamic display architecture. + +The driver exports some omapfb specific ioctls, which are compatible with the +ioctls in the old driver. + +The rest of the non standard features are exported via sysfs. Whether the final +implementation will use sysfs, or ioctls, is still open. + +V4L2 drivers +------------ + +Currently there are no V4L2 display drivers planned, but it is possible to +implement such either to omapfb driver, or as a separate one. From omap-dss +point of view the V4L2 drivers should be similar to framebuffer driver. + +Architecture +-------------------- + +Some clarification what the different components do: + + - Framebuffer is a memory area inside OMAP's SDRAM that contains the pixel + data for the image. Framebuffer has width and height and color depth. + - Overlay defines where the pixels are read from and where they go on the + screen. The overlay may be smaller than framebuffer, thus displaying only + part of the framebuffer. The position of the overlay may be changed if + the overlay is smaller than the display. + - Overlay manager combines the overlays in to one image and feeds them to + display. + - Display is the actual physical display device. + +A framebuffer can be connected to multiple overlays to show the same pixel data +on all of the overlays. Note that in this case the overlay input sizes are the +same, but, in case of scalable video overlays, the output size can be +different. Any framebuffer can be connected to any overlay. + +An overlay can be connected to one overlay manager. Also DISPC overlays can be +connected only to DISPC overlay managers, and virtual overlays can be only +connected to virtual overlays. + +An overlay manager can be connected to one display. There are certain +restrictions which kinds of displays an overlay manager can be connected: + + - DISPC TV overlay manager can be only connected to TV display. + - Virtual overlay managers can only be connected to DBI or DSI displays. + - DISPC LCD overlay manager can be connected to all displays, except TV + display. + +Sysfs +----- +The sysfs interface is a hack, but works for testing. I don't think sysfs +interface is the best for this in the final version, but I don't quite know +what would be the best interfaces for these things. + +In /sys/devices/platform/omapfb we have four files: framebuffers, +overlays, managers and displays. You can read them so see the current +setup, and change them by writing to it in the form of +"<item-id> <opt1>:<val1> <opt2>:<val2>..." + +"framebuffers" lists all framebuffers. Its format is: + <fb number> + t:<target overlay> + +"overlays" lists all overlays. Its format is: + <overlay name> + t:<target manager> + x:<xpos> + y:<ypos> + iw:<input width, read only> + ih:<input height, read only> + w:<output width> + h:<output height> + e:<enabled> + +"managers" lists all overlay managers. Its format is: + <manager name> + t:<target display> + +"displays" lists all displays. Its format is: + <display name> + w:<width> + h:<height> + e:<enabled> + u:<update mode> + t:<tear sync on/off> + +There is also a debug sysfs file at /sys/devices/platform/omap-dss/clk which +shows how DSS has configured the clocks. + +Examples +-------- + +In the example scripts "omapfb" is a symlink to /sys/devices/platform/omapfb/. + +Default setup on OMAP3 SDP +-------------------------- + +Here's the default setup on OMAP3 SDP board. All planes go to LCD. DVI +and TV-out are not in use. The columns from left to right are: +framebuffers, overlays, overlay managers, displays. Framebuffers are +handled by omapfb, and the rest by the DSS. + +FB0 --- GFX -\ DVI +FB1 --- VID1 --+- LCD ---- LCD +FB2 --- VID2 -/ TV ----- TV + +Switch from LCD to DVI +---------------------- + +dviline=`cat omapfb/displays |grep dvi` +w=`echo $dviline | cut -d " " -f 2 | cut -d ":" -f 2` +h=`echo $dviline | cut -d " " -f 3 | cut -d ":" -f 2` + +echo "lcd e:0" > omapfb/displays +echo "lcd t:none" > omapfb/managers +fbset -fb /dev/fb0 -xres $w -yres $h +# at this point you have to switch the dvi/lcd dip-switch from the omap board +echo "lcd t:dvi" > omapfb/managers +echo "dvi e:1" > omapfb/displays + +After this the configuration looks like: + +FB0 --- GFX -\ -- DVI +FB1 --- VID1 --+- LCD -/ LCD +FB2 --- VID2 -/ TV ----- TV + +Clone GFX overlay to LCD and TV +------------------------------- + +tvline=`cat /sys/devices/platform/omapfb/displays |grep tv` +w=`echo $tvline | cut -d " " -f 2 | cut -d ":" -f 2` +h=`echo $tvline | cut -d " " -f 3 | cut -d ":" -f 2` + +echo "1 t:none" > omapfb/framebuffers +echo "0 t:gfx,vid1" > omapfb/framebuffers +echo "gfx e:1" > omapfb/overlays +echo "vid1 t:tv w:$w h:$h e:1" > omapfb/overlays +echo "tv e:1" > omapfb/displays + +After this the configuration looks like (only relevant parts shown): + +FB0 +-- GFX ---- LCD ---- LCD + \- VID1 ---- TV ---- TV + +Misc notes +---------- + +OMAP FB allocates the framebuffer memory when it starts using +dma_alloc_writecombine(). This requires large continuous physical memory areas +and you can pre-reserve that area with "Consistent DMA memory size" Kconfig +option. + +Using DSI DPLL to generate pixel clock it is possible produce the pixel clock +of 86.5MHz (max possible), and with that you get 1280x1024@57 output from DVI. + +TODO +---- + +OMAP2 not tested for some time +- DSS2 did work on OMAP2, but I haven't been able to test it for some time. + +DSS locking + +Error checking +- Lots of checks are missing or implemented just as BUG() + +Rotate (external FB) +Rotate (VRFB) +Rotate (SMS) + +System DMA update for DSI +- Can be used for RGB16 and RGB24P modes. Probably not for RGB24U (how + to skip the empty byte?) + +Power management +- Context saving + +Resolution change +- The x/y res of the framebuffer are not display resolutions, but the size + of the overlay. +- The display resolution affects all planes on the display. + +OMAP1 support +- Not sure if needed + |
From: Tomi V. <tom...@no...> - 2008-11-04 16:10:39
|
Signed-off-by: Tomi Valkeinen <tom...@no...> --- arch/arm/plat-omap/dss/venc.c | 499 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 499 insertions(+), 0 deletions(-) create mode 100644 arch/arm/plat-omap/dss/venc.c diff --git a/arch/arm/plat-omap/dss/venc.c b/arch/arm/plat-omap/dss/venc.c new file mode 100644 index 0000000..a9739ad --- /dev/null +++ b/arch/arm/plat-omap/dss/venc.c @@ -0,0 +1,499 @@ +/* + * linux/arch/arm/plat-omap/dss/venc.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tom...@no...> + * + * VENC settings from TI's DSS driver + * + * 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/>. + */ + +#define DSS_SUBSYS_NAME "VENC" + +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> + +#include <linux/semaphore.h> +#include <mach/board.h> +#include <mach/gpio.h> +#include <mach/display.h> +#include <mach/cpu.h> + +#include "dss.h" + +#define VENC_BASE 0x48050C00 + +/* Venc registers */ +#define VENC_REV_ID 0x00 +#define VENC_STATUS 0x04 +#define VENC_F_CONTROL 0x08 +#define VENC_VIDOUT_CTRL 0x10 +#define VENC_SYNC_CTRL 0x14 +#define VENC_LLEN 0x1C +#define VENC_FLENS 0x20 +#define VENC_HFLTR_CTRL 0x24 +#define VENC_CC_CARR_WSS_CARR 0x28 +#define VENC_C_PHASE 0x2C +#define VENC_GAIN_U 0x30 +#define VENC_GAIN_V 0x34 +#define VENC_GAIN_Y 0x38 +#define VENC_BLACK_LEVEL 0x3C +#define VENC_BLANK_LEVEL 0x40 +#define VENC_X_COLOR 0x44 +#define VENC_M_CONTROL 0x48 +#define VENC_BSTAMP_WSS_DATA 0x4C +#define VENC_S_CARR 0x50 +#define VENC_LINE21 0x54 +#define VENC_LN_SEL 0x58 +#define VENC_L21__WC_CTL 0x5C +#define VENC_HTRIGGER_VTRIGGER 0x60 +#define VENC_SAVID__EAVID 0x64 +#define VENC_FLEN__FAL 0x68 +#define VENC_LAL__PHASE_RESET 0x6C +#define VENC_HS_INT_START_STOP_X 0x70 +#define VENC_HS_EXT_START_STOP_X 0x74 +#define VENC_VS_INT_START_X 0x78 +#define VENC_VS_INT_STOP_X__VS_INT_START_Y 0x7C +#define VENC_VS_INT_STOP_Y__VS_EXT_START_X 0x80 +#define VENC_VS_EXT_STOP_X__VS_EXT_START_Y 0x84 +#define VENC_VS_EXT_STOP_Y 0x88 +#define VENC_AVID_START_STOP_X 0x90 +#define VENC_AVID_START_STOP_Y 0x94 +#define VENC_FID_INT_START_X__FID_INT_START_Y 0xA0 +#define VENC_FID_INT_OFFSET_Y__FID_EXT_START_X 0xA4 +#define VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y 0xA8 +#define VENC_TVDETGP_INT_START_STOP_X 0xB0 +#define VENC_TVDETGP_INT_START_STOP_Y 0xB4 +#define VENC_GEN_CTRL 0xB8 +#define VENC_OUTPUT_CONTROL 0xC4 +#define VENC_DAC_B__DAC_C 0xC8 + +static DECLARE_MUTEX(venc_lock); + +struct venc_config { + u32 f_control; + u32 vidout_ctrl; + u32 sync_ctrl; + u32 llen; + u32 flens; + u32 hfltr_ctrl; + u32 cc_carr_wss_carr; + u32 c_phase; + u32 gain_u; + u32 gain_v; + u32 gain_y; + u32 black_level; + u32 blank_level; + u32 x_color; + u32 m_control; + u32 bstamp_wss_data; + u32 s_carr; + u32 line21; + u32 ln_sel; + u32 l21__wc_ctl; + u32 htrigger_vtrigger; + u32 savid__eavid; + u32 flen__fal; + u32 lal__phase_reset; + u32 hs_int_start_stop_x; + u32 hs_ext_start_stop_x; + u32 vs_int_start_x; + u32 vs_int_stop_x__vs_int_start_y; + u32 vs_int_stop_y__vs_ext_start_x; + u32 vs_ext_stop_x__vs_ext_start_y; + u32 vs_ext_stop_y; + u32 avid_start_stop_x; + u32 avid_start_stop_y; + u32 fid_int_start_x__fid_int_start_y; + u32 fid_int_offset_y__fid_ext_start_x; + u32 fid_ext_start_y__fid_ext_offset_y; + u32 tvdetgp_int_start_stop_x; + u32 tvdetgp_int_start_stop_y; + u32 gen_ctrl; + + int width; + int height; +}; + +/* from TRM */ +static const struct venc_config venc_config_pal_trm = { + .f_control = 0, + .vidout_ctrl = 1, + .sync_ctrl = 0x40, + .llen = 0x35F, /* 863 */ + .flens = 0x270, /* 624 */ + .hfltr_ctrl = 0, + .cc_carr_wss_carr = 0x2F7225ED, + .c_phase = 0, + .gain_u = 0x111, + .gain_v = 0x181, + .gain_y = 0x140, + .black_level = 0x3B, + .blank_level = 0x3B, + .x_color = 0x7, + .m_control = 0x2, + .bstamp_wss_data = 0x3F, + .s_carr = 0x2A098ACB, + .line21 = 0, + .ln_sel = 0x01290015, + .l21__wc_ctl = 0x0000F603, + .htrigger_vtrigger = 0, + + .savid__eavid = 0x06A70108, + .flen__fal = 0x00180270, + .lal__phase_reset = 0x00180270, + .hs_int_start_stop_x = 0x00880358, + .hs_ext_start_stop_x = 0x000F035F, + .vs_int_start_x = 0x01A70000, + .vs_int_stop_x__vs_int_start_y = 0x000001A7, + .vs_int_stop_y__vs_ext_start_x = 0x01AF0000, + .vs_ext_stop_x__vs_ext_start_y = 0x000101AF, + .vs_ext_stop_y = 0x00000025, + .avid_start_stop_x = 0x03530083, + .avid_start_stop_y = 0x026C002E, + .fid_int_start_x__fid_int_start_y = 0x0001008A, + .fid_int_offset_y__fid_ext_start_x = 0x002E0138, + .fid_ext_start_y__fid_ext_offset_y = 0x01380001, + + .tvdetgp_int_start_stop_x = 0x00140001, + .tvdetgp_int_start_stop_y = 0x00010001, + .gen_ctrl = 0x00FF0000, + + .width = 720, + .height = 574, /* for some reason, this isn't 576 */ +}; + +/* from TRM */ +static const struct venc_config venc_config_ntsc_trm = { + .f_control = 0, + .vidout_ctrl = 1, + .sync_ctrl = 0x8040, + .llen = 0x359, + .flens = 0x20C, + .hfltr_ctrl = 0, + .cc_carr_wss_carr = 0x043F2631, + .c_phase = 0, + .gain_u = 0x102, + .gain_v = 0x16C, + .gain_y = 0x12F, + .black_level = 0x43, + .blank_level = 0x38, + .x_color = 0x7, + .m_control = 0x1, + .bstamp_wss_data = 0x38, + .s_carr = 0x21F07C1F, + .line21 = 0, + .ln_sel = 0x01310011, + .l21__wc_ctl = 0x0000F003, + .htrigger_vtrigger = 0, + + .savid__eavid = 0x069300F4, + .flen__fal = 0x0016020C, + .lal__phase_reset = 0x00060107, + .hs_int_start_stop_x = 0x008E0350, + .hs_ext_start_stop_x = 0x000F0359, + .vs_int_start_x = 0x01A00000, + .vs_int_stop_x__vs_int_start_y = 0x020701A0, + .vs_int_stop_y__vs_ext_start_x = 0x01AC0024, + .vs_ext_stop_x__vs_ext_start_y = 0x020D01AC, + .vs_ext_stop_y = 0x00000006, + .avid_start_stop_x = 0x03480078, + .avid_start_stop_y = 0x02060024, + .fid_int_start_x__fid_int_start_y = 0x0001008A, + .fid_int_offset_y__fid_ext_start_x = 0x01AC0106, + .fid_ext_start_y__fid_ext_offset_y = 0x01060006, + + .tvdetgp_int_start_stop_x = 0x00140001, + .tvdetgp_int_start_stop_y = 0x00010001, + .gen_ctrl = 0x00F90000, + + .width = 720, + .height = 482, +}; + +static const struct venc_config venc_config_pal_bdghi = { + .f_control = 0, + .vidout_ctrl = 0, + .sync_ctrl = 0, + .hfltr_ctrl = 0, + .x_color = 0, + .line21 = 0, + .ln_sel = 21, + .htrigger_vtrigger = 0, + .tvdetgp_int_start_stop_x = 0x00140001, + .tvdetgp_int_start_stop_y = 0x00010001, + .gen_ctrl = 0x00FB0000, + + .llen = 864-1, + .flens = 625-1, + .cc_carr_wss_carr = 0x2F7625ED, + .c_phase = 0xDF, + .gain_u = 0x111, + .gain_v = 0x181, + .gain_y = 0x140, + .black_level = 0x3e, + .blank_level = 0x3e, + .m_control = 0<<2 | 1<<1, + .bstamp_wss_data = 0x42, + .s_carr = 0x2a098acb, + .l21__wc_ctl = 0<<13 | 0x16<<8 | 0<<0, + .savid__eavid = 0x06A70108, + .flen__fal = 23<<16 | 624<<0, + .lal__phase_reset = 2<<17 | 310<<0, + .hs_int_start_stop_x = 0x00920358, + .hs_ext_start_stop_x = 0x000F035F, + .vs_int_start_x = 0x1a7<<16, + .vs_int_stop_x__vs_int_start_y = 0x000601A7, + .vs_int_stop_y__vs_ext_start_x = 0x01AF0036, + .vs_ext_stop_x__vs_ext_start_y = 0x27101af, + .vs_ext_stop_y = 0x05, + .avid_start_stop_x = 0x03530082, + .avid_start_stop_y = 0x0270002E, + .fid_int_start_x__fid_int_start_y = 0x0005008A, + .fid_int_offset_y__fid_ext_start_x = 0x002E0138, + .fid_ext_start_y__fid_ext_offset_y = 0x01380005, + + .width = 720, + .height = 576, +}; + +static struct { + void __iomem *base; + struct clk *dss_54m_fck; + struct clk *dss_96m_fck; + struct clk *dss_ick; + struct clk *dss1_fck; + const struct venc_config *config; +} venc; + +static struct omap_panel venc_panel = { + .name = "tv-out", + .x_res = 0, + .y_res = 0, + .bpp = 24, +}; + +static inline void venc_write_reg(int idx, u32 val) +{ + __raw_writel(val, venc.base + idx); +} + +static inline u32 venc_read_reg(int idx) +{ + u32 l = __raw_readl(venc.base + idx); + return l; +} + +static void venc_write_config(const struct venc_config *config) +{ + DSSDBG("write venc conf\n"); + + venc_write_reg(VENC_LLEN, config->llen); + venc_write_reg(VENC_FLENS, config->flens); + venc_write_reg(VENC_CC_CARR_WSS_CARR, config->cc_carr_wss_carr); + venc_write_reg(VENC_C_PHASE, config->c_phase); + venc_write_reg(VENC_GAIN_U, config->gain_u); + venc_write_reg(VENC_GAIN_V, config->gain_v); + venc_write_reg(VENC_GAIN_Y, config->gain_y); + venc_write_reg(VENC_BLACK_LEVEL, config->black_level); + venc_write_reg(VENC_BLANK_LEVEL, config->blank_level); + venc_write_reg(VENC_M_CONTROL, config->m_control); + venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data); + venc_write_reg(VENC_S_CARR, config->s_carr); + venc_write_reg(VENC_L21__WC_CTL, config->l21__wc_ctl); + venc_write_reg(VENC_SAVID__EAVID, config->savid__eavid); + venc_write_reg(VENC_FLEN__FAL, config->flen__fal); + venc_write_reg(VENC_LAL__PHASE_RESET, config->lal__phase_reset); + venc_write_reg(VENC_HS_INT_START_STOP_X, config->hs_int_start_stop_x); + venc_write_reg(VENC_HS_EXT_START_STOP_X, config->hs_ext_start_stop_x); + venc_write_reg(VENC_VS_INT_START_X, config->vs_int_start_x); + venc_write_reg(VENC_VS_INT_STOP_X__VS_INT_START_Y, + config->vs_int_stop_x__vs_int_start_y); + venc_write_reg(VENC_VS_INT_STOP_Y__VS_EXT_START_X, + config->vs_int_stop_y__vs_ext_start_x); + venc_write_reg(VENC_VS_EXT_STOP_X__VS_EXT_START_Y, + config->vs_ext_stop_x__vs_ext_start_y); + venc_write_reg(VENC_VS_EXT_STOP_Y, config->vs_ext_stop_y); + venc_write_reg(VENC_AVID_START_STOP_X, config->avid_start_stop_x); + venc_write_reg(VENC_AVID_START_STOP_Y, config->avid_start_stop_y); + venc_write_reg(VENC_FID_INT_START_X__FID_INT_START_Y, + config->fid_int_start_x__fid_int_start_y); + venc_write_reg(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X, + config->fid_int_offset_y__fid_ext_start_x); + venc_write_reg(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y, + config->fid_ext_start_y__fid_ext_offset_y); + + venc_write_reg(VENC_DAC_B__DAC_C, venc_read_reg(VENC_DAC_B__DAC_C)); + venc_write_reg(VENC_VIDOUT_CTRL, config->vidout_ctrl); + venc_write_reg(VENC_HFLTR_CTRL, config->hfltr_ctrl); + venc_write_reg(VENC_X_COLOR, config->x_color); + venc_write_reg(VENC_LINE21, config->line21); + venc_write_reg(VENC_LN_SEL, config->ln_sel); + venc_write_reg(VENC_HTRIGGER_VTRIGGER, config->htrigger_vtrigger); + venc_write_reg(VENC_TVDETGP_INT_START_STOP_X, + config->tvdetgp_int_start_stop_x); + venc_write_reg(VENC_TVDETGP_INT_START_STOP_Y, + config->tvdetgp_int_start_stop_y); + venc_write_reg(VENC_GEN_CTRL, config->gen_ctrl); + venc_write_reg(VENC_F_CONTROL, config->f_control); + venc_write_reg(VENC_SYNC_CTRL, config->sync_ctrl); +} + +static void venc_reset(void) +{ + int t = 1000; + + venc_write_reg(VENC_F_CONTROL, venc_read_reg(VENC_F_CONTROL) | (1<<8)); + while (venc_read_reg(VENC_F_CONTROL) & (1<<8)) { + if (--t == 0) { + DSSERR("Failed to reset venc\n"); + return; + } + } +} + +static void venc_enable_clocks(int enable) +{ + if (enable) { + clk_enable(venc.dss_ick); + clk_enable(venc.dss1_fck); + clk_enable(venc.dss_54m_fck); + clk_enable(venc.dss_96m_fck); + } else { + clk_disable(venc.dss_96m_fck); + clk_disable(venc.dss_54m_fck); + clk_disable(venc.dss1_fck); + clk_disable(venc.dss_ick); + } +} + +int venc_init(void) +{ + u8 rev_id; + int use_pal = 1; /* XXX */ + + if (use_pal) + venc.config = &venc_config_pal_trm; + else + venc.config = &venc_config_ntsc_trm; + + venc_panel.x_res = venc.config->width; + venc_panel.y_res = venc.config->height; + + venc.base = ioremap(VENC_BASE, SZ_1K); + if (!venc.base) { + DSSERR("can't ioremap VENC\n"); + return -ENOMEM; + } + + venc.dss_ick = get_dss_ick(); + venc.dss1_fck = get_dss1_fck(); + venc.dss_54m_fck = get_tv_fck(); + venc.dss_96m_fck = get_96m_fck(); + + /* enable clocks */ + venc_enable_clocks(1); + + /* configure venc */ + venc_reset(); + venc_write_config(venc.config); + + rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff); + printk(KERN_INFO "OMAP VENC rev %d\n", rev_id); + + venc_enable_clocks(0); + + return 0; +} + +void venc_exit(void) +{ + iounmap(venc.base); +} + +static int venc_enable_display(struct omap_display *display) +{ + DSSDBG("venc_enable_display\n"); + + down(&venc_lock); + + if (display->state != OMAP_DSS_DISPLAY_DISABLED) { + up(&venc_lock); + return -EINVAL; + } + + venc_enable_clocks(1); + + dss_set_venc_output(display->hw_config.u.venc.type); + dss_set_dac_pwrdn_bgz(1); + + venc_write_config(venc.config); + + if (display->hw_config.u.venc.type == OMAP_DSS_VENC_TYPE_COMPOSITE) { + if (cpu_is_omap24xx()) + venc_write_reg(VENC_OUTPUT_CONTROL, 0x2); + else + venc_write_reg(VENC_OUTPUT_CONTROL, 0xa); + } else { /* S-Video */ + venc_write_reg(VENC_OUTPUT_CONTROL, 0xd); + } + + dispc_set_digit_size(venc.config->width, venc.config->height/2); + + if (display->hw_config.panel_enable) + display->hw_config.panel_enable(display); + + dispc_enable_digit_out(1); + + display->state = OMAP_DSS_DISPLAY_ACTIVE; + + up(&venc_lock); + + return 0; +} + +static void venc_disable_display(struct omap_display *display) +{ + DSSDBG("venc_disable_display\n"); + + down(&venc_lock); + + if (display->state == OMAP_DSS_DISPLAY_DISABLED) { + up(&venc_lock); + return; + } + + venc_write_reg(VENC_OUTPUT_CONTROL, 0); + dss_set_dac_pwrdn_bgz(0); + + dispc_enable_digit_out(0); + + if (display->hw_config.panel_disable) + display->hw_config.panel_disable(display); + + venc_enable_clocks(0); + + display->state = OMAP_DSS_DISPLAY_DISABLED; + + up(&venc_lock); +} + +void venc_init_display(struct omap_display *display) +{ + display->panel = &venc_panel; + display->enable = venc_enable_display; + display->disable = venc_disable_display; +} |
From: Jarkko N. <jar...@no...> - 2008-11-05 10:28:19
|
On Tue, 04 Nov 2008 18:10:12 +0200 "ext Tomi Valkeinen" <tom...@no...> wrote: > Signed-off-by: Tomi Valkeinen <tom...@no...> > --- > > arch/arm/plat-omap/dss/venc.c | 499 +++++++++++++++++++++++++++++++++++++++++ > 1 files changed, 499 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/plat-omap/dss/venc.c > > +#include <mach/board.h> > +#include <mach/gpio.h> Are these two includes needed? Jarkko |
From: Tomi V. <tom...@no...> - 2008-11-04 16:10:40
|
Signed-off-by: Tomi Valkeinen <tom...@no...> --- arch/arm/plat-omap/dss/dsi.c | 3027 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 3027 insertions(+), 0 deletions(-) create mode 100644 arch/arm/plat-omap/dss/dsi.c diff --git a/arch/arm/plat-omap/dss/dsi.c b/arch/arm/plat-omap/dss/dsi.c new file mode 100644 index 0000000..47e5628 --- /dev/null +++ b/arch/arm/plat-omap/dss/dsi.c @@ -0,0 +1,3027 @@ +/* + * linux/arch/arm/plat-omap/dss/dsi.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tom...@no...> + * + * 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/>. + */ + +#define DSS_SUBSYS_NAME "DSI" + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> + +#include <mach/board.h> +#include <mach/display.h> + +#include "dss.h" + +/*#define VERBOSE*/ +/*#define VERBOSE_IRQ*/ +/*#define MEASURE_PERF*/ + +#define DSI_BASE 0x4804FC00 + +struct dsi_reg { u16 idx; }; + +#define DSI_REG(idx) ((const struct dsi_reg) { idx }) + +/* DSI Protocol Engine */ + +#define DSI_REVISION DSI_REG(0x0000) +#define DSI_SYSCONFIG DSI_REG(0x0010) +#define DSI_SYSSTATUS DSI_REG(0x0014) +#define DSI_IRQSTATUS DSI_REG(0x0018) +#define DSI_IRQENABLE DSI_REG(0x001C) +#define DSI_CTRL DSI_REG(0x0040) +#define DSI_COMPLEXIO_CFG1 DSI_REG(0x0048) +#define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(0x004C) +#define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(0x0050) +#define DSI_CLK_CTRL DSI_REG(0x0054) +#define DSI_TIMING1 DSI_REG(0x0058) +#define DSI_TIMING2 DSI_REG(0x005C) +#define DSI_VM_TIMING1 DSI_REG(0x0060) +#define DSI_VM_TIMING2 DSI_REG(0x0064) +#define DSI_VM_TIMING3 DSI_REG(0x0068) +#define DSI_CLK_TIMING DSI_REG(0x006C) +#define DSI_TX_FIFO_VC_SIZE DSI_REG(0x0070) +#define DSI_RX_FIFO_VC_SIZE DSI_REG(0x0074) +#define DSI_COMPLEXIO_CFG2 DSI_REG(0x0078) +#define DSI_RX_FIFO_VC_FULLNESS DSI_REG(0x007C) +#define DSI_VM_TIMING4 DSI_REG(0x0080) +#define DSI_TX_FIFO_VC_EMPTINESS DSI_REG(0x0084) +#define DSI_VM_TIMING5 DSI_REG(0x0088) +#define DSI_VM_TIMING6 DSI_REG(0x008C) +#define DSI_VM_TIMING7 DSI_REG(0x0090) +#define DSI_STOPCLK_TIMING DSI_REG(0x0094) +#define DSI_VC_CTRL(n) DSI_REG(0x0100 + (n * 0x20)) +#define DSI_VC_TE(n) DSI_REG(0x0104 + (n * 0x20)) +#define DSI_VC_LONG_PACKET_HEADER(n) DSI_REG(0x0108 + (n * 0x20)) +#define DSI_VC_LONG_PACKET_PAYLOAD(n) DSI_REG(0x010C + (n * 0x20)) +#define DSI_VC_SHORT_PACKET_HEADER(n) DSI_REG(0x0110 + (n * 0x20)) +#define DSI_VC_IRQSTATUS(n) DSI_REG(0x0118 + (n * 0x20)) +#define DSI_VC_IRQENABLE(n) DSI_REG(0x011C + (n * 0x20)) + +/* DSIPHY_SCP */ + +#define DSIPHY_CFG0 DSI_REG(0x200 + 0x0000) +#define DSIPHY_CFG1 DSI_REG(0x200 + 0x0004) +#define DSIPHY_CFG2 DSI_REG(0x200 + 0x0008) +#define DSIPHY_CFG5 DSI_REG(0x200 + 0x0014) + +/* DSI_PLL_CTRL_SCP */ + +#define DSI_PLL_CONTROL DSI_REG(0x300 + 0x0000) +#define DSI_PLL_STATUS DSI_REG(0x300 + 0x0004) +#define DSI_PLL_GO DSI_REG(0x300 + 0x0008) +#define DSI_PLL_CONFIGURATION1 DSI_REG(0x300 + 0x000C) +#define DSI_PLL_CONFIGURATION2 DSI_REG(0x300 + 0x0010) + +#define REG_GET(idx, start, end) \ + FLD_GET(dsi_read_reg(idx), start, end) + +#define REG_FLD_MOD(idx, val, start, end) \ + dsi_write_reg(idx, FLD_MOD(dsi_read_reg(idx), val, start, end)) + +/* Global interrupts */ +#define DSI_IRQ_VC0 (1 << 0) +#define DSI_IRQ_VC1 (1 << 1) +#define DSI_IRQ_VC2 (1 << 2) +#define DSI_IRQ_VC3 (1 << 3) +#define DSI_IRQ_WAKEUP (1 << 4) +#define DSI_IRQ_RESYNC (1 << 5) +#define DSI_IRQ_PLL_LOCK (1 << 7) +#define DSI_IRQ_PLL_UNLOCK (1 << 8) +#define DSI_IRQ_PLL_RECALL (1 << 9) +#define DSI_IRQ_COMPLEXIO_ERR (1 << 10) +#define DSI_IRQ_HS_TX_TIMEOUT (1 << 14) +#define DSI_IRQ_LP_RX_TIMEOUT (1 << 15) +#define DSI_IRQ_TE_TRIGGER (1 << 16) +#define DSI_IRQ_ACK_TRIGGER (1 << 17) +#define DSI_IRQ_SYNC_LOST (1 << 18) +#define DSI_IRQ_LDO_POWER_GOOD (1 << 19) +#define DSI_IRQ_TA_TIMEOUT (1 << 20) +#define DSI_IRQ_ERROR_MASK \ + (DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \ + DSI_IRQ_TA_TIMEOUT) +#define DSI_IRQ_CHANNEL_MASK 0xf + +/* Virtual channel interrupts */ +#define DSI_VC_IRQ_CS (1 << 0) +#define DSI_VC_IRQ_ECC_CORR (1 << 1) +#define DSI_VC_IRQ_PACKET_SENT (1 << 2) +#define DSI_VC_IRQ_FIFO_TX_OVF (1 << 3) +#define DSI_VC_IRQ_FIFO_RX_OVF (1 << 4) +#define DSI_VC_IRQ_BTA (1 << 5) +#define DSI_VC_IRQ_ECC_NO_CORR (1 << 6) +#define DSI_VC_IRQ_FIFO_TX_UDF (1 << 7) +#define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8) +#define DSI_VC_IRQ_ERROR_MASK \ + (DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \ + DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \ + DSI_VC_IRQ_FIFO_TX_UDF) + +/* ComplexIO interrupts */ +#define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0) +#define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1) +#define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2) +#define DSI_CIO_IRQ_ERRESC1 (1 << 5) +#define DSI_CIO_IRQ_ERRESC2 (1 << 6) +#define DSI_CIO_IRQ_ERRESC3 (1 << 7) +#define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10) +#define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11) +#define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12) +#define DSI_CIO_IRQ_STATEULPS1 (1 << 15) +#define DSI_CIO_IRQ_STATEULPS2 (1 << 16) +#define DSI_CIO_IRQ_STATEULPS3 (1 << 17) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25) +#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30) +#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31) + +#define DSI_DT_DCS_SHORT_WRITE_0 0x05 +#define DSI_DT_DCS_SHORT_WRITE_1 0x15 +#define DSI_DT_DCS_READ 0x06 +#define DSI_DT_SET_MAX_RET_PKG_SIZE 0x37 +#define DSI_DT_NULL_PACKET 0x09 +#define DSI_DT_DCS_LONG_WRITE 0x39 + +#define DSI_DT_RX_ACK_WITH_ERR 0x02 +#define DSI_DT_RX_DCS_LONG_READ 0x1c +#define DSI_DT_RX_SHORT_READ_1 0x21 +#define DSI_DT_RX_SHORT_READ_2 0x22 + +#define FINT_MAX 2100000 +#define FINT_MIN 750000 +#define REGN_MAX (1 << 7) +#define REGM_MAX ((1 << 11) - 1) +#define REGM3_MAX (1 << 4) +#define REGM4_MAX (1 << 4) + +enum fifo_size { + DSI_FIFO_SIZE_0 = 0, + DSI_FIFO_SIZE_32 = 1, + DSI_FIFO_SIZE_64 = 2, + DSI_FIFO_SIZE_96 = 3, + DSI_FIFO_SIZE_128 = 4, +}; + +static struct +{ + void __iomem *base; + + struct clk *dss_ick; + struct clk *dss1_fck; + struct clk *dss2_fck; + + unsigned long dsi1_pll_fclk; /* Hz */ + unsigned long dsi2_pll_fclk; /* Hz */ + unsigned long dsiphy; /* Hz */ + unsigned long ddr_clk; /* Hz */ + + struct { + enum fifo_size fifo_size; + int dest_per; /* destination peripheral 0-3 */ + } vc[4]; + + struct mutex lock; + + struct completion bta_completion; + + spinlock_t update_lock; + int update_ongoing; + int update_syncers; + struct completion update_completion; + struct work_struct framedone_work; + + enum omap_dss_update_mode update_mode; + int use_te; + int framedone_scheduled; /* helps to catch strange framedone bugs */ + + struct { + struct omap_display *display; + int x, y, w, h; + int bytespp; + } update_region; + +#ifdef MEASURE_PERF + ktime_t measure_time; + int measure_frames; +#endif +} dsi; + + +static inline void dsi_write_reg(const struct dsi_reg idx, u32 val) +{ + __raw_writel(val, dsi.base + idx.idx); +} + +static inline u32 dsi_read_reg(const struct dsi_reg idx) +{ + return __raw_readl(dsi.base + idx.idx); +} + +static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum, + int value) +{ + int t = 1000; + + while (REG_GET(idx, bitnum, bitnum) != value) { + if (--t == 0) + return !value; + } + + return value; +} + + +#ifdef MEASURE_PERF +static void start_measuring(void) +{ + dsi.measure_time = ktime_get(); +} + +static void end_measuring(const char *name) +{ + ktime_t t; + u32 total_bytes; + u32 us; + const int numframes = 100; + + if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED) + return; + + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) { + dsi.measure_frames++; + if (dsi.measure_frames < numframes) + return; + dsi.measure_frames = 0; + } + + t = ktime_get(); + t = ktime_sub(t, dsi.measure_time); + us = (u32)ktime_to_us(t); + if (us == 0) + us = 1; + + total_bytes = dsi.update_region.w * + dsi.update_region.h * + dsi.update_region.bytespp; + + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) { + DSSINFO("%s update: %d frames in %u us, %u frames/sec\n", + name, numframes, + us, + 1000*1000 / us); + } else { + DSSINFO("%s update in %u us (%u Hz), %u bytes, %u kbytes/sec\n", + name, + us, + 1000*1000 / us, + total_bytes, + total_bytes * 1000 / us); + } +} +#else +#define start_measuring() +#define end_measuring(x) +#endif + + + + +static void print_irq_status(u32 status) +{ +#ifndef VERBOSE_IRQ + if ((status & ~DSI_IRQ_CHANNEL_MASK) == 0) + return; +#endif + printk(KERN_DEBUG "DSI IRQ: 0x%x: ", status); + +#define PIS(x) \ + if (status & DSI_IRQ_##x) \ + printk(#x " "); +#ifdef VERBOSE_IRQ + PIS(VC0); + PIS(VC1); + PIS(VC2); + PIS(VC3); +#endif + PIS(WAKEUP); + PIS(RESYNC); + PIS(PLL_LOCK); + PIS(PLL_UNLOCK); + PIS(PLL_RECALL); + PIS(COMPLEXIO_ERR); + PIS(HS_TX_TIMEOUT); + PIS(LP_RX_TIMEOUT); + PIS(TE_TRIGGER); + PIS(ACK_TRIGGER); + PIS(SYNC_LOST); + PIS(LDO_POWER_GOOD); + PIS(TA_TIMEOUT); +#undef PIS + + printk("\n"); +} + +static void print_irq_status_vc(int channel, u32 status) +{ +#ifndef VERBOSE_IRQ + if ((status & ~DSI_VC_IRQ_PACKET_SENT) == 0) + return; +#endif + printk(KERN_DEBUG "DSI VC(%d) IRQ 0x%x: ", channel, status); + +#define PIS(x) \ + if (status & DSI_VC_IRQ_##x) \ + printk(#x " "); + PIS(CS); + PIS(ECC_CORR); +#ifdef VERBOSE_IRQ + PIS(PACKET_SENT); +#endif + PIS(FIFO_TX_OVF); + PIS(FIFO_RX_OVF); + PIS(BTA); + PIS(ECC_NO_CORR); + PIS(FIFO_TX_UDF); + PIS(PP_BUSY_CHANGE); +#undef PIS + printk("\n"); +} + +static void print_irq_status_cio(u32 status) +{ + printk(KERN_DEBUG "DSI CIO IRQ 0x%x: ", status); + +#define PIS(x) \ + if (status & DSI_CIO_IRQ_##x) \ + printk(#x " "); + PIS(ERRSYNCESC1); + PIS(ERRSYNCESC2); + PIS(ERRSYNCESC3); + PIS(ERRESC1); + PIS(ERRESC2); + PIS(ERRESC3); + PIS(ERRCONTROL1); + PIS(ERRCONTROL2); + PIS(ERRCONTROL3); + PIS(STATEULPS1); + PIS(STATEULPS2); + PIS(STATEULPS3); + PIS(ERRCONTENTIONLP0_1); + PIS(ERRCONTENTIONLP1_1); + PIS(ERRCONTENTIONLP0_2); + PIS(ERRCONTENTIONLP1_2); + PIS(ERRCONTENTIONLP0_3); + PIS(ERRCONTENTIONLP1_3); + PIS(ULPSACTIVENOT_ALL0); + PIS(ULPSACTIVENOT_ALL1); +#undef PIS + + printk("\n"); +} + +static int debug_irq; + +/* called from dss */ +void dsi_irq_handler(void) +{ + u32 irqstatus, vcstatus, ciostatus; + int i; + + irqstatus = dsi_read_reg(DSI_IRQSTATUS); + + if (irqstatus & DSI_IRQ_ERROR_MASK) { + DSSERR("DSI error, irqstatus %x\n", irqstatus); + print_irq_status(irqstatus); + } else if (debug_irq) { + print_irq_status(irqstatus); + } + + for (i = 0; i < 4; ++i) { + if ((irqstatus & (1<<i)) == 0) + continue; + + vcstatus = dsi_read_reg(DSI_VC_IRQSTATUS(i)); + + if (vcstatus & DSI_VC_IRQ_BTA) + complete(&dsi.bta_completion); + + if (vcstatus & DSI_VC_IRQ_ERROR_MASK) { + DSSERR("DSI VC(%d) error, vc irqstatus %x\n", + i, vcstatus); + print_irq_status_vc(i, vcstatus); + } else if (debug_irq) { + print_irq_status_vc(i, vcstatus); + } + + dsi_write_reg(DSI_VC_IRQSTATUS(i), vcstatus); + } + + if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) { + ciostatus = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); + + dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, ciostatus); + + DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus); + print_irq_status_cio(ciostatus); + } + + dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); +} + + +static void _dsi_initialize_irq(void) +{ + u32 l; + int i; + + /* disable all interrupts */ + dsi_write_reg(DSI_IRQENABLE, 0); + for (i = 0; i < 4; ++i) + dsi_write_reg(DSI_VC_IRQENABLE(i), 0); + dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, 0); + + /* clear interrupt status */ + l = dsi_read_reg(DSI_IRQSTATUS); + dsi_write_reg(DSI_IRQSTATUS, l & ~DSI_IRQ_CHANNEL_MASK); + + for (i = 0; i < 4; ++i) { + l = dsi_read_reg(DSI_VC_IRQSTATUS(i)); + dsi_write_reg(DSI_VC_IRQSTATUS(i), l); + } + + l = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); + dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, l); + + /* enable error irqs */ + l = DSI_IRQ_ERROR_MASK; + dsi_write_reg(DSI_IRQENABLE, l); + + l = DSI_VC_IRQ_ERROR_MASK; + for (i = 0; i < 4; ++i) + dsi_write_reg(DSI_VC_IRQENABLE(i), l); + + /* XXX zonda responds incorrectly, causing control error: + Exit from LP-ESC mode to LP11 uses wrong transition states on the + data lines LP0 and LN0. */ + dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, + -1 & (~DSI_CIO_IRQ_ERRCONTROL2)); +} + +static void dsi_vc_enable_bta_irq(int channel) +{ + u32 l; + + l = dsi_read_reg(DSI_VC_IRQENABLE(channel)); + l |= DSI_VC_IRQ_BTA; + dsi_write_reg(DSI_VC_IRQENABLE(channel), l); +} + +static void dsi_vc_disable_bta_irq(int channel) +{ + u32 l; + + l = dsi_read_reg(DSI_VC_IRQENABLE(channel)); + l &= ~DSI_VC_IRQ_BTA; + dsi_write_reg(DSI_VC_IRQENABLE(channel), l); +} + +/* DSI func clock. this could also be DSI2_PLL_FCLK */ +static inline void enable_clocks(int enable) +{ + if (enable) { + clk_enable(dsi.dss_ick); + clk_enable(dsi.dss1_fck); + } else { + clk_disable(dsi.dss1_fck); + clk_disable(dsi.dss_ick); + } +} + +/* source clock for DSI PLL. this could also be PCLKFREE */ +static inline void dsi_enable_pll_clock(int enable) +{ + if (enable) + clk_enable(dsi.dss2_fck); + else + clk_disable(dsi.dss2_fck); +} + +#if 1 + +#ifdef DEBUG +static void _dsi_print_reset_status(void) +{ + u32 l; + + /* A dummy read using the SCP interface to any DSIPHY register is + * required after DSIPHY reset to complete the reset of the DSI complex + * I/O. */ + l = dsi_read_reg(DSIPHY_CFG5); + + printk(KERN_DEBUG "DSI resets: "); + + l = dsi_read_reg(DSI_PLL_STATUS); + printk("PLL (%d) ", FLD_GET(l, 0, 0)); + + l = dsi_read_reg(DSI_COMPLEXIO_CFG1); + printk("CIO (%d) ", FLD_GET(l, 29, 29)); + + l = dsi_read_reg(DSIPHY_CFG5); + printk("PHY (%x, %d, %d, %d)\n", + FLD_GET(l, 28, 26), + FLD_GET(l, 29, 29), + FLD_GET(l, 30, 30), + FLD_GET(l, 31, 31)); +} +#else +#define _dsi_print_reset_status() +#endif + +static int _dsi_reset(void) +{ + int r = 0; + + /* Soft reset */ + REG_FLD_MOD(DSI_SYSCONFIG, 1, 1, 1); + + if (wait_for_bit_change(DSI_SYSSTATUS, 0, 1) != 1) { + DSSERR("soft reset failed\n"); + r = -ENODEV; + } + + /* A dummy read using the SCP interface to any DSIPHY register is + * required after DSIPHY reset to complete the reset of the DSI complex + * I/O. */ + dsi_read_reg(DSIPHY_CFG5); + + _dsi_print_reset_status(); + + return r; +} +#endif + +static inline int dsi_if_enable(int enable) +{ + DSSDBG("dsi_if_enable(%d)\n", enable); + + enable = enable ? 1 : 0; + REG_FLD_MOD(DSI_CTRL, enable, 0, 0); /* IF_EN */ + + if (wait_for_bit_change(DSI_CTRL, 0, enable) != enable) { + DSSERR("Failed to set dsi_if_enable to %d\n", enable); + return -EIO; + } + + return 0; +} + +static unsigned long dsi_fclk_rate(void) +{ + unsigned long r; + + if (dss_get_dsi_clk_source() == 0) { + /* DSI FCLK source is DSS1_ALWON_FCK, which is dss1_fck */ + r = clk_get_rate(dsi.dss1_fck); + } else { + /* DSI FCLK source is DSI2_PLL_FCLK */ + r = dsi.dsi2_pll_fclk; + } + + return r; +} + +static int dsi_set_lp_clk_divisor(void) +{ + int n; + unsigned long dsi_fclk; + unsigned long mhz; + + /* LP_CLK_DIVISOR, DSI fclk/n, should be 20MHz - 32kHz */ + + dsi_fclk = dsi_fclk_rate(); + + for (n = 1; n < (1 << 13) - 1; ++n) { + mhz = dsi_fclk / n; + if (mhz <= 20*1000*1000) + break; + } + + if (n == (1 << 13) - 1) { + DSSERR("DSI: Failed to find LP_CLK_DIVISOR\n"); + return -EINVAL; + } + + DSSDBG("LP_CLK_DIV %d, LP_CLK %ld\n", n, mhz); + + REG_FLD_MOD(DSI_CLK_CTRL, n, 12, 0); /* LP_CLK_DIVISOR */ + if (dsi_fclk > 30*1000*1000) + REG_FLD_MOD(DSI_CLK_CTRL, 1, 21, 21); /* LP_RX_SYNCHRO_ENABLE */ + + return 0; +} + + +enum dsi_pll_power_state { + DSI_PLL_POWER_OFF = 0x0, + DSI_PLL_POWER_ON_HSCLK = 0x1, + DSI_PLL_POWER_ON_ALL = 0x2, + DSI_PLL_POWER_ON_DIV = 0x3, +}; + +static int dsi_pll_power(enum dsi_pll_power_state state) +{ + int t = 0; + + REG_FLD_MOD(DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_CMD */ + + /* PLL_PWR_STATUS */ + while (FLD_GET(dsi_read_reg(DSI_CLK_CTRL), 29, 28) != state) { + udelay(1); + if (t++ > 1000) { + DSSERR("DSI: Failed to set DSI PLL power mode to %d\n", + state); + return -ENODEV; + } + } + + return 0; +} + +/* return 1 for exact match */ +static int iterate_dispc_divs(int is_tft, unsigned long pck, + struct dsi_clock_info *cur, struct dsi_clock_info *best) +{ + int pcd_min = is_tft ? 2 : 3; + + for (cur->lck_div = 1; cur->lck_div <= 255; ++cur->lck_div) { + unsigned long lck = cur->dispc_fck / cur->lck_div; + + for (cur->pck_div = pcd_min; cur->pck_div <= 255; + ++cur->pck_div) { + + cur->pck = lck / cur->pck_div; + + if (abs(cur->pck - pck) < abs(best->pck - pck)) { + *best = *cur; + /* + DSSDBG("best match fck %ld, pck %ld, regn %d, " + "regm %d, regm3 %d, ld %d, pd %d\n", + best->dispc_fck, + best->pck, + best->regn, best->regm, + best->regm3, + best->lck_div, best->pck_div); + */ + } + + if (cur->pck == pck) + return 1; + + if (cur->pck < pck) + break; + } + + if (lck / pcd_min < cur->pck) + break; + } + + return 0; +} + +int dsi_pll_calc_pck(int is_tft, unsigned long pck, + struct dsi_clock_info *cinfo) +{ + struct dsi_clock_info cur, best; + + DSSDBG("dsi_pll_calc\n"); + + memset(&best, 0, sizeof(best)); + + memset(&cur, 0, sizeof(cur)); + cur.clkin = clk_get_rate(dsi.dss2_fck); + cur.use_dss2_fck = 1; + cur.highfreq = 0; + + /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */ + /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */ + /* To reduce PLL lock time, keep Fint high (around 2 MHz) */ + for (cur.regn = 1; cur.regn < REGN_MAX; ++cur.regn) { + if (cur.highfreq == 0) + cur.fint = cur.clkin / cur.regn; + else + cur.fint = cur.clkin / (2 * cur.regn); + + if (cur.fint > FINT_MAX || cur.fint < FINT_MIN) + continue; + + /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */ + for (cur.regm = 1; cur.regm < REGM_MAX; ++cur.regm) { + unsigned long a, b; + + a = 2 * cur.regm * (cur.clkin/1000); + b = cur.regn * (cur.highfreq + 1); + cur.dsiphy = a / b * 1000; + + if (cur.dsiphy > 1800 * 1000 * 1000) + break; + + /* DSI1_PLL_FCLK(MHz) = DSIPHY(MHz) / regm3 < 173MHz */ + for (cur.regm3 = 1; cur.regm3 < REGM3_MAX; + ++cur.regm3) { + int r; + + cur.dispc_fck = cur.dsiphy / cur.regm3; + + /* this will narrow down the search a bit, + * but still give pixclocks below what was + * requested */ + if (cur.dispc_fck < pck) + break; + + if (cur.dispc_fck > DISPC_MAX_FCK) + continue; + +#ifdef CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK + if (cur.dispc_fck < + pck * CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK) + continue; +#endif + r = iterate_dispc_divs(is_tft, pck, + &cur, &best); + if (r == 1) + goto found; + + } + } + } +found: + + /* DSI2_PLL_FCLK(MHz) = DSIPHY(MHz) / regm4 < 173MHz */ + /* hardcoded 48MHz for now. what should it be? */ + best.regm4 = best.dsiphy / (48000000); + if (best.regm4 > REGM4_MAX) + best.regm4 = REGM4_MAX; + best.dsi_fck = best.dsiphy / best.regm4; + + *cinfo = best; + + return 0; +} + +static int dsi_pll_calc_datafreq(unsigned long datafreq, + struct dsi_clock_info *cinfo) +{ + struct dsi_clock_info cur, best; + const int use_dss2_fck = 1; + + DSSDBG("dsi_pll_calc_datarate\n"); + + memset(&best, 0, sizeof(best)); + + memset(&cur, 0, sizeof(cur)); + cur.use_dss2_fck = use_dss2_fck; + if (use_dss2_fck) { + cur.clkin = clk_get_rate(dsi.dss2_fck); + cur.highfreq = 0; + } else { + cur.clkin = dispc_pclk_rate(); + if (cur.clkin < 32000000) + cur.highfreq = 0; + else + cur.highfreq = 1; + } + + /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */ + /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */ + /* To reduce PLL lock time, keep Fint high (around 2 MHz) */ + for (cur.regn = 1; cur.regn < REGN_MAX; ++cur.regn) { + if (cur.highfreq == 0) + cur.fint = cur.clkin / cur.regn; + else + cur.fint = cur.clkin / (2 * cur.regn); + + if (cur.fint > FINT_MAX || cur.fint < FINT_MIN) + continue; + + /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */ + for (cur.regm = 1; cur.regm < REGM_MAX; ++cur.regm) { + unsigned long a, b; + + a = 2 * cur.regm * (cur.clkin/1000); + b = cur.regn * (cur.highfreq + 1); + cur.dsiphy = a / b * 1000; + + if (cur.dsiphy > 1800 * 1000 * 1000) + break; + + if (abs(cur.dsiphy - datafreq) < + abs(best.dsiphy - datafreq)) { + best = cur; + /* DSSDBG("best %ld\n", best.dsiphy); */ + } + + if (cur.dsiphy == datafreq) + goto found; + } + } +found: + /* DSI1_PLL_FCLK(MHz) = DSIPHY(MHz) / regm3 < 173MHz */ + /* hardcoded 48MHz for now. what should it be? */ + best.regm3 = best.dsiphy / (48000000); + if (best.regm3 > REGM3_MAX) + best.regm3 = REGM3_MAX; + best.dispc_fck = best.dsiphy / best.regm3; + + /* DSI2_PLL_FCLK(MHz) = DSIPHY(MHz) / regm4 < 173MHz */ + /* hardcoded 48MHz for now. what should it be? */ + best.regm4 = best.dsiphy / (48000000); + if (best.regm4 > REGM4_MAX) + best.regm4 = REGM4_MAX; + best.dsi_fck = best.dsiphy / best.regm4; + + *cinfo = best; + + return 0; +} + +int dsi_pll_program(struct dsi_clock_info *cinfo) +{ + int r = 0; + u32 l; + + DSSDBG("dsi_pll_program\n"); + + enable_clocks(1); + + dsi.dsiphy = cinfo->dsiphy; + dsi.ddr_clk = dsi.dsiphy / 4; + dsi.dsi1_pll_fclk = cinfo->dispc_fck; + dsi.dsi2_pll_fclk = cinfo->dsi_fck; + + DSSDBG("DSI Fint %ld\n", cinfo->fint); + + DSSDBG("clkin (%s) rate %ld, highfreq %d\n", + cinfo->use_dss2_fck ? "dss2_fck" : "pclkfree", + cinfo->clkin, + cinfo->highfreq); + + /* DSIPHY == CLKIN4DDR */ + DSSDBG("DSIPHY = 2 * %d / %d * %lu / %d = %lu\n", + cinfo->regm, + cinfo->regn, + cinfo->clkin, + cinfo->highfreq + 1, + cinfo->dsiphy); + + DSSDBG("Data rate on 1 DSI lane %ld Mbps\n", + dsi.dsiphy / 1000 / 1000 / 2); + + DSSDBG("Clock lane freq %ld Hz\n", dsi.ddr_clk); + + DSSDBG("regm3 = %d, dsi1_pll_fclk = %lu\n", + cinfo->regm3, cinfo->dispc_fck); + DSSDBG("regm4 = %d, dsi2_pll_fclk = %lu\n", + cinfo->regm4, cinfo->dsi_fck); + + REG_FLD_MOD(DSI_PLL_CONTROL, 0, 0, 0); /* DSI_PLL_AUTOMODE = manual */ + + l = dsi_read_reg(DSI_PLL_CONFIGURATION1); + l = FLD_MOD(l, 1, 0, 0); /* DSI_PLL_STOPMODE */ + l = FLD_MOD(l, cinfo->regn - 1, 7, 1); /* DSI_PLL_REGN */ + l = FLD_MOD(l, cinfo->regm, 18, 8); /* DSI_PLL_REGM */ + l = FLD_MOD(l, cinfo->regm3 - 1, 22, 19); /* DSI_CLOCK_DIV */ + l = FLD_MOD(l, cinfo->regm4 - 1, 26, 23); /* DSIPROTO_CLOCK_DIV */ + dsi_write_reg(DSI_PLL_CONFIGURATION1, l); + + l = dsi_read_reg(DSI_PLL_CONFIGURATION2); + l = FLD_MOD(l, 7, 4, 1); /* DSI_PLL_FREQSEL */ + /* DSI_PLL_CLKSEL */ + l = FLD_MOD(l, cinfo->use_dss2_fck ? 0 : 1, 11, 11); + l = FLD_MOD(l, cinfo->highfreq, 12, 12); /* DSI_PLL_HIGHFREQ */ + l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ + l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */ + l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */ + dsi_write_reg(DSI_PLL_CONFIGURATION2, l); + + REG_FLD_MOD(DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */ + + if (wait_for_bit_change(DSI_PLL_GO, 0, 0) != 0) { + DSSERR("dsi pll go bit not going down.\n"); + r = -EIO; + goto err; + } + + if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) { + DSSERR("DSI: cannot lock PLL\n"); + r = -EIO; + goto err; + } + + l = dsi_read_reg(DSI_PLL_CONFIGURATION2); + l = FLD_MOD(l, 0, 0, 0); /* DSI_PLL_IDLE */ + l = FLD_MOD(l, 0, 5, 5); /* DSI_PLL_PLLLPMODE */ + l = FLD_MOD(l, 0, 6, 6); /* DSI_PLL_LOWCURRSTBY */ + l = FLD_MOD(l, 0, 7, 7); /* DSI_PLL_TIGHTPHASELOCK */ + l = FLD_MOD(l, 0, 8, 8); /* DSI_PLL_DRIFTGUARDEN */ + l = FLD_MOD(l, 0, 10, 9); /* DSI_PLL_LOCKSEL */ + l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ + l = FLD_MOD(l, 1, 14, 14); /* DSIPHY_CLKINEN */ + l = FLD_MOD(l, 0, 15, 15); /* DSI_BYPASSEN */ + l = FLD_MOD(l, 1, 16, 16); /* DSS_CLOCK_EN */ + l = FLD_MOD(l, 0, 17, 17); /* DSS_CLOCK_PWDN */ + l = FLD_MOD(l, 1, 18, 18); /* DSI_PROTO_CLOCK_EN */ + l = FLD_MOD(l, 0, 19, 19); /* DSI_PROTO_CLOCK_PWDN */ + l = FLD_MOD(l, 0, 20, 20); /* DSI_HSDIVBYPASS */ + dsi_write_reg(DSI_PLL_CONFIGURATION2, l); + + DSSDBG("PLL config done\n"); +err: + enable_clocks(0); + + return r; +} + +int dsi_pll_init(int enable_hsclk, int enable_hsdiv) +{ + int r = 0; + int fck_div, lck_div, pck_div; + unsigned long fck; + enum dsi_pll_power_state pwstate; + + DSSDBG("PLL init\n"); + + enable_clocks(1); + dsi_enable_pll_clock(1); + + /* configure dispc fck and pixel clock to something sane */ + fck = dispc_calc_clock_div(1, 48 * 1000 * 1000, + &fck_div, &lck_div, &pck_div); + if (fck == 0) + return -EINVAL; + + dispc_set_clock_div(fck_div, lck_div, pck_div); + + /* PLL does not come out of reset without this... */ + dispc_pck_free_enable(1); + + if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) { + DSSERR("DSI: PLL not coming out of reset.\n"); + r = -ENODEV; + goto err; + } + + /* ... but if left on, we get problems when planes do not + * fill the whole display. No idea about this XXX */ + dispc_pck_free_enable(0); + + if (enable_hsclk && enable_hsdiv) + pwstate = DSI_PLL_POWER_ON_ALL; + else if (enable_hsclk) + pwstate = DSI_PLL_POWER_ON_HSCLK; + else if (enable_hsdiv) + pwstate = DSI_PLL_POWER_ON_DIV; + else + pwstate = DSI_PLL_POWER_OFF; + + r = dsi_pll_power(pwstate); + + if (r) + goto err; + + enable_clocks(0); + + DSSDBG("PLL init done\n"); + + return 0; +err: + enable_clocks(0); + dsi_enable_pll_clock(0); + return r; +} + +void dsi_pll_uninit(void) +{ + dsi_pll_power(DSI_PLL_POWER_OFF); + dsi_enable_pll_clock(0); + DSSDBG("PLL uninit done\n"); +} + +unsigned long dsi_get_dsi1_pll_rate(void) +{ + return dsi.dsi1_pll_fclk; +} + +unsigned long dsi_get_dsi2_pll_rate(void) +{ + return dsi.dsi2_pll_fclk; +} + +ssize_t dsi_print_clocks(char *buf, ssize_t size) +{ + ssize_t l = 0; + int clksel; + + enable_clocks(1); + + clksel = REG_GET(DSI_PLL_CONFIGURATION2, 11, 11); + + l += snprintf(buf + l, size - l, "- dsi -\n"); + + l += snprintf(buf + l, size - l, "dsi fclk source = %s\n", + dss_get_dsi_clk_source() == 0 ? + "dss1_alwon_fclk" : "dsi2_pll_fclk"); + + l += snprintf(buf + l, size - l, "dsi pll source = %s\n", + clksel == 0 ? + "dss2_alwon_fclk" : "pclkfree"); + + l += snprintf(buf + l, size - l, + "DSIPHY\t\t%lu\nDDR_CLK\t\t%lu\n", + dsi.dsiphy, dsi.ddr_clk); + + l += snprintf(buf + l, size - l, + "dsi1_pll_fck\t%lu (%s)\n" + "dsi2_pll_fck\t%lu (%s)\n", + dsi.dsi1_pll_fclk, + dss_get_dispc_clk_source() == 0 ? "off" : "on", + dsi.dsi2_pll_fclk, + dss_get_dsi_clk_source() == 0 ? "off" : "on"); + + enable_clocks(0); + + return l; +} + + +enum dsi_complexio_power_state { + DSI_COMPLEXIO_POWER_OFF = 0x0, + DSI_COMPLEXIO_POWER_ON = 0x1, + DSI_COMPLEXIO_POWER_ULPS = 0x2, +}; + +static int dsi_complexio_power(enum dsi_complexio_power_state state) +{ + int t = 0; + + /* PWR_CMD */ + REG_FLD_MOD(DSI_COMPLEXIO_CFG1, state, 28, 27); + + /* PWR_STATUS */ + while (FLD_GET(dsi_read_reg(DSI_COMPLEXIO_CFG1), 26, 25) != state) { + udelay(1); + if (t++ > 1000) { + DSSERR("DSI: failed to set complexio power state to " + "%d\n", state); + return -ENODEV; + } + } + + return 0; +} + +static void dsi_complexio_config(struct omap_display *display) +{ + u32 r; + + int clk_lane = display->hw_config.u.dsi.clk_lane; + int data1_lane = display->hw_config.u.dsi.data1_lane; + int data2_lane = display->hw_config.u.dsi.data2_lane; + int clk_pol = display->hw_config.u.dsi.clk_pol; + int data1_pol = display->hw_config.u.dsi.data1_pol; + int data2_pol = display->hw_config.u.dsi.data2_pol; + + r = dsi_read_reg(DSI_COMPLEXIO_CFG1); + r = FLD_MOD(r, clk_lane, 2, 0); + r = FLD_MOD(r, clk_pol, 3, 3); + r = FLD_MOD(r, data1_lane, 6, 4); + r = FLD_MOD(r, data1_pol, 7, 7); + r = FLD_MOD(r, data2_lane, 10, 8); + r = FLD_MOD(r, data2_pol, 11, 11); + dsi_write_reg(DSI_COMPLEXIO_CFG1, r); + + /* The configuration of the DSI complex I/O (number of data lanes, + position, differential order) should not be changed while + DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. In order for + the hardware to take into account a new configuration of the complex + I/O (done in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to + follow this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, + then reset the DSS.DSI_CTRL[0] IF_EN to 0, then set + DSS.DSI_CLK_CTRL[20] LP_CLK_ENABLE to 1 and finally set again the + DSS.DSI_CTRL[0] IF_EN bit to 1. If the sequence is not followed, the + DSI complex I/O configuration is unknown. */ + + /* + REG_FLD_MOD(DSI_CTRL, 1, 0, 0); + REG_FLD_MOD(DSI_CTRL, 0, 0, 0); + REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); + REG_FLD_MOD(DSI_CTRL, 1, 0, 0); + */ +} + +static inline int ns2ddr(int ns) +{ + /* convert time in ns to ddr ticks, rounding up */ + return (ns * (dsi.ddr_clk/1000/1000) + 999) / 1000; +} + +static void dsi_complexio_timings(void) +{ + u32 r; + u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit; + u32 tlpx_half, tclk_trail, tclk_zero; + u32 tclk_prepare; + + /* calculate timings */ + + /* 1 * DDR_CLK = 2 * UI */ + + /* min 40ns + 4*UI max 85ns + 6*UI */ + ths_prepare = ns2ddr(59) + 2; + + /* min 145ns + 10*UI */ + ths_prepare_ths_zero = ns2ddr(145) + 5; + + /* min max(8*UI, 60ns+4*UI) */ + ths_trail = max(4, ns2ddr(60) + 2); + + /* min 100ns */ + ths_exit = ns2ddr(100); + + /* tlpx min 50n */ + tlpx_half = ns2ddr(25); + + /* min 60ns */ + tclk_trail = ns2ddr(60); + + /* min 38ns, max 95ns */ + tclk_prepare = ns2ddr(38); + + /* min tclk-prepare + tclk-zero = 300ns */ + tclk_zero = ns2ddr(300 - 38); + +#ifdef VERBOSE + DSSDBG("ths_prepare %d, ths_prepare_ths_zero %d\n", + ths_prepare, ths_prepare_ths_zero); + DSSDBG("ths_trail %d, ths_exit %d\n", ths_trail, ths_exit); + + + DSSDBG("tlpx_half %d, tclk_trail %d, tclk_zero %d\n", tlpx_half, + tclk_trail, tclk_zero); + DSSDBG("tclk_prepare %d\n", tclk_prepare); +#endif + + /* program timings */ + + r = dsi_read_reg(DSIPHY_CFG0); + r = FLD_MOD(r, ths_prepare, 31, 24); + r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16); + r = FLD_MOD(r, ths_trail, 15, 8); + r = FLD_MOD(r, ths_exit, 7, 0); + dsi_write_reg(DSIPHY_CFG0, r); + + r = dsi_read_reg(DSIPHY_CFG1); + r = FLD_MOD(r, tlpx_half, 22, 16); + r = FLD_MOD(r, tclk_trail, 15, 8); + r = FLD_MOD(r, tclk_zero, 7, 0); + dsi_write_reg(DSIPHY_CFG1, r); + + r = dsi_read_reg(DSIPHY_CFG2); + r = FLD_MOD(r, tclk_prepare, 7, 0); + dsi_write_reg(DSIPHY_CFG2, r); +} + + +static int dsi_complexio_init(struct omap_display *display) +{ + int r = 0; + + DSSDBG("dsi_complexio_init\n"); + + /* CIO_CLK_ICG, enable L3 clk to CIO */ + REG_FLD_MOD(DSI_CLK_CTRL, 1, 14, 14); + + if (wait_for_bit_change(DSIPHY_CFG5, 30, 1) != 1) { + DSSERR("DSI: ComplexIO PHY not coming out of reset.\n"); + r = -ENODEV; + goto err; + } + + dsi_complexio_config(display); + + r = dsi_complexio_power(DSI_COMPLEXIO_POWER_ON); + + if (r) + goto err; + + if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 29, 1) != 1) { + DSSERR("DSI: ComplexIO not coming out of reset.\n"); + r = -ENODEV; + goto err; + } + + if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 21, 1) != 1) { + DSSERR("DSI: ComplexIO LDO power down.\n"); + r = -ENODEV; + goto err; + } + + dsi_complexio_timings(); + + /* + The configuration of the DSI complex I/O (number of data lanes, + position, differential order) should not be changed while + DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. For the + hardware to recognize a new configuration of the complex I/O (done + in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to follow + this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, next + reset the DSS.DSI_CTRL[0] IF_EN to 0, then set DSS.DSI_CLK_CTRL[20] + LP_CLK_ENABLE to 1, and finally, set again the DSS.DSI_CTRL[0] IF_EN + bit to 1. If the sequence is not followed, the DSi complex I/O + configuration is undetermined. + */ + dsi_if_enable(1); + dsi_if_enable(0); + REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ + dsi_if_enable(1); + dsi_if_enable(0); + + DSSDBG("CIO init done\n"); +err: + return r; +} + +static void dsi_complexio_uninit(void) +{ + dsi_complexio_power(DSI_COMPLEXIO_POWER_OFF); +} + + + +static void dsi_config_tx_fifo(enum fifo_size size1, enum fifo_size size2, + enum fifo_size size3, enum fifo_size size4) +{ + u32 r = 0; + int add = 0; + int i; + + dsi.vc[0].fifo_size = size1; + dsi.vc[1].fifo_size = size2; + dsi.vc[2].fifo_size = size3; + dsi.vc[3].fifo_size = size4; + + for (i = 0; i < 4; i++) { + u8 v; + int size = dsi.vc[i].fifo_size; + + if (add + size > 4) { + DSSERR("DSI: Illegal FIFO configuration\n"); + BUG(); + } + + v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); + r |= v << (8 * i); + /*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */ + add += size; + } + + dsi_write_reg(DSI_TX_FIFO_VC_SIZE, r); +} + +static void dsi_config_rx_fifo(enum fifo_size size1, enum fifo_size size2, + enum fifo_size size3, enum fifo_size size4) +{ + u32 r = 0; + int add = 0; + int i; + + dsi.vc[0].fifo_size = size1; + dsi.vc[1].fifo_size = size2; + dsi.vc[2].fifo_size = size3; + dsi.vc[3].fifo_size = size4; + + for (i = 0; i < 4; i++) { + u8 v; + int size = dsi.vc[i].fifo_size; + + if (add + size > 4) { + DSSERR("DSI: Illegal FIFO configuration\n"); + BUG(); + } + + v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); + r |= v << (8 * i); + /*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */ + add += size; + } + + dsi_write_reg(DSI_RX_FIFO_VC_SIZE, r); +} + +static int dsi_force_tx_stop_mode_io(void) +{ + u32 r; + + r = dsi_read_reg(DSI_TIMING1); + r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ + dsi_write_reg(DSI_TIMING1, r); + + if (wait_for_bit_change(DSI_TIMING1, 15, 0) != 0) { + DSSERR("TX_STOP bit not going down\n"); + return -EIO; + } + + return 0; +} + +static void dsi_vc_print_status(int channel) +{ + u32 r; + + r = dsi_read_reg(DSI_VC_CTRL(channel)); + DSSDBG("vc %d: TX_FIFO_NOT_EMPTY %d, BTA_EN %d, VC_BUSY %d, " + "TX_FIFO_FULL %d, RX_FIFO_NOT_EMPTY %d, ", + channel, + FLD_GET(r, 5, 5), + FLD_GET(r, 6, 6), + FLD_GET(r, 15, 15), + FLD_GET(r, 16, 16), + FLD_GET(r, 20, 20)); + + r = dsi_read_reg(DSI_TX_FIFO_VC_EMPTINESS); + DSSDBG("EMPTINESS %d\n", (r >> (8 * channel)) & 0xff); +} + +static void dsi_vc_config(int channel) +{ + u32 r; + + DSSDBG("dsi_vc_config %d\n", channel); + + r = dsi_read_reg(DSI_VC_CTRL(channel)); + + r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */ + r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */ + r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */ + r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */ + r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */ + r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */ + r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */ + + r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */ + r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */ + + dsi_write_reg(DSI_VC_CTRL(channel), r); +} + +static void dsi_vc_config_vp(int channel) +{ + u32 r; + + DSSDBG("dsi_vc_config_vp\n"); + + r = dsi_read_reg(DSI_VC_CTRL(channel)); + + r = FLD_MOD(r, 1, 1, 1); /* SOURCE, 1 = video port */ + r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */ + r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */ + r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */ + r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */ + r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */ + r = FLD_MOD(r, 1, 9, 9); /* MODE_SPEED, high speed on/off */ + + r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */ + r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */ + + dsi_write_reg(DSI_VC_CTRL(channel), r); +} + + +static int dsi_vc_enable(int channel, int enable) +{ + DSSDBG("dsi_vc_enable channel %d, enable %d\n", channel, enable); + + enable = enable ? 1 : 0; + + REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 0, 0); + + if (wait_for_bit_change(DSI_VC_CTRL(channel), 0, enable) != enable) { + DSSERR("Failed to set dsi_vc_enable to %d\n", enable); + return -EIO; + } + + return 0; +} + +static void dsi_vc_enable_hs(int channel, int enable) +{ + DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable); + + dsi_vc_enable(channel, 0); + dsi_if_enable(0); + + REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 9, 9); + + dsi_vc_enable(channel, 1); + dsi_if_enable(1); + + dsi_force_tx_stop_mode_io(); +} + +static void dsi_vc_flush_long_data(int channel) +{ + while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + u32 val; + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n", + (val >> 0) & 0xff, + (val >> 8) & 0xff, + (val >> 16) & 0xff, + (val >> 24) & 0xff); + } +} + +static u16 dsi_vc_flush_receive_data(int channel) +{ + /* RX_FIFO_NOT_EMPTY */ + while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + u32 val; + u8 dt; + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + DSSDBG("\trawval %#08x\n", val); + dt = FLD_GET(val, 7, 0); + if (dt == DSI_DT_RX_ACK_WITH_ERR) { + u16 err = FLD_GET(val, 23, 8); + DSSERR("\tACK with ERROR: %#x\n", err); + if (err & (1 << 9)) + DSSERR("\t\tECC multibit\n"); + if (err & (1 << 11)) + DSSERR("\t\tData type not recognized\n"); + if (err & (1 << 12)) + DSSERR("\t\tInvalid VC ID\n"); + + } else if (dt == DSI_DT_RX_SHORT_READ_1) { + DSSDBG("\tDCS short response, 1 byte: %#x\n", + FLD_GET(val, 23, 8)); + return FLD_GET(val, 23, 8); + } else if (dt == DSI_DT_RX_SHORT_READ_2) { + DSSDBG("\tDCS short response, 2 byte: %#x\n", + FLD_GET(val, 23, 8)); + return FLD_GET(val, 23, 8); + } else if (dt == DSI_DT_RX_DCS_LONG_READ) { + DSSDBG("\tDCS long response, len %d\n", + FLD_GET(val, 23, 8)); + dsi_vc_flush_long_data(channel); + } else { + DSSERR("\tunknown datatype\n"); + } + } + return 0; +} + +static int dsi_vc_send_bta(int channel) +{ + unsigned long tmo; + + /*DSSDBG("dsi_vc_send_bta_sync %d\n", channel); */ + + if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */ + DSSERR("rx fifo not empty when sending BTA, dumping data:\n"); + dsi_vc_flush_receive_data(channel); + } + + REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */ + + tmo = jiffies + msecs_to_jiffies(10); + while (REG_GET(DSI_VC_CTRL(channel), 6, 6) == 1) { + if (time_after(jiffies, tmo)) { + DSSERR("Failed to send BTA\n"); + return -EIO; + } + } + + return 0; +} + +static int dsi_vc_send_bta_sync(int channel) +{ + int r = 0; + + init_completion(&dsi.bta_completion); + + dsi_vc_enable_bta_irq(channel); + + r = dsi_vc_send_bta(channel); + if (r) + goto err; + + if (wait_for_completion_timeout(&dsi.bta_completion, + msecs_to_jiffies(500)) == 0) { + DSSERR("Failed to receive BTA\n"); + r = -EIO; + goto err; + } +err: + dsi_vc_disable_bta_irq(channel); + + return r; +} + +static inline void dsi_vc_write_long_header(int channel, u8 data_type, + u16 len, u8 ecc) +{ + u32 val; + u8 data_id; + + /*data_id = data_type | channel << 6; */ + data_id = data_type | dsi.vc[channel].dest_per << 6; + + val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) | + FLD_VAL(ecc, 31, 24); + + dsi_write_reg(DSI_VC_LONG_PACKET_HEADER(channel), val); +} + +static inline void dsi_vc_write_long_payload(int channel, + u8 b1, u8 b2, u8 b3, u8 b4) +{ + u32 val; + + val = b4 << 24 | b3 << 16 | b2 << 8 | b1 << 0; + +/* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n", + b1, b2, b3, b4, val); */ + + dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(channel), val); +} + +static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, + u8 ecc) +{ + /*u32 val; */ + int i; + u8 *p; + int r = 0; + u8 b1, b2, b3, b4; + + /*DSSDBG("dsi_vc_send_long, %d bytes\n", len); */ + + /* len + header */ + if (dsi.vc[channel].fifo_size * 32 * 4 < len + 4) { + DSSERR("DSI: unable to send long packet: packet too long.\n"); + return -EINVAL; + } + + dsi_vc_write_long_header(channel, data_type, len, ecc); + + /*dsi_vc_print_status(0); */ + + p = data; + for (i = 0; i < len >> 2; i++) { + /*DSSDBG("\tsending full packet %d\n", i); */ + /*dsi_vc_print_status(0); */ + + b1 = *p++; + b2 = *p++; + b3 = *p++; + b4 = *p++; + + dsi_vc_write_long_payload(channel, b1, b2, b3, b4); + } + + i = len % 4; + if (i) { + b1 = 0; b2 = 0; b3 = 0; + + /*DSSDBG("\tsending remainder bytes %d\n", i); */ + + switch (i) { + case 3: + b1 = *p++; + b2 = *p++; + b3 = *p++; + break; + case 2: + b1 = *p++; + b2 = *p++; + break; + case 1: + b1 = *p++; + break; + } + + dsi_vc_write_long_payload(channel, b1, b2, b3, 0); + } + + return r; +} + +static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc) +{ + u32 r; + u8 data_id; +/* + DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n", + channel, + data_type, data & 0xff, (data >> 8) & 0xff); +*/ + if (FLD_GET(dsi_read_reg(DSI_VC_CTRL(channel)), 16, 16)) { + DSSERR("ERROR FIFO FULL, aborting transfer\n"); + return -EINVAL; + } + + data_id = data_type | channel << 6; + + r = (data_id << 0) | (data << 8) | (ecc << 24); + + dsi_write_reg(DSI_VC_SHORT_PACKET_HEADER(channel), r); + + return 0; +} + +int dsi_vc_send_null(int channel) +{ + u8 nullpkg[] = {0, 0, 0, 0}; + return dsi_vc_send_long(0, DSI_DT_NULL_PACKET, nullpkg, 4, 0); +} +EXPORT_SYMBOL(dsi_vc_send_null); + +int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len) +{ + int r; + + BUG_ON(len == 0); + + if (len == 1) { + r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_0, + data[0], 0); + } else if (len == 2) { + r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_1, + data[0] | (data[1] << 8), 0); + } else { + /* 0x39 = DCS Long Write */ + r = dsi_vc_send_long(channel, DSI_DT_DCS_LONG_WRITE, + data, len, 0); + } + + return r; +} +EXPORT_SYMBOL(dsi_vc_dcs_write_nosync); + +int dsi_vc_dcs_write(int channel, u8 *data, int len) +{ + int r; + + r = dsi_vc_dcs_write_nosync(channel, data, len); + if (r) + return r; + + /* Some devices need time to process the msg in low power mode. + This also makes the write synchronous, and checks that + the peripheral is still alive */ + r = dsi_vc_send_bta_sync(channel); + + return r; +} +EXPORT_SYMBOL(dsi_vc_dcs_write); + +int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) +{ + u32 val; + u8 dt; + int debug = 0; + + if (debug) + DSSDBG("dsi_vc_dcs_read\n"); + + dsi_vc_send_short(channel, DSI_DT_DCS_READ, dcs_cmd, 0); + + dsi_vc_send_bta_sync(channel); + + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + if (debug) + DSSDBG("\trawval %#08x\n", val); + dt = FLD_GET(val, 7, 0); + if (dt == DSI_DT_RX_ACK_WITH_ERR) { + u16 err = FLD_GET(val, 23, 8); + DSSERR("\tACK with ERROR: %#x\n", err); + if (err & (1 << 9)) + DSSERR("\t\tECC multibit\n"); + if (err & (1 << 11)) + DSSERR("\t\tData type not recognized\n"); + if (err & (1 << 12)) + DSSERR("\t\tInvalid VC ID\n"); + return -1; + + } else if (dt == DSI_DT_RX_SHORT_READ_1) { + u8 data = FLD_GET(val, 15, 8); + if (debug) + DSSDBG("\tDCS short response, 1 byte: %#x\n", data); + + if (buflen < 1) + return -1; + + buf[0] = data; + + return 1; + } else if (dt == DSI_DT_RX_SHORT_READ_2) { + u16 data = FLD_GET(val, 23, 8); + if (debug) + DSSDBG("\tDCS short response, 2 byte: %#x\n", data); + + if (buflen < 2) + return -1; + + buf[0] = data & 0xff; + buf[1] = (data >> 8) & 0xff; + + return 2; + } else if (dt == DSI_DT_RX_DCS_LONG_READ) { + int x; + int len = FLD_GET(val, 23, 8); + if (debug) + DSSDBG("\tDCS long response, len %d\n", len); + + if (len > buflen) + return -1; + + x = 0; + while (x < len) { + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + if (debug) + DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 " + "%#02x\n", + (val >> 0) & 0xff, + (val >> 8) & 0xff, + (val >> 16) & 0xff, + (val >> 24) & 0xff); + + if (x < len) + buf[x++] = (val >> 0) & 0xff; + if (x < len) + buf[x++] = (val >> 8) & 0xff; + if (x < len) + buf[x++] = (val >> 16) & 0xff; + if (x < len) + buf[x++] = (val >> 24) & 0xff; + } + + return len; + } else { + DSSERR("\tunknown datatype\n"); + return -1; + } +} +EXPORT_SYMBOL(dsi_vc_dcs_read); + + +int dsi_vc_set_max_rx_packet_size(int channel, u16 len) +{ + return dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE, + len, 0); +} +EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size); + + +static int dsi_set_lp_rx_timeout(int ns, int x4, int x16) +{ + u32 r; + unsigned long fck; + int ticks; + + /* ticks in DSI_FCK */ + + fck = dsi_fclk_rate(); + ticks = (fck / 1000 / 1000) * ns / 1000; + + if (ticks > 0x1fff) { + DSSERR("LP_TX_TO too high\n"); + return -EINVAL; + } + + r = dsi_read_reg(DSI_TIMING2); + r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */ + r = FLD_MOD(r, x16, 14, 14); /* LP_RX_TO_X16 */ + r = FLD_MOD(r, x4, 13, 13); /* LP_RX_TO_X4 */ + r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */ + dsi_write_reg(DSI_TIMING2, r); + + DSSDBG("LP_RX_TO %ld ns (%#x ticks)\n", + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / + (fck / 1000 / 1000), + ticks); + + return 0; +} + +static int dsi_set_ta_timeout(int ns, int x8, int x16) +{ + u32 r; + unsigned long fck; + int ticks; + + /* ticks in DSI_FCK */ + + fck = dsi_fclk_rate(); + ticks = (fck / 1000 / 1000) * ns / 1000; + + if (ticks > 0x1fff) { + DSSERR("TA_TO too high\n"); + return -EINVAL; + } + + r = dsi_read_reg(DSI_TIMING1); + r = FLD_MOD(r, 1, 31, 31); /* TA_TO */ + r = FLD_MOD(r, x16, 30, 30); /* TA_TO_X16 */ + r = FLD_MOD(r, x8, 29, 29); /* TA_TO_X8 */ + r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */ + dsi_write_reg(DSI_TIMING1, r); + + DSSDBG("TA_TO %ld ns (%#x ticks)\n", + (ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1) * 1000) / + (fck / 1000 / 1000), + ticks); + + return 0; +} + +static int dsi_set_stop_state_counter(int ns, int x4, int x16) +{ + u32 r; + unsigned long fck; + int ticks; + + /* ticks in DSI_FCK */ + + fck = dsi_fclk_rate(); + ticks = (fck / 1000 / 1000) * ns / 1000; + + if (ticks > 0x1fff) { + DSSERR("STOP_STATE_COUNTER_IO too high\n"); + return -EINVAL; + } + + r = dsi_read_reg(DSI_TIMING1); + r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ + r = FLD_MOD(r, x16, 14, 14); /* STOP_STATE_X16_IO */ + r = FLD_MOD(r, x4, 13, 13); /* STOP_STATE_X4_IO */ + r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */ + dsi_write_reg(DSI_TIMING1, r); + + DSSDBG("STOP_STATE_COUNTER %ld ns (%#x ticks)\n", + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / + (fck / 1000 / 1000), + ticks); + + return 0; +} + +static int dsi_set_hs_tx_timeout(int ns, int x4, int x16) +{ + u32 r; + unsigned long fck; + int ticks; + + /* ticks in TxByteClkHS */ + + fck = dsi.ddr_clk / 4; + ticks = (fck / 1000 / 1000) * ns / 1000; + + if (ticks > 0x1fff) { + DSSERR("HS_TX_TO too high\n"); + return -EINVAL; + } + + r = dsi_read_reg(DSI_TIMING2); + r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */ + r = FLD_MOD(r, x16, 30, 30); /* HS_TX_TO_X16 */ + r = FLD_MOD(r, x4, 29, 29); /* HS_TX_TO_X8 (4 really) */ + r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */ + dsi_write_reg(DSI_TIMING2, r); + + DSSDBG("HS_TX_TO %ld ns (%#x ticks)\n", + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / + (fck / 1000 / 1000), + ticks); + + return 0; +} +static int dsi_proto_config(struct omap_display *display) +{ + u32 r; + int buswidth = 0; + + dsi_config_tx_fifo(DSI_FIFO_SIZE_128, + DSI_FIFO_SIZE_0, + DSI_FIFO_SIZE_0, + DSI_FIFO_SIZE_0); + + dsi_config_rx_fifo(DSI_FIFO_SIZE_128, + DSI_FIFO_SIZE_0, + DSI_FIFO_SIZE_0, + DSI_FIFO_SIZE_0); + + /* XXX what values for the timeouts? */ + dsi_set_stop_state_counter(1000, 0, 0); + + dsi_set_ta_timeout(50000, 1, 1); + + /* 3000ns * 16 */ + dsi_set_lp_rx_timeout(3000, 0, 1); + + /* 10000ns * 4 */ + dsi_set_hs_tx_timeout(10000, 1, 0); + + switch (display->bpp) { + case 16: + buswidth = 0; + break; + case 18: + buswidth = 1; + break; + case 24: + buswidth = 2; + break; + default: + BUG(); + } + + r = dsi_read_reg(DSI_CTRL); + r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */ + r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */ + r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */ + /* XXX what should the ratio be */ + r = FLD_MOD(r, 0, 4, 4); /* VP_CLK_RATIO, VP_PCLK = VP_CLK/2 */ + r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */ + r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */ + r = FLD_MOD(r, 2, 13, 12); /* LINE_BUFFER, 2 lines */ + r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */ + r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */ + r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */ + r = FLD_MOD(r, 0, 25, 25); /* DCS_CMD_CODE, 1=start, 0=continue */ + + dsi_write_reg(DSI_CTRL, r); + + /* we configure vc0 for L4 communication, and + * vc1 for dispc */ + dsi_vc_config(0); + dsi_vc_config_vp(1); + + /* set all vc targets to peripheral 0 */ + dsi.vc[0].dest_per = 0; + dsi.vc[1].dest_per = 0; + dsi.vc[2].dest_per = 0; + dsi.vc[3].dest_per = 0; + + return 0; +} + +static void dsi_proto_timings(void) +{ + int tlpx_half, tclk_zero, tclk_prepare, tclk_trail; + int tclk_pre, tclk_post; + int ddr_clk_pre, ddr_clk_post; + u32 r; + + r = dsi_read_reg(DSIPHY_CFG1); + tlpx_half = FLD_GET(r, 22, 16); + tclk_trail = FLD_GET(r, 15, 8); + tclk_zero = FLD_GET(r, 7, 0); + + r = dsi_read_reg(DSIPHY_CFG2); + tclk_prepare = FLD_GET(r, 7, 0); + + /* min 8*UI */ + tclk_pre = 4; + /* min 60ns + 52*UI */ + tclk_post = ns2ddr(60) + 26; + + ddr_clk_pre = (tclk_pre + tlpx_half*2 + tclk_zero + tclk_prepare) / 4; + ddr_clk_post = (tclk_post + tclk_trail) / 4; + + r = dsi_read_reg(DSI_CLK_TIMING); + r = FLD_MOD(r, ddr_clk_pre, 15, 8); + r = FLD_MOD(r, ddr_clk_post, 7, 0); + dsi_write_reg(DSI_CLK_TIMING, r); + +#ifdef VERBOSE + DSSDBG("ddr_clk_pre %d, ddr_clk_post %d\n", + ddr_clk_pre, + ddr_clk_post); +#endif +} + + +#define DSI_DECL_VARS \ + int __dsi_cb = 0; u32 __dsi_cv = 0; + +#define DSI_FLUSH(ch) \ + if (__dsi_cb > 0) { \ + /*DSSDBG("sending long packet %#010x\n", __dsi_cv);*/ \ + dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \ + __dsi_cb = __dsi_cv = 0; \ + } + +#define DSI_PUSH(ch, data) \ + do { \ + __dsi_cv |= (data) << (__dsi_cb * 8); \ + /*DSSDBG("cv = %#010x, cb = %d\n", __dsi_cv, __dsi_cb);*/ \ + if (++__dsi_cb > 3) \ + DSI_FLUSH(ch); \ + } while (0) + +static int dsi_update_screen_l4(struct omap_display *display, + int x, int y, int w, int h) +{ + /* Note: supports only 24bit colors in 32bit container */ + int first = 1; + int fifo_stalls = 0; + int max_dsi_packet_size; + int max_data_per_packet; + int max_pixels_per_packet; + int pixels_left; + int bytespp = 3; + int scr_width; + u32 *data; + int start_offset; + int horiz_inc; + int current_x; + struct omap_overlay *ovl; + + debug_irq = 0; + + DSSDBG("dsi_update_screen_l4 (%d,%d %dx%d)\n", + x, y, w, h); + + ovl = &display->manager->overlays[0]; + + if (ovl->info.color_mode != OMAP_DSS_COLOR_RGB24U) + return -EINVAL; + + if (display->ctrl->bpp != 24) + return -EINVAL; + + enable_clocks(1); + + scr_width = ovl->info.screen_width; + data = ovl->info.vaddr; + + start_offset = scr_width * y + x; + horiz_inc = scr_width - w; + current_x = x; + + /* We need header(4) + DCSCMD(1) + pixels(numpix*bytespp) bytes + * in fifo */ + + /* When using CPU, max long packet size is TX buffer size */ + max_dsi_packet_size = dsi.vc[0].fifo_size * 32 * 4; + + /* we seem to get better perf if we divide the tx fifo to half, + and while the other half is being sent, we fill the other half + max_dsi_packet_size /= 2; */ + + max_data_per_packet = max_dsi_packet_size - 4 - 1; + + max_pixels_per_packet = max_data_per_packet / bytespp; + + DSSDBG("max_pixels_per_packet %d\n", max_pixels_per_packet); + + display->ctrl->setup_update(display, x, y, w, h); + + pixels_left = w * h; + + DSSDBG("total pixels %d\n", pixels_left); + + data += start_offset; + + dsi.update_region.x = x; + dsi.update_region.y = y; + dsi.update_region.w = w; + dsi.update_region.h = h; + dsi.update_region.bytespp = bytespp; + + start_measuring(); + + while (pixels_left > 0) { + /* 0x2c = write_memory_start */ + /* 0x3c = write_memory_continue */ + u8 dcs_cmd = first ? 0x2c : 0x3c; + int pixels; + DSI_DECL_VARS; + first = 0; + +#if 1 + /* using fifo not empty */ + /* TX_FIFO_NOT_EMPTY */ + while (FLD_GET(dsi_read_reg(DSI_VC_CTRL(0)), 5, 5)) { + udelay(1); + fifo_stalls++; + if (fifo_stalls > 0xfffff) { + DSSERR("fifo stalls overflow, pixels left %d\n", + pixels_left); + dsi_if_enable(0); + enable_clocks(0); + return -EIO; + } + } +#elif 1 + /* using fifo emptiness */ + while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 < + max_dsi_packet_size) { + fifo_stalls++; + if (fifo_stalls > 0xfffff) { + DSSERR("fifo stalls overflow, pixels left %d\n", + pixels_left); + dsi_if_enable(0); + enable_clocks(0); + return -EIO; + } + } +#else + while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 == 0) { + fifo_stalls++; + if (fifo_stalls > 0xfffff) { + DSSERR("fifo stalls overflow, pixels left %d\n", + pixels_left); + dsi_if_enable(0); + enable_clocks(0); + return -EIO; + } + } +#endif + pixels = min(max_pixels_per_packet, pixels_left); + + pixels_left -= pixels; + + dsi_vc_write_long_header(0, DSI_DT_DCS_LONG_WRITE, + 1 + pixels * bytespp, 0); + + DSI_PUSH(0, dcs_cmd); + + while (pixels-- > 0) { + u32 pix = *data++; + + DSI_PUSH(0, (pix >> 16) & 0xff); + DSI_PUSH(0, (pix >> 8) & 0xff); + DSI_PUSH(0, (pix >> 0) & 0xff); + + current_x++; + if (current_x == x+w) { + current_x = x; + data += horiz_inc; + } + } + + DSI_FLUSH(0); + } + + end_measuring("L4"); + + enable_clocks(0); + + return 0; +} + +#if 0 +static void dsi_clear_screen_l4(struct omap_display *display, + int x, int y, int w, int h) +{ + int first = 1; + int fifo_stalls = 0; + int max_dsi_packet_size; + int max_data_per_packet; + int max_pixels_per_packet; + int pixels_left; + int bytespp = 3; + int pixnum; + + debug_irq = 0; + + DSSDBG("dsi_clear_screen_l4 (%d,%d %dx%d)\n", + x, y, w, h); + + if (display->ctrl->bpp != 24) + return -EINVAL; + + /* We need header(4) + DCSCMD(1) + pixels(numpix*bytespp) + * bytes in fifo */ + + /* When using CPU, max long packet size is TX buffer size */ + max_dsi_packet_size = dsi.vc[0].fifo_size * 32 * 4; + + max_data_per_packet = max_dsi_packet_size - 4 - 1; + + max_pixels_per_packet = max_data_per_packet / bytespp; + + enable_clocks(1); + + display->ctrl->setup_update(display, x, y, w, h); + + pixels_left = w * h; + + dsi.update_region.x = x; + dsi.update_region.y = y; + dsi.update_region.w = w; + dsi.update_region.h = h; + dsi.update_region.bytespp = bytespp; + + start_measuring(); + + pixnum = 0; + + while (pixels_left > 0) { + /* 0x2c = write_memory_start */ + /* 0x3c = write_memory_continue */ + u8 dcs_cmd = first ? 0x2c : 0x3c; + int pixels; + DSI_DECL_VARS; + first = 0; + + /* TX_FIFO_NOT_EMPTY */ + while (FLD_GET(dsi_read_reg(DSI_VC_CTRL(0)), 5, 5)) { + fifo_stalls++; + if (fifo_stalls > 0xfffff) { + DSSERR("fifo stalls overflow\n"); + dsi_if_enable(0); + enable_clocks(0); + return; + } + } + + pixels = min(max_pixels_per_packet, pixels_left); + + pixels_left -= pixels; + + dsi_vc_write_long_header(0, DSI_DT_DCS_LONG_WRITE, + 1 + pixels * bytespp, 0); + + DSI_PUSH(0, dcs_cmd); + + while (pixels-- > 0) { + u32 pix; + + pix = 0x000000; + + DSI_PUSH(0, (pix >> 16) & 0xff); + DSI_PUSH(0, (pix >> 8) & 0xff); + DSI_PUSH(0, (pix >> 0) & 0xff); + } + + DSI_FLUSH(0); + } + + enable_clocks(0); + + end_measuring("L4 CLEAR"); +} +#endif + +static int dsi_wait_for_framedone(void) +{ + unsigned long flags; + + spin_lock_irqsave(&dsi.update_lock, flags); + if (dsi.update_ongoing) { + long wait = msecs_to_jiffies(1000); + dsi.update_syncers++; + spin_unlock_irqrestore(&dsi.update_lock, flags); + wait = wait_for_completion_timeout(&dsi.update_completion, + wait); + if (wait == 0) { + DSSERR("timeout waiting sync\n"); + return -ETIME; + } + } else { + spin_unlock_irqrestore(&dsi.update_lock, flags); + } + + return 0; +} + +static void dsi_setup_update_dispc(struct omap_display *display, + int x, int y, int w, int h) +{ + int bytespp = 3; + + DSSDBG("dsi_setup_update_dispc(%d,%d %dx%d)\n", + x, y, w, h); + + dsi.update_region.display = display; + dsi.update_region.x = x; + dsi.update_region.y = y; + dsi.update_region.w = w; + dsi.update_region.h = h; + dsi.update_region.bytespp = bytespp; + + enable_clocks(1); + + dispc_setup_partial_planes(display, &x, &y, &w, &h); + + dispc_set_lcd_size(w, h); + + enable_clocks(0); +} + +static void dsi_update_screen_dispc(struct omap_display *display) +{ + int bytespp = 3; + int total_len; + int line_packet_len; + int x, y, w, h; + u32 l; + + x = dsi.update_region.x; + y = dsi.update_region.y; + w = dsi.update_region.w; + h = dsi.update_region.h; + + DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n", + x, y, w, h); + + enable_clocks(1); + + /* TODO: one packet could be longer, I think? Max is the line buffer */ + line_packet_len = w * bytespp + 1; /* 1 byte for DCS cmd */ + total_len = line_packet_len * h; + + display->ctrl->setup_update(display, x, y, w, h); + + if (0) + dsi_vc_print_status(1); + + start_measuring(); + + l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */ + dsi_write_reg(DSI_VC_TE(1), l); + + dsi_vc_write_long_header(1, DSI_DT_DCS_LONG_WRITE, line_packet_len, 0); + + if (dsi.use_te) + l = FLD_MOD(l, 1, 30, 30); /* TE_EN */ + else + l = FLD_MOD(l, 1, 31, 31); /* TE_START */ + dsi_write_reg(DSI_VC_TE(1), l); + + dispc_enable_lcd_out(1); + + if (dsi.use_te) + dsi_vc_send_bta(1); +} + +static void framedone_callback(void *data, u32 mask) +{ + if (dsi.framedone_scheduled) { + DSSERR("Framedone already scheduled. Bogus FRAMEDONE IRQ?\n"); + return; + } + + dsi.framedone_scheduled = 1; + + /* We get FRAMEDONE when DISPC has finished sending pixels and turns + * itself off. However, DSI still has the pixels in its buffers, and + * is sending the data. Thus we have to wait until we can do a new + * transfer or turn the clocks off. We do that in a separate work + * func. */ + schedule_work(&dsi.framedone_work); +} + +static void framedone_worker(struct work_struct *work) +{ + unsigned long flags; + u32 l; + unsigned long tmo; + int i = 0; + + l = REG_GET(DSI_VC_TE(1), 23, 0); /* TE_SIZE */ + + /* There shouldn't be much stuff in DSI buffers, if any, so we'll + * just busyloop */ + if (l > 0) { + tmo = jiffies + msecs_to_jiffies(50); + while (REG_GET(DSI_VC_TE(1), 23, 0) > 0) { /* TE_SIZE */ + i++; + if (time_after(jiffies, tmo)) { + DSSERR("timeout waiting TE_SIZE to zero\n"); + break; + } + cpu_relax(); + } + } + + if (REG_GET(DSI_VC_TE(1), 30, 30)) + DSSERR("TE_EN not zero\n"); + + if (REG_GET(DSI_VC_TE(1), 31, 31)) + DSSERR("TE_START not zero\n"); + + spin_lock_irqsave(&dsi.update_lock, flags); + if (dsi.update_ongoing == 0) { + spin_unlock_irqrestore(&dsi.update_lock, flags); + DSSERR("framedone irq without update request\n"); + return; + } + spin_unlock_irqrestore(&dsi.update_lock, flags); + + end_measuring("DISPC"); + + DSSDBG("FRAMEDONE\n"); + +#if 0 + if (l) + DSSWARN("FRAMEDONE irq too early, %d bytes, %d loops\n", l, i); +#else + if (l > 1024*3) + DSSWARN("FRAMEDONE irq too early, %d bytes, %d loops\n", l, i); +#endif + + spin_lock_irqsave(&dsi.update_lock, flags); + dsi.update_ongoing = 0; + while (dsi.update_syncers > 0) { + complete(&dsi.update_completion); + --dsi.update_syncers; + } + spin_unlock_irqrestore(&dsi.update_lock, flags); + +#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC + dispc_fake_vsync_irq(); +#endif + enable_clocks(0); + + dsi.framedone_scheduled = 0; + + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) { + spin_lock_irqsave(&dsi.update_lock, flags); + dsi.update_ongoing = 1; + spin_unlock_irqrestore(&dsi.update_lock, flags);... [truncated message content] |
From: Tomi V. <tom...@no...> - 2008-11-04 16:10:42
|
Signed-off-by: Tomi Valkeinen <tom...@no...> --- arch/arm/plat-omap/fb.c | 9 arch/arm/plat-omap/include/mach/omapfb.h | 7 drivers/video/Kconfig | 1 drivers/video/Makefile | 1 drivers/video/omap/Kconfig | 5 drivers/video/omap2/Kconfig | 29 + drivers/video/omap2/Makefile | 2 drivers/video/omap2/omapfb-ioctl.c | 428 ++++++++++ drivers/video/omap2/omapfb-main.c | 1247 ++++++++++++++++++++++++++++++ drivers/video/omap2/omapfb-sysfs.c | 833 ++++++++++++++++++++ drivers/video/omap2/omapfb.h | 104 +++ 11 files changed, 2663 insertions(+), 3 deletions(-) create mode 100644 drivers/video/omap2/Kconfig create mode 100644 drivers/video/omap2/Makefile create mode 100644 drivers/video/omap2/omapfb-ioctl.c create mode 100644 drivers/video/omap2/omapfb-main.c create mode 100644 drivers/video/omap2/omapfb-sysfs.c create mode 100644 drivers/video/omap2/omapfb.h diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index 3746222..0ba1603 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -36,7 +36,8 @@ #include <mach/sram.h> #include <mach/omapfb.h> -#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) +#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) \ + || defined(CONFIG_FB_OMAP2) || defined(CONFIG_FB_OMAP2_MODULE) static struct omapfb_platform_data omapfb_config; static int config_invalid; @@ -298,14 +299,18 @@ unsigned long omapfb_reserve_sram(unsigned long sram_pstart, return reserved; } +#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) void omapfb_set_ctrl_platform_data(void *data) { omapfb_config.ctrl_platform_data = data; } +#endif static inline int omap_init_fb(void) { +#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) const struct omap_lcd_config *conf; +#endif if (config_invalid) return 0; @@ -313,6 +318,7 @@ static inline int omap_init_fb(void) printk(KERN_ERR "Invalid FB mem configuration entries\n"); return 0; } +#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config); if (conf == NULL) { if (configured_regions) @@ -321,6 +327,7 @@ static inline int omap_init_fb(void) return 0; } omapfb_config.lcd = *conf; +#endif return platform_device_register(&omap_fb_device); } diff --git a/arch/arm/plat-omap/include/mach/omapfb.h b/arch/arm/plat-omap/include/mach/omapfb.h index a3c4408..e69c0b1 100644 --- a/arch/arm/plat-omap/include/mach/omapfb.h +++ b/arch/arm/plat-omap/include/mach/omapfb.h @@ -90,6 +90,13 @@ enum omapfb_color_format { OMAPFB_COLOR_CLUT_1BPP, OMAPFB_COLOR_RGB444, OMAPFB_COLOR_YUY422, + + OMAPFB_COLOR_ARGB16, + OMAPFB_COLOR_RGB24U, /* RGB24, 32-bit container */ + OMAPFB_COLOR_RGB24P, /* RGB24, 24-bit container */ + OMAPFB_COLOR_ARGB32, + OMAPFB_COLOR_RGBA32, + OMAPFB_COLOR_RGBX32, }; struct omapfb_update_window { diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 0f13448..4b45731 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2084,6 +2084,7 @@ config FB_METRONOME and could also have been called by some vendors as PVI-nnnn. source "drivers/video/omap/Kconfig" +source "drivers/video/omap2/Kconfig" source "drivers/video/backlight/Kconfig" source "drivers/video/display/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 248bddc..4d69355 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -120,6 +120,7 @@ obj-$(CONFIG_FB_SM501) += sm501fb.o obj-$(CONFIG_FB_XILINX) += xilinxfb.o obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o obj-$(CONFIG_FB_OMAP) += omap/ +obj-$(CONFIG_OMAP2_DSS) += omap2/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 5ebd591..8b6c675 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -1,6 +1,7 @@ config FB_OMAP tristate "OMAP frame buffer support (EXPERIMENTAL)" - depends on FB && ARCH_OMAP + depends on FB && ARCH_OMAP && (OMAP2_DSS = "n") + select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -76,7 +77,7 @@ config FB_OMAP_BOOTLOADER_INIT config FB_OMAP_CONSISTENT_DMA_SIZE int "Consistent DMA memory size (MB)" - depends on FB_OMAP + depends on FB && ARCH_OMAP range 1 14 default 2 help diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig new file mode 100644 index 0000000..4b72479 --- /dev/null +++ b/drivers/video/omap2/Kconfig @@ -0,0 +1,29 @@ +config FB_OMAP2 + tristate "OMAP2/3 frame buffer support (EXPERIMENTAL)" + depends on FB && OMAP2_DSS + + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + Frame buffer driver for OMAP2/3 based boards. + +config FB_OMAP2_DEBUG + bool "Debug output for OMAP2/3 FB" + depends on FB_OMAP2 + +config FB_OMAP2_FORCE_AUTO_UPDATE + bool "Force main display to automatic update mode" + depends on FB_OMAP2 + help + Forces main display to automatic update mode (if possible), + and also enables tearsync (if possible). By default + displays that support manual update are started in manual + update mode. + +menu "OMAP2/3 Display Device Drivers" + depends on OMAP2_DSS + + +endmenu + diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile new file mode 100644 index 0000000..51c2e00 --- /dev/null +++ b/drivers/video/omap2/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_FB_OMAP2) += omapfb.o +omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o diff --git a/drivers/video/omap2/omapfb-ioctl.c b/drivers/video/omap2/omapfb-ioctl.c new file mode 100644 index 0000000..1ceb6b9 --- /dev/null +++ b/drivers/video/omap2/omapfb-ioctl.c @@ -0,0 +1,428 @@ +/* + * linux/drivers/video/omap2/omapfb-ioctl.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tom...@no...> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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/fb.h> +#include <linux/device.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> + +#include <mach/display.h> +#include <mach/omapfb.h> + +#include "omapfb.h" + +static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + struct omap_overlay *ovl; + int r = 0; + + DBG("omapfb_setup_plane\n"); + + if (ofbi->num_overlays != 1) { + r = -EINVAL; + goto out; + } + + ovl = ofbi->overlays[0]; + + omapfb_lock(fbdev); + + if (display) { + if (pi->pos_x + pi->out_width > display->x_res || + pi->pos_y + pi->out_height > display->y_res) { + r = -EINVAL; + goto out; + } + } + + if (pi->enabled && !ofbi->region.size) { + /* + * This plane's memory was freed, can't enable it + * until it's reallocated. + */ + r = -EINVAL; + goto out; + } + + if (!ovl) { + r = -EINVAL; + goto out; + } + + r = omapfb_setup_overlay(fbi, ovl, pi->pos_x, pi->pos_y, + pi->out_width, pi->out_height); + if (r) + goto out; + + ovl->enable(ovl, pi->enabled); + + if (ovl->manager) + ovl->manager->apply(ovl->manager); + + if (display) { + if (display->sync) + display->sync(display); + + if (display->update) + display->update(display, 0, 0, + display->x_res, display->y_res); + } + +out: + omapfb_unlock(fbdev); + if (r) + dev_err(fbdev->dev, "setup_plane failed\n"); + return r; +} + +static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + + omapfb_lock(fbdev); + + if (ofbi->num_overlays != 1) { + memset(pi, 0, sizeof(*pi)); + } else { + struct omap_overlay_info *ovli; + struct omap_overlay *ovl; + + ovl = ofbi->overlays[0]; + ovli = &ovl->info; + + pi->pos_x = ovli->pos_x; + pi->pos_y = ovli->pos_y; + pi->enabled = ovli->enabled; + pi->channel_out = 0; /* xxx */ + pi->mirror = 0; + pi->out_width = ovli->out_width; + pi->out_height = ovli->out_height; + } + + omapfb_unlock(fbdev); + + return 0; +} + +static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omapfb_mem_region *rg; + int ret = -EINVAL; + + rg = &ofbi->region; + + omapfb_lock(fbdev); + if (mi->size > rg->size) { + ret = -ENOMEM; + goto out; + } + + if (mi->type != rg->type) + goto out; + + ret = 0; +out: + omapfb_unlock(fbdev); + + return ret; +} + +static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omapfb_mem_region *rg; + + rg = &ofbi->region; + memset(mi, 0, sizeof(*mi)); + + omapfb_lock(fbdev); + mi->size = rg->size; + mi->type = rg->type; + omapfb_unlock(fbdev); + + return 0; +} + +static int omapfb_update_window(struct fb_info *fbi, + u32 x, u32 y, u32 w, u32 h) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + + if (!display) + return 0; + + if (w == 0 || h == 0) + return 0; + + if (x + w > display->x_res || y + h > display->y_res) + return -EINVAL; + + omapfb_lock(fbdev); + display->update(display, x, y, w, h); + omapfb_unlock(fbdev); + + return 0; +} + +static int omapfb_set_update_mode(struct fb_info *fbi, + enum omapfb_update_mode mode) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + enum omap_dss_update_mode um; + int r; + + if (!display || !display->set_update_mode) + return -EINVAL; + + switch (mode) { + case OMAPFB_UPDATE_DISABLED: + um = OMAP_DSS_UPDATE_DISABLED; + break; + + case OMAPFB_AUTO_UPDATE: + um = OMAP_DSS_UPDATE_AUTO; + break; + + case OMAPFB_MANUAL_UPDATE: + um = OMAP_DSS_UPDATE_MANUAL; + break; + + default: + return -EINVAL; + } + + omapfb_lock(fbdev); + r = display->set_update_mode(display, um); + omapfb_unlock(fbdev); + + return r; +} + +static int omapfb_get_update_mode(struct fb_info *fbi, + enum omapfb_update_mode *mode) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + enum omap_dss_update_mode m; + + if (!display || !display->get_update_mode) + return -EINVAL; + + omapfb_lock(fbdev); + m = display->get_update_mode(display); + omapfb_unlock(fbdev); + + switch (m) { + case OMAP_DSS_UPDATE_DISABLED: + *mode = OMAPFB_UPDATE_DISABLED; + break; + case OMAP_DSS_UPDATE_AUTO: + *mode = OMAPFB_AUTO_UPDATE; + break; + case OMAP_DSS_UPDATE_MANUAL: + *mode = OMAPFB_MANUAL_UPDATE; + break; + default: + BUG(); + } + + return 0; +} + +int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + + union { + struct omapfb_update_window_old uwnd_o; + struct omapfb_update_window uwnd; + struct omapfb_plane_info plane_info; + struct omapfb_caps caps; + struct omapfb_mem_info mem_info; + enum omapfb_update_mode update_mode; + int test_num; + } p; + + int r = 0; + + DBG("ioctl %x (%d)\n", cmd, cmd & 0xff); + + switch (cmd) { + case OMAPFB_SYNC_GFX: + if (!display || !display->sync) { + r = -EINVAL; + break; + } + + omapfb_lock(fbdev); + r = display->sync(display); + omapfb_unlock(fbdev); + break; + + case OMAPFB_UPDATE_WINDOW_OLD: + if (!display || !display->update) { + r = -EINVAL; + break; + } + + if (copy_from_user(&p.uwnd_o, + (void __user *)arg, + sizeof(p.uwnd_o))) { + r = -EFAULT; + break; + } + + r = omapfb_update_window(fbi, p.uwnd_o.x, p.uwnd_o.y, + p.uwnd_o.width, p.uwnd_o.height); + break; + + case OMAPFB_UPDATE_WINDOW: + if (!display || !display->update) { + r = -EINVAL; + break; + } + + if (copy_from_user(&p.uwnd, (void __user *)arg, + sizeof(p.uwnd))) { + r = -EFAULT; + break; + } + + r = omapfb_update_window(fbi, p.uwnd.x, p.uwnd.y, + p.uwnd.width, p.uwnd.height); + break; + + case OMAPFB_SETUP_PLANE: + if (copy_from_user(&p.plane_info, (void __user *)arg, + sizeof(p.plane_info))) + r = -EFAULT; + else + r = omapfb_setup_plane(fbi, &p.plane_info); + break; + + case OMAPFB_QUERY_PLANE: + r = omapfb_query_plane(fbi, &p.plane_info); + if (r < 0) + break; + if (copy_to_user((void __user *)arg, &p.plane_info, + sizeof(p.plane_info))) + r = -EFAULT; + break; + + case OMAPFB_SETUP_MEM: + if (copy_from_user(&p.mem_info, (void __user *)arg, + sizeof(p.mem_info))) + r = -EFAULT; + else + r = omapfb_setup_mem(fbi, &p.mem_info); + break; + + case OMAPFB_QUERY_MEM: + r = omapfb_query_mem(fbi, &p.mem_info); + if (r < 0) + break; + if (copy_to_user((void __user *)arg, &p.mem_info, + sizeof(p.mem_info))) + r = -EFAULT; + break; + + case OMAPFB_GET_CAPS: + if (!display) { + r = -EINVAL; + break; + } + + p.caps.ctrl = display->caps; + + if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps))) + r = -EFAULT; + break; + + case OMAPFB_SET_UPDATE_MODE: + if (get_user(p.update_mode, (int __user *)arg)) + r = -EFAULT; + else + r = omapfb_set_update_mode(fbi, p.update_mode); + break; + + case OMAPFB_GET_UPDATE_MODE: + r = omapfb_get_update_mode(fbi, &p.update_mode); + if (r) + break; + if (put_user(p.update_mode, + (enum omapfb_update_mode __user *)arg)) + r = -EFAULT; + break; + + /* LCD and CTRL tests do the same thing for backward + * compatibility */ + case OMAPFB_LCD_TEST: + if (get_user(p.test_num, (int __user *)arg)) { + r = -EFAULT; + break; + } + if (!display || !display->run_test) { + r = -EINVAL; + break; + } + + r = display->run_test(display, p.test_num); + + break; + + case OMAPFB_CTRL_TEST: + if (get_user(p.test_num, (int __user *)arg)) { + r = -EFAULT; + break; + } + if (!display || !display->run_test) { + r = -EINVAL; + break; + } + + r = display->run_test(display, p.test_num); + + break; + + default: + DBG("ioctl unhandled\n"); + r = -EINVAL; + } + + return r; +} + + diff --git a/drivers/video/omap2/omapfb-main.c b/drivers/video/omap2/omapfb-main.c new file mode 100644 index 0000000..7ef7080 --- /dev/null +++ b/drivers/video/omap2/omapfb-main.c @@ -0,0 +1,1247 @@ +/* + * linux/drivers/video/omap2/omapfb-main.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tom...@no...> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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/delay.h> +#include <linux/fb.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/device.h> +#include <linux/platform_device.h> + +#include <mach/display.h> +#include <mach/omapfb.h> + +#include "omapfb.h" + +#define MODULE_NAME "omapfb" + +#ifdef DEBUG +static void fill_fb(void *addr, struct fb_info *fbi) +{ + struct fb_var_screeninfo *var = &fbi->var; + + const short w = var->xres_virtual; + const short h = var->yres_virtual; + + int y, x; + u8 *p = addr; + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + if (var->bits_per_pixel == 16) { + u16 *pw = (u16 *)p; + + if (x == 20 || x == w - 20 || + y == 20 || y == h - 20) + *pw = 0xffff; + else if (x == y || w - x == h - y) + *pw = ((1<<5)-1)<<11; + else if (w - x == y || x == h - y) + *pw = ((1<<6)-1)<<5; + else { + int t = x / (w/3); + if (t == 0) + *pw = y % 32; + else if (t == 1) + *pw = (y % 64) << 5; + else if (t == 2) + *pw = (y % 32) << 11; + } + } else if (var->bits_per_pixel == 24) { + u8 *pb = (u8 *)p; + + int r = 0, g = 0, b = 0; + + if (x == 20 || x == w - 20 || + y == 20 || y == h - 20) + r = g = b = 0xff; + else if (x == y || w - x == h - y) + r = 0xff; + else if (w - x == y || x == h - y) + g = 0xff; + else { + int q = x / (w / 3); + u8 base = 255 - (y % 256); + if (q == 0) + r = base; + else if (q == 1) + g = base; + else if (q == 2) + b = base; + } + + pb[0] = b; + pb[1] = g; + pb[2] = r; + + } else if (var->bits_per_pixel == 32) { + u32 *pd = (u32 *)p; + + if (x == 20 || x == w - 20 || + y == 20 || y == h - 20) + *pd = 0xffffff; + else if (x == y || w - x == h - y) + *pd = 0xff0000; + else if (w - x == y || x == h - y) + *pd = 0x00ff00; + else { + u8 base = 255 - (y % 256); + *pd = base << ((x / (w/3)) << 3); + } + } + + p += var->bits_per_pixel >> 3; + } + } +} +#endif + +static enum omap_color_mode fb_mode_to_dss_mode(struct fb_var_screeninfo *var) +{ + switch (var->nonstd) { + case 0: + break; + case OMAPFB_COLOR_YUV422: + return OMAP_DSS_COLOR_YUV2; + + case OMAPFB_COLOR_YUY422: + return OMAP_DSS_COLOR_UYVY; + + case OMAPFB_COLOR_ARGB16: + return OMAP_DSS_COLOR_ARGB16; + + case OMAPFB_COLOR_ARGB32: + return OMAP_DSS_COLOR_ARGB32; + + case OMAPFB_COLOR_RGBA32: + return OMAP_DSS_COLOR_RGBA32; + + case OMAPFB_COLOR_RGBX32: + return OMAP_DSS_COLOR_RGBX32; + + default: + return -EINVAL; + } + + switch (var->bits_per_pixel) { + case 1: + return OMAP_DSS_COLOR_CLUT1; + case 2: + return OMAP_DSS_COLOR_CLUT2; + case 4: + return OMAP_DSS_COLOR_CLUT4; + case 8: + return OMAP_DSS_COLOR_CLUT8; + case 12: + return OMAP_DSS_COLOR_RGB12U; + case 16: + return OMAP_DSS_COLOR_RGB16; + case 24: + return OMAP_DSS_COLOR_RGB24P; + case 32: + return OMAP_DSS_COLOR_RGB24U; + default: + return -EINVAL; + } + + return -EINVAL; +} + +static void set_fb_fix(struct fb_info *fbi) +{ + struct fb_fix_screeninfo *fix = &fbi->fix; + struct fb_var_screeninfo *var = &fbi->var; + struct omapfb_mem_region *rg = &FB2OFB(fbi)->region; + + DBG("set_fb_fix\n"); + + /* used by open/write in fbmem.c */ + fbi->screen_base = (char __iomem *)rg->vaddr; + + /* used by mmap in fbmem.c */ + fix->smem_start = rg->paddr; + fix->smem_len = rg->size; + + fix->type = FB_TYPE_PACKED_PIXELS; + + if (var->nonstd) + fix->visual = FB_VISUAL_PSEUDOCOLOR; + else { + switch (var->bits_per_pixel) { + case 32: + case 24: + case 16: + case 12: + fix->visual = FB_VISUAL_TRUECOLOR; + /* 12bpp is stored in 16 bits */ + break; + case 1: + case 2: + case 4: + case 8: + fix->visual = FB_VISUAL_PSEUDOCOLOR; + break; + } + } + + fix->accel = FB_ACCEL_NONE; + fix->line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; +} + +/* check new var and possibly modify it to be ok */ +static int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omap_display *display = fb2display(fbi); + unsigned long max_frame_size; + unsigned long line_size; + int xres_min, xres_max; + int yres_min, yres_max; + enum omap_color_mode mode = 0; + struct omap_overlay *ovl; + + DBG("check_fb_var %d\n", ofbi->id); + + if (ofbi->num_overlays == 0) { + dev_err(ofbi->fbdev->dev, "no overlays, aborting\n"); + return -EINVAL; + } + + /* XXX: uses the first overlay */ + ovl = ofbi->overlays[0]; + + /* if we are using non standard mode, fix the bpp first */ + switch (var->nonstd) { + case 0: + break; + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUY422: + case OMAPFB_COLOR_ARGB16: + var->bits_per_pixel = 16; + break; + case OMAPFB_COLOR_ARGB32: + case OMAPFB_COLOR_RGBA32: + case OMAPFB_COLOR_RGBX32: + var->bits_per_pixel = 32; + break; + default: + DBG("invalid nonstd mode\n"); + return -EINVAL; + } + + mode = fb_mode_to_dss_mode(var); + if (mode < 0) { + DBG("cannot convert var to omap dss mode\n"); + return -EINVAL; + } + + if ((ovl->supported_modes & mode) == 0) { + DBG("invalid mode\n"); + return -EINVAL; + } + + xres_min = OMAPFB_PLANE_XRES_MIN; + xres_max = (display ? display->x_res : 2048) - ovl->info.pos_x; + yres_min = OMAPFB_PLANE_YRES_MIN; + yres_max = (display ? display->y_res : 2048) - ovl->info.pos_y; + + if (var->xres < xres_min) + var->xres = xres_min; + if (var->yres < yres_min) + var->yres = yres_min; + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + max_frame_size = ofbi->region.size; + line_size = (var->xres_virtual * var->bits_per_pixel) >> 3; + + if (line_size * var->yres_virtual > max_frame_size) { + /* Try to keep yres_virtual first */ + line_size = max_frame_size / var->yres_virtual; + var->xres_virtual = line_size * 8 / var->bits_per_pixel; + if (var->xres_virtual < var->xres) { + /* Still doesn't fit. Shrink yres_virtual too */ + var->xres_virtual = var->xres; + line_size = var->xres * var->bits_per_pixel / 8; + var->yres_virtual = max_frame_size / line_size; + } + /* Recheck this, as the virtual size changed. */ + if (var->xres_virtual < var->xres) + var->xres = var->xres_virtual; + if (var->yres_virtual < var->yres) + var->yres = var->yres_virtual; + if (var->xres < xres_min || var->yres < yres_min) { + DBG("Cannot fit FB to memory\n"); + return -EINVAL; + } + } + if (var->xres + var->xoffset > var->xres_virtual) + var->xoffset = var->xres_virtual - var->xres; + if (var->yres + var->yoffset > var->yres_virtual) + var->yoffset = var->yres_virtual - var->yres; + + if (var->bits_per_pixel == 16) { + var->red.offset = 11; var->red.length = 5; + var->red.msb_right = 0; + var->green.offset = 5; var->green.length = 6; + var->green.msb_right = 0; + var->blue.offset = 0; var->blue.length = 5; + var->blue.msb_right = 0; + } else if (var->bits_per_pixel == 24) { + var->red.offset = 16; var->red.length = 8; + var->red.msb_right = 0; + var->green.offset = 8; var->green.length = 8; + var->green.msb_right = 0; + var->blue.offset = 0; var->blue.length = 8; + var->blue.msb_right = 0; + var->transp.offset = 0; var->transp.length = 0; + } else if (var->bits_per_pixel == 32) { + var->red.offset = 16; var->red.length = 8; + var->red.msb_right = 0; + var->green.offset = 8; var->green.length = 8; + var->green.msb_right = 0; + var->blue.offset = 0; var->blue.length = 8; + var->blue.msb_right = 0; + var->transp.offset = 0; var->transp.length = 0; + } else { + DBG("failed to setup fb color mask\n"); + return -EINVAL; + } + + DBG("xres = %d, yres = %d, vxres = %d, vyres = %d\n", + var->xres, var->yres, + var->xres_virtual, var->yres_virtual); + + var->height = -1; + var->width = -1; + var->grayscale = 0; + + if (display && display->check_timings) { + struct omap_video_timings timings; + timings.pixel_clock = PICOS2KHZ(var->pixclock); + timings.hfp = var->left_margin; + timings.hbp = var->right_margin; + timings.vfp = var->upper_margin; + timings.vbp = var->lower_margin; + timings.hsw = var->hsync_len; + timings.vsw = var->vsync_len; + + if (display->check_timings(display, &timings)) { + DBG("illegal video timings\n"); + return -EINVAL; + } + + /* pixclock in ps, the rest in pixclock */ + var->pixclock = KHZ2PICOS(timings.pixel_clock); + var->left_margin = timings.hfp; + var->right_margin = timings.hbp; + var->upper_margin = timings.vfp; + var->lower_margin = timings.vbp; + var->hsync_len = timings.hsw; + var->vsync_len = timings.vsw; + } + + /* TODO: get these from panel->config */ + var->vmode = FB_VMODE_NONINTERLACED; + var->sync = 0; + + return 0; +} + +/* + * --------------------------------------------------------------------------- + * fbdev framework callbacks + * --------------------------------------------------------------------------- + */ +static int omapfb_open(struct fb_info *fbi, int user) +{ + return 0; +} + +static int omapfb_release(struct fb_info *fbi, int user) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + + DBG("Closing fb with plane index %d\n", ofbi->id); + + omapfb_lock(fbdev); +#if 1 + if (display) { + /* XXX Is this really needed ? */ + if (display->sync) + display->sync(display); + + if (display->update) + display->update(display, + 0, 0, + display->x_res, display->y_res); + } +#endif + + if (display && display->sync) + display->sync(display); + + omapfb_unlock(fbdev); + + return 0; +} + +/* setup overlay according to the fb */ +int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, + int posx, int posy, int outw, int outh) +{ + int r = 0; + struct omapfb_info *ofbi = FB2OFB(fbi); + struct fb_var_screeninfo *var = &fbi->var; + enum omap_color_mode mode = 0; + int offset; + u32 data_start_p; + void *data_start_v; + + DBG("setup_overlay %d\n", ofbi->id); + + if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 && + (outw != var->xres || outh != var->yres)) { + r = -EINVAL; + goto err; + } + + offset = ((var->yoffset * var->xres_virtual + + var->xoffset) * var->bits_per_pixel) >> 3; + + data_start_p = ofbi->region.paddr + offset; + data_start_v = ofbi->region.vaddr + offset; + + mode = fb_mode_to_dss_mode(var); + + if (mode == -EINVAL) { + r = -EINVAL; + goto err; + } + + r = ovl->setup_input(ovl, + data_start_p, data_start_v, + var->xres_virtual, + var->xres, var->yres, + mode); + + if (r) + goto err; + + r = ovl->setup_output(ovl, + posx, posy, + outw, outh); + + if (r) + goto err; + + return 0; + +err: + DBG("setup_overlay failed\n"); + return r; +} + +/* apply var to the overlay */ +int omapfb_apply_changes(struct fb_info *fbi, int init) +{ + int r = 0; + struct omapfb_info *ofbi = FB2OFB(fbi); + struct fb_var_screeninfo *var = &fbi->var; + /*struct omap_display *display = fb2display(fbi);*/ + struct omap_overlay *ovl; + int posx, posy; + int outw, outh; + int i; + + for (i = 0; i < ofbi->num_overlays; i++) { + ovl = ofbi->overlays[i]; + + DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id); + + if (init || (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { + outw = var->xres; + outh = var->yres; + } else { + outw = ovl->info.out_width; + outh = ovl->info.out_height; + } + + if (init) { + posx = 0; + posy = 0; + } else { + posx = ovl->info.pos_x; + posy = ovl->info.pos_y; + } + + r = omapfb_setup_overlay(fbi, ovl, posx, posy, outw, outh); + if (r) + goto err; + + /* disabled for now. if the display has changed, var + * still contains the old timings. */ +#if 0 + if (display && display->set_timings) { + struct omap_video_timings timings; + timings.pixel_clock = PICOS2KHZ(var->pixclock); + timings.hfp = var->left_margin; + timings.hbp = var->right_margin; + timings.vfp = var->upper_margin; + timings.vbp = var->lower_margin; + timings.hsw = var->hsync_len; + timings.vsw = var->vsync_len; + + display->set_timings(display, &timings); + } +#endif + if (!init && ovl->manager) + ovl->manager->apply(ovl->manager); + } + return 0; +err: + DBG("apply_changes failed\n"); + return r; +} + +/* checks var and eventually tweaks it to something supported, + * DO NOT MODIFY PAR */ +static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) +{ + int r; + + DBG("check_var(%d)\n", FB2OFB(fbi)->id); + + r = check_fb_var(fbi, var); + + return r; +} + +/* set the video mode according to info->var */ +static int omapfb_set_par(struct fb_info *fbi) +{ + int r; + + DBG("set_par(%d)\n", FB2OFB(fbi)->id); + + set_fb_fix(fbi); + r = omapfb_apply_changes(fbi, 0); + + return r; +} + +static void omapfb_rotate(struct fb_info *fbi, int rotate) +{ + DBG("rotate(%d)\n", FB2OFB(fbi)->id); + return; +} + +static int omapfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *fbi) +{ + DBG("pan_display(%d)\n", FB2OFB(fbi)->id); + return 0; +} + +static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omapfb_mem_region *rg = &ofbi->region; + + return dma_mmap_writecombine(fbdev->dev, vma, + rg->vaddr, + rg->paddr, + rg->size); +} + +/* Store a single color palette entry into a pseudo palette or the hardware + * palette if one is available. For now we support only 16bpp and thus store + * the entry only to the pseudo palette. + */ +static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green, + u_int blue, u_int transp, int update_hw_pal) +{ + /*struct omapfb_info *ofbi = FB2OFB(fbi);*/ + /*struct omapfb2_device *fbdev = ofbi->fbdev;*/ + struct fb_var_screeninfo *var = &fbi->var; + int r = 0; + + enum omapfb_color_format mode = OMAPFB_COLOR_RGB24U; /* XXX */ + + /*switch (plane->color_mode) {*/ + switch (mode) { + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUV420: + case OMAPFB_COLOR_YUY422: + r = -EINVAL; + break; + case OMAPFB_COLOR_CLUT_8BPP: + case OMAPFB_COLOR_CLUT_4BPP: + case OMAPFB_COLOR_CLUT_2BPP: + case OMAPFB_COLOR_CLUT_1BPP: + /* + if (fbdev->ctrl->setcolreg) + r = fbdev->ctrl->setcolreg(regno, red, green, blue, + transp, update_hw_pal); + */ + /* Fallthrough */ + r = -EINVAL; + break; + case OMAPFB_COLOR_RGB565: + case OMAPFB_COLOR_RGB444: + case OMAPFB_COLOR_RGB24P: + case OMAPFB_COLOR_RGB24U: + if (r != 0) + break; + + if (regno < 0) { + r = -EINVAL; + break; + } + + if (regno < 16) { + u16 pal; + pal = ((red >> (16 - var->red.length)) << + var->red.offset) | + ((green >> (16 - var->green.length)) << + var->green.offset) | + (blue >> (16 - var->blue.length)); + ((u32 *)(fbi->pseudo_palette))[regno] = pal; + } + break; + default: + BUG(); + } + return r; +} + +static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + DBG("setcolreg\n"); + + return _setcolreg(info, regno, red, green, blue, transp, 1); +} + +static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info) +{ + int count, index, r; + u16 *red, *green, *blue, *transp; + u16 trans = 0xffff; + + DBG("setcmap\n"); + + red = cmap->red; + green = cmap->green; + blue = cmap->blue; + transp = cmap->transp; + index = cmap->start; + + for (count = 0; count < cmap->len; count++) { + if (transp) + trans = *transp++; + r = _setcolreg(info, index++, *red++, *green++, *blue++, trans, + count == cmap->len - 1); + if (r != 0) + return r; + } + + return 0; +} + +static int omapfb_blank(int blank, struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_display *display = fb2display(fbi); + int do_update = 0; + int r = 0; + + omapfb_lock(fbdev); + + switch (blank) { + case VESA_NO_BLANKING: + if (display->state != OMAP_DSS_DISPLAY_SUSPENDED) { + r = -EINVAL; + goto exit; + } + + if (display->resume) + r = display->resume(display); + + if (r == 0 && display->get_update_mode && + display->get_update_mode(display) == + OMAP_DSS_UPDATE_MANUAL) + do_update = 1; + + break; + + case VESA_POWERDOWN: + if (display->state != OMAP_DSS_DISPLAY_ACTIVE) { + r = -EINVAL; + goto exit; + } + + if (display->suspend) + r = display->suspend(display); + + break; + + default: + r = -EINVAL; + } + +exit: + omapfb_unlock(fbdev); + + if (r == 0 && do_update && display->update) + r = display->update(display, + 0, 0, + display->x_res, display->y_res); + + return r; +} + +static struct fb_ops omapfb_ops = { + .owner = THIS_MODULE, + .fb_open = omapfb_open, + .fb_release = omapfb_release, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_blank = omapfb_blank, + .fb_ioctl = omapfb_ioctl, + .fb_check_var = omapfb_check_var, + .fb_set_par = omapfb_set_par, + .fb_rotate = omapfb_rotate, + .fb_pan_display = omapfb_pan_display, + .fb_mmap = omapfb_mmap, + .fb_setcolreg = omapfb_setcolreg, + .fb_setcmap = omapfb_setcmap, +}; + +static int omapfb_free_fbmem(struct omapfb2_device *fbdev) +{ + int i; + + DBG("free fbmem\n"); + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + struct omapfb_mem_region *rg; + + rg = &ofbi->region; + + if (rg->alloc) { + dma_free_writecombine(fbdev->dev, rg->size, + rg->vaddr, rg->paddr); + } + + rg->vaddr = NULL; + rg->paddr = 0; + rg->alloc = 0; + } + + fbdev->num_fbs = 0; + + return 0; +} + +static int omapfb_allocate_fbmem(struct omapfb2_device *fbdev) +{ + int i; + struct omapfb_mem_desc *plat_mem_desc; + struct omapfb_platform_data *pdata = fbdev->dev->platform_data; + + plat_mem_desc = &pdata->mem_desc; + + DBG("omapfb: setup mem regions, %d regions\n", + plat_mem_desc->region_cnt); + + for (i = 0; i < plat_mem_desc->region_cnt; i++) { + struct omapfb_mem_region *plat_rg; + struct omapfb_mem_region *rg; + struct omapfb_info *ofb_info = FB2OFB(fbdev->fbs[i]); + + plat_rg = &plat_mem_desc->region[i]; + rg = &ofb_info->region; + + memset(rg, 0, sizeof(*rg)); + + DBG("platform region%d phys %08x virt %p size=%lu\n", + i, + plat_rg->paddr, + plat_rg->vaddr, + plat_rg->size); + + if (plat_rg->paddr == 0) { + u32 paddr; + void *vaddr; + + vaddr = dma_alloc_writecombine(fbdev->dev, + plat_rg->size, + &paddr, GFP_KERNEL); + + if (vaddr == NULL) { + dev_err(fbdev->dev, + "failed to allocate framebuffer\n"); + return -ENOMEM; + } + + rg->paddr = paddr; + rg->vaddr = vaddr; + rg->size = plat_rg->size; + rg->alloc = 1; + } else { + dev_err(fbdev->dev, + "Using preallocated fb not supported\n"); + return -EINVAL; + } + } + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofb_info = FB2OFB(fbdev->fbs[i]); + struct omapfb_mem_region *rg; + rg = &ofb_info->region; + + DBG("region%d phys %08x virt %p size=%lu\n", + i, + rg->paddr, + rg->vaddr, + rg->size); + } + + return 0; +} + +/* initialize fb_info, var, fix to something sane based on the display */ +static int fbinfo_init(struct omapfb2_device *fbdev, struct fb_info *fbi) +{ + struct fb_var_screeninfo *var = &fbi->var; + struct fb_fix_screeninfo *fix = &fbi->fix; + struct omap_display *display = fb2display(fbi); + int r = 0; + + if (!display) { + dev_err(fbdev->dev, "cannot fbinfo_init, no display\n"); + return -EINVAL; + } + + fbi->fbops = &omapfb_ops; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->pseudo_palette = fbdev->pseudo_palette; + + strncpy(fix->id, MODULE_NAME, sizeof(fix->id)); + + var->xres = display->x_res; + var->yres = display->y_res; + var->xres_virtual = var->xres; + var->yres_virtual = var->yres; + /* var->rotate = def_rotate; */ + + var->nonstd = 0; + + switch (display->bpp) { + case 16: + var->bits_per_pixel = 16; + break; + case 18: + var->bits_per_pixel = 16; + break; + case 24: + var->bits_per_pixel = 32; + break; + default: + dev_err(fbdev->dev, "illegal display bpp\n"); + return -EINVAL; + } + + if (display->get_timings) { + struct omap_video_timings timings; + display->get_timings(display, &timings); + + /* pixclock in ps, the rest in pixclock */ + var->pixclock = KHZ2PICOS(timings.pixel_clock); + var->left_margin = timings.hfp; + var->right_margin = timings.hbp; + var->upper_margin = timings.vfp; + var->lower_margin = timings.vbp; + var->hsync_len = timings.hsw; + var->vsync_len = timings.vsw; + } else { + var->pixclock = 0; + var->left_margin = 0; + var->right_margin = 0; + var->upper_margin = 0; + var->lower_margin = 0; + var->hsync_len = 0; + var->vsync_len = 0; + } + + r = check_fb_var(fbi, var); + if (r) + goto err; + + set_fb_fix(fbi); + +#ifdef DEBUG + fill_fb(FB2OFB(fbi)->region.vaddr, fbi); +#endif +err: + return r; +} + +static void fbinfo_cleanup(struct omapfb2_device *fbdev, struct fb_info *fbi) +{ + fb_dealloc_cmap(&fbi->cmap); +} + + +static void omapfb_free_resources(struct omapfb2_device *fbdev) +{ + int i; + + DBG("free_resources\n"); + + if (fbdev == NULL) + return; + + for (i = 0; i < fbdev->num_fbs; i++) + unregister_framebuffer(fbdev->fbs[i]); + + /* free the reserved fbmem */ + omapfb_free_fbmem(fbdev); + + for (i = 0; i < fbdev->num_fbs; i++) { + fbinfo_cleanup(fbdev, fbdev->fbs[i]); + framebuffer_release(fbdev->fbs[i]); + } + + + for (i = 0; i < fbdev->num_displays; i++) { + if (fbdev->displays[i]->state != OMAP_DSS_DISPLAY_DISABLED) + fbdev->displays[i]->disable(fbdev->displays[i]); + + omap_dss_put_display(fbdev->displays[i]); + } + + dev_set_drvdata(fbdev->dev, NULL); + kfree(fbdev); +} + +static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) +{ + int r; + int i; + struct omapfb_mem_desc *plat_mem_desc; + struct omapfb_platform_data *pdata = fbdev->dev->platform_data; + + plat_mem_desc = &pdata->mem_desc; + + fbdev->num_fbs = 0; + + DBG("create %d framebuffers\n", plat_mem_desc->region_cnt); + + /* allocate fb_infos */ + for (i = 0; i < plat_mem_desc->region_cnt; i++) { + struct fb_info *fbi; + struct omapfb_info *ofbi; + + fbi = framebuffer_alloc(sizeof(struct omapfb_info), + fbdev->dev); + + if (fbi == NULL) { + dev_err(fbdev->dev, + "unable to allocate memory for plane info\n"); + return -ENOMEM; + } + + fbdev->fbs[i] = fbi; + + ofbi = FB2OFB(fbi); + ofbi->fbdev = fbdev; + /* XXX here we presume we have enough overlays */ + ofbi->overlays[0] = fbdev->overlays[i]; + ofbi->num_overlays = 1; + ofbi->id = i; + fbdev->num_fbs++; + } + + DBG("fb_infos allocated\n"); + + /* allocate fb memories */ + r = omapfb_allocate_fbmem(fbdev); + if (r) { + dev_err(fbdev->dev, "failed to allocate fbmem\n"); + return r; + } + + DBG("fbmems allocated\n"); + + /* setup fb_infos */ + for (i = 0; i < fbdev->num_fbs; i++) { + r = fbinfo_init(fbdev, fbdev->fbs[i]); + if (r) { + dev_err(fbdev->dev, "failed to setup fb_info\n"); + return r; + } + } + + DBG("fb_infos initialized\n"); + + for (i = 0; i < fbdev->num_fbs; i++) { + r = register_framebuffer(fbdev->fbs[i]); + if (r != 0) { + dev_err(fbdev->dev, + "registering framebuffer %d failed\n", i); + return r; + } + } + + DBG("framebuffers registered\n"); + + for (i = 0; i < fbdev->num_fbs; i++) { + r = omapfb_apply_changes(fbdev->fbs[i], 1); + if (r) + dev_err(fbdev->dev, "failed to change mode\n"); + } + + /* Enable the first framebuffer that has overlay that is connected + * to display. Usually this would be the GFX plane. */ + r = 0; + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + int t; + + for (t = 0; t < ofbi->num_overlays; t++) { + struct omap_overlay *ovl = ofbi->overlays[t]; + if (ovl->manager && ovl->manager->display) { + ovl->enable(ovl, 1); + r = 1; + break; + } + } + + if (r) + break; + } + + DBG("create_framebuffers done\n"); + + return 0; +} + +static int omapfb_probe(struct platform_device *pdev) +{ + struct omapfb2_device *fbdev = NULL; + int r = 0; + int i, t; + struct omap_overlay *ovl; + struct omap_display *def_display; + + DBG("omapfb_probe\n"); + + if (pdev->num_resources != 0) { + dev_err(&pdev->dev, "probed for an unknown device\n"); + r = -ENODEV; + goto err0; + } + + if (pdev->dev.platform_data == NULL) { + dev_err(&pdev->dev, "missing platform data\n"); + r = -ENOENT; + goto err0; + } + + fbdev = kzalloc(sizeof(struct omapfb2_device), GFP_KERNEL); + if (fbdev == NULL) { + r = -ENOMEM; + goto err0; + } + + mutex_init(&fbdev->mtx); + + fbdev->dev = &pdev->dev; + platform_set_drvdata(pdev, fbdev); + + fbdev->num_displays = 0; + t = omap_dss_get_num_displays(); + for (i = 0; i < t; i++) { + struct omap_display *display; + display = omap_dss_get_display(i); + if (!display) { + dev_err(&pdev->dev, "can't get display %d\n", i); + r = -EINVAL; + goto cleanup; + } + + fbdev->displays[fbdev->num_displays++] = display; + } + + if (fbdev->num_displays == 0) { + dev_err(&pdev->dev, "no displays\n"); + r = -EINVAL; + goto cleanup; + } + + fbdev->num_overlays = omap_dss_get_num_overlays(); + for (i = 0; i < fbdev->num_overlays; i++) + fbdev->overlays[i] = omap_dss_get_overlay(i); + + fbdev->num_managers = omap_dss_get_num_overlay_managers(); + for (i = 0; i < fbdev->num_managers; i++) + fbdev->managers[i] = omap_dss_get_overlay_manager(i); + + + /* gfx overlay should be the default one. find a display + * connected to that, and use it as default display */ + ovl = omap_dss_get_overlay(0); + if (ovl->manager && ovl->manager->display) { + def_display = ovl->manager->display; + } else { + dev_err(&pdev->dev, "cannot find default display\n"); + r = -EINVAL; + goto cleanup; + } + + r = omapfb_create_framebuffers(fbdev); + if (r) + goto cleanup; + + for (i = 0; i < fbdev->num_managers; i++) { + struct omap_overlay_manager *mgr; + mgr = fbdev->managers[i]; + r = mgr->apply(mgr); + if (r) { + dev_err(fbdev->dev, "failed to apply dispc config\n"); + goto cleanup; + } + } + + DBG("mgr->apply'ed\n"); + + r = def_display->enable(def_display); + if (r) { + dev_err(fbdev->dev, "Failed to enable display '%s'\n", + def_display->name); + goto cleanup; + } + + /* set the update mode */ + if (def_display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { +#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_AUTO); + if (def_display->enable_te) + def_display->enable_te(def_display, 1); +#else + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_MANUAL); + if (def_display->enable_te) + def_display->enable_te(def_display, 0); +#endif + } else { + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_AUTO); + } + + for (i = 0; i < fbdev->num_displays; i++) { + struct omap_display *display = fbdev->displays[i]; + + if (display->update) + display->update(display, + 0, 0, + display->x_res, display->y_res); + } + + DBG("display->updated\n"); + + omapfb_create_sysfs(fbdev); + DBG("sysfs created\n"); + + return 0; + +cleanup: + omapfb_free_resources(fbdev); +err0: + dev_err(&pdev->dev, "failed to setup omapfb\n"); + return r; +} + +static int omapfb_remove(struct platform_device *pdev) +{ + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + + /* FIXME: wait till completion of pending events */ + + omapfb_remove_sysfs(fbdev); + + omapfb_free_resources(fbdev); + + return 0; +} + +static struct platform_driver omapfb_driver = { + .probe = omapfb_probe, + .remove = omapfb_remove, + .driver = { + .name = "omapfb", + .owner = THIS_MODULE, + }, +}; + +static int __init omapfb_init(void) +{ + DBG("omapfb_init\n"); + + if (platform_driver_register(&omapfb_driver)) { + printk(KERN_ERR "failed to register omapfb driver\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit omapfb_exit(void) +{ + DBG("omapfb_exit\n"); + platform_driver_unregister(&omapfb_driver); +} + +/* late_initcall to let panel/ctrl drivers loaded first. + * I guess better option would be a more dynamic approach, + * so that omapfb reacts to new panels when they are loaded */ +late_initcall(omapfb_init); +/*module_init(omapfb_init);*/ +module_exit(omapfb_exit); + +MODULE_AUTHOR("Tomi Valkeinen <tom...@no...>"); +MODULE_DESCRIPTION("OMAP2/3 Framebuffer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/omap2/omapfb-sysfs.c b/drivers/video/omap2/omapfb-sysfs.c new file mode 100644 index 0000000..e01edd1 --- /dev/null +++ b/drivers/video/omap2/omapfb-sysfs.c @@ -0,0 +1,833 @@ +/* + * linux/drivers/video/omap2/omapfb-sysfs.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tom...@no...> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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/fb.h> +#include <linux/sysfs.h> +#include <linux/device.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> + +#include <mach/display.h> +#include <mach/omapfb.h> + +#include "omapfb.h" + +static int omapfb_attach_framebuffer(struct fb_info *fbi, + struct omap_overlay *ovl) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + int i, t; + int r; + + if (ofbi->num_overlays >= OMAPFB_MAX_OVL_PER_FB) { + dev_err(fbdev->dev, "fb has max number of overlays already\n"); + return -EINVAL; + } + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i] == ovl) { + dev_err(fbdev->dev, "fb already attached to overlay\n"); + return -EINVAL; + } + } + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi2 = FB2OFB(fbdev->fbs[i]); + for (t = 0; t < ofbi2->num_overlays; t++) { + if (ofbi2->overlays[t] == ovl) { + dev_err(fbdev->dev, "overlay already in use\n"); + return -EINVAL; + } + } + } + + ofbi->overlays[ofbi->num_overlays++] = ovl; + +/* + if (ovl->manager && ovl->manager->display) + omapfb_adjust_fb(fbi, ovl, 0, 0); +*/ + r = omapfb_apply_changes(fbi, 1); + if (r) + return r; + + if (ovl->manager) + ovl->manager->apply(ovl->manager); + + return 0; +} + +static int omapfb_detach_framebuffer(struct fb_info *fbi, + struct omap_overlay *ovl) +{ + int i; + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i] == ovl) + break; + } + + if (i == ofbi->num_overlays) { + dev_err(fbdev->dev, "cannot detach fb, overlay not attached\n"); + return -EINVAL; + } + + ovl->enable(ovl, 0); + + if (ovl->manager) + ovl->manager->apply(ovl->manager); + + for (i = i + 1; i < ofbi->num_overlays; i++) + ofbi->overlays[i-1] = ofbi->overlays[i]; + + ofbi->num_overlays--; + + return 0; +} + + +static ssize_t show_framebuffers(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + ssize_t l = 0, size = PAGE_SIZE; + int i, t; + + omapfb_lock(fbdev); + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + + l += snprintf(buf + l, size - l, "%d t:", ofbi->id); + + if (ofbi->num_overlays == 0) + l += snprintf(buf + l, size - l, "none"); + + for (t = 0; t < ofbi->num_overlays; t++) { + struct omap_overlay *ovl; + ovl = ofbi->overlays[t]; + + l += snprintf(buf + l, size - l, "%s%s", + t == 0 ? "" : ",", + ovl->name); + } + + l += snprintf(buf + l, size - l, "\n"); + } + + omapfb_unlock(fbdev); + + return l; +} + +static struct omap_overlay *find_overlay_by_name(struct omapfb2_device *fbdev, + char *name) +{ + int i; + + for (i = 0; i < fbdev->num_overlays; i++) + if (strcmp(name, fbdev->overlays[i]->name) == 0) + return fbdev->overlays[i]; + + return NULL; +} + +static struct omap_display *find_display_by_name(struct omapfb2_device *fbdev, + char *name) +{ + int i; + + for (i = 0; i < fbdev->num_displays; i++) + if (strcmp(name, fbdev->displays[i]->name) == 0) + return fbdev->displays[i]; + + return NULL; +} + +static struct omap_overlay_manager *find_manager_by_name( + struct omapfb2_device *fbdev, + char *name) +{ + int i; + + for (i = 0; i < fbdev->num_managers; i++) + if (strcmp(name, fbdev->managers[i]->name) == 0) + return fbdev->managers[i]; + + return NULL; +} + +static int parse_overlays(struct omapfb2_device *fbdev, char *str, + struct omap_overlay *ovls[]) +{ + int num_ovls = 0; + int s, e = 0; + char ovlname[10]; + + while (1) { + struct omap_overlay *ovl; + + s = e; + + while (e < strlen(str) && str[e] != ',') + e++; + + strncpy(ovlname, str + s, e - s); + ovlname[e-s] = 0; + + DBG("searching for '%s'\n", ovlname); + ovl = find_overlay_by_name(fbdev, ovlname); + + if (ovl) { + DBG("found an overlay\n"); + ovls[num_ovls] = ovl; + num_ovls++; + } else { + DBG("unknown overlay %s\n", str); + return 0; + } + + if (e == strlen(str)) + break; + + e++; + } + + return num_ovls; +} + +static ssize_t store_framebuffers(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + int idx; + char fbname[3]; + unsigned long fbnum; + char ovlnames[40]; + int num_ovls = 0; + struct omap_overlay *ovls[OMAPFB_MAX_OVL_PER_FB]; + struct fb_info *fbi; + struct omapfb_info *ofbi; + int r, i; + + idx = 0; + while (idx < count && buf[idx] != ' ') + ++idx; + + if (idx == count) + return -EINVAL; + + if (idx >= sizeof(fbname)) + return -EINVAL; + + strncpy(fbname, buf, idx); + fbname[idx] = 0; + idx++; + + if (strict_strtoul(fbname, 10, &fbnum)) + return -EINVAL; + + r = sscanf(buf + idx, "t:%39s", ovlnames); + + if (r != 1) { + r = -EINVAL; + goto err; + } + + omapfb_lock(fbdev); + + if (fbnum >= fbdev->num_fbs) { + dev_err(dev, "fb not found\n"); + r = -EINVAL; + goto err; + } + + fbi = fbdev->fbs[fbnum]; + ofbi = FB2OFB(fbi); + + if (strcmp(ovlnames, "none") == 0) { + num_ovls = 0; + } else { + num_ovls = parse_overlays(fbdev, ovlnames, ovls); + + if (num_ovls == 0) { + dev_err(dev, "overlays not found\n"); + r = -EINVAL; + goto err; + } + } + + for (i = 0; i < ofbi->num_overlays; i++) { + r = omapfb_detach_framebuffer(fbi, ofbi->overlays[i]); + if (r) { + dev_err(dev, "detach failed\n"); + goto err; + } + } + + if (num_ovls > 0) { + for (i = 0; i < num_ovls; i++) { + r = omapfb_attach_framebuffer(fbi, ovls[i]); + if (r) { + dev_err(dev, "attach failed\n"); + goto err; + } + } + } + + omapfb_unlock(fbdev); + return count; + +err: + omapfb_unlock(fbdev); + return r; +} + +static ssize_t show_overlays(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + ssize_t l = 0, size = PAGE_SIZE; + int i, mgr_num; + + omapfb_lock(fbdev); + + for (i = 0; i < fbdev->num_overlays; i++) { + struct omap_overlay *ovl; + struct omap_overlay_manager *mgr; + + ovl = fbdev->overlays[i]; + mgr = ovl->manager; + + for (mgr_num = 0; mgr_num < fbdev->num_managers; mgr_num++) + if (fbdev->managers[mgr_num] == mgr) + break; + + l += snprintf(buf + l, size - l, + "%s t:%s x:%d y:%d iw:%d ih:%d w: %d h: %d e:%d\n", + ovl->name, + mgr ? mgr->name : "none", + ovl->info.pos_x, + ovl->info.pos_y, + ovl->info.width, + ovl->info.height, + ovl->info.out_width, + ovl->info.out_height, + ovl->info.enabled); + } + + omapfb_unlock(fbdev); + + return l; +} + +static ssize_t store_overlays(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + int idx; + struct omap_overlay *ovl = NULL; + struct omap_overlay_manager *mgr; + int r; + char ovlname[10]; + int posx, posy, outw, outh; + int enabled; + + idx = 0; + while (idx < count && buf[idx] != ' ') + ++idx; + + if (idx == count) + return -EINVAL; + + if (idx >= sizeof(ovlname)) + return -EINVAL; + + strncpy(ovlname, buf, idx); + ovlname[idx] = 0; + idx++; + + omapfb_lock(fbdev); + + ovl = find_overlay_by_name(fbdev, ovlname); + + if (!ovl) { + dev_err(dev, "ovl not found\n"); + r = -EINVAL; + goto err; + } + + DBG("ovl %s found\n", ovl->name); + + mgr = ovl->manager; + + posx = ovl->info.pos_x; + posy = ovl->info.pos_y; + outw = ovl->info.out_width; + outh = ovl->info.out_height; + enabled = ovl->info.enabled; + + while (idx < count) { + char c; + int val; + int len; + char sval[10]; + + r = sscanf(buf + idx, "%c:%d%n", &c, &val, &len); + + if (r != 2) { + val = 0; + + r = sscanf(buf + idx, "%c:%9s%n", &c, sval, &len); + + if (r != 2) { + dev_err(dev, "sscanf failed, aborting\n"); + r = -EINVAL; + goto err; + } + } else { + sval[0] = 0; + } + + switch (c) { + case 't': + if (strcmp(sval, "none") == 0) { + mgr = NULL; + } else { + mgr = find_manager_by_name(fbdev, sval); + + if (mgr == NULL) { + dev_err(dev, "no such manager\n"); + r = -EINVAL; + goto err; + } + + DBG("manager %s found\n", mgr->name); + } + + break; + + case 'x': + posx = val; + break; + + case 'y': + posy = val; + break; + + case 'w': + if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) + outw = val; + break; + + case 'h': + if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) + outh = val; + break; + + case 'e': + enabled = val; + break; + + default: + dev_err(dev, "unknown option %c\n", c); + r = -EINVAL; + goto err; + } + + idx += len + 1; + } + + r = ovl->setup_output(ovl, posx, posy, outw, outh); + + if (r) { + dev_err(dev, "setup overlay failed\n"); + goto err; + } + + if (mgr != ovl->manager) { + /* detach old manager */ + if (ovl->manager) { + r = ovl->unset_manager(ovl); + if (r) { + dev_err(dev, "detach failed\n"); + goto err; + } + } + + if (mgr) { + r = ovl->set_manager(ovl, mgr); + if (r) { + dev_err(dev, "Failed to attach overlay\n"); + goto err; + } + } + } + + r = ovl->enable(ovl, enabled); + + if (r) { + dev_err(dev, "enable overlay failed\n"); + goto err; + } + + if (mgr) { + r = mgr->apply(mgr); + if (r) { + dev_err(dev, "failed to apply dispc config\n"); + goto err; + } + } else { + ovl->enable(ovl, 0); + } + + if (mgr && mgr->display && mgr->display->update) + mgr->display->update(mgr->display, + 0, 0, + mgr->display->x_res, mgr->display->y_res); + + omapfb_unlock(fbdev); + return count; + +err: + omapfb_unlock(fbdev); + return r; +} + +static ssize_t show_managers(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + ssize_t l = 0, size = PAGE_SIZE; + int i; + + omapfb_lock(fbdev); + + for (i = 0; i < fbdev->num_managers; i++) { + struct omap_display *display; + struct omap_overlay_manager *mgr; + + mgr = fbdev->managers[i]; + display = mgr->display; + + l += snprintf(buf + l, size - l, "%s t:%s\n", + mgr->name, display ? display->name : "none"); + } + + omapfb_unlock(fbdev); + + return l; +} + +static ssize_t store_managers(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + int idx; + struct omap_overlay_manager *mgr; + struct omap_display *display; + char mgrname[10]; + char displayname[10]; + int r; + + idx = 0; + while (idx < count && buf[idx] != ' ') + ++idx; + + if (idx == count) + return -EINVAL; + + if (idx >= sizeof(mgrname)) + return -EINVAL; + + strncpy(mgrname, buf, idx); + mgrname[idx] = 0; + idx++; + + omapfb_lock(fbdev); + + mgr = find_manager_by_name(fbdev, mgrname); + + if (!mgr) { + dev_err(dev, "manager not found\n"); + r = -EINVAL; + goto err; + } + + r = sscanf(buf + idx, "t:%9s", displayname); + + if (r != 1) { + r = -EINVAL; + goto err; + } + + if (strcmp(displayname, "none") == 0) { + display = NULL; + } else { + display = find_display_by_name(fbdev, displayname); + + if (!display) { + dev_err(dev, "display not found\n"); + r = -EINVAL; + goto err; + } + } + + if (mgr->display) { + r = mgr->unset_display(mgr); + if (r) { + dev_err(dev, "failed to unset display\n"); + goto err; + } + } + + if (display) { + r = mgr->set_display(mgr, display); + if (r) { + dev_err(dev, "failed to set manager\n"); + goto err; + } + + r = mgr->apply(mgr); + if (r) { + dev_err(dev, "failed to apply dispc config\n"); + goto err; + } + } + + omapfb_unlock(fbdev); + return count; + +err: + omapfb_unlock(fbdev); + return r; +} + +static ssize_t show_displays(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + ssize_t l = 0, size = PAGE_SIZE; + int i; + + omapfb_lock(fbdev); + + for (i = 0; i < fbdev->num_displays; i++) { + struct omap_display *display; + enum omap_dss_update_mode mode = -1; + int te = 0; + + display = fbdev->displays[i]; + + if (display->get_update_mode) + mode = display->get_update_mode(display); + + if (display->get_te) + te = display->get_te(display); + + l += snprintf(buf + l, size - l, + "%s w:%d h:%d e:%d u:%d t:%d\n", + display->name, + display->x_res, + display->y_res, + display->state != OMAP_DSS_DISPLAY_DISABLED, + mode, te); + } + + omapfb_unlock(fbdev); + + return l; +} + +static ssize_t store_displays(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + int idx; + int enable, width, height; + enum omap_dss_update_mode mode; + struct omap_display *display = NULL; + int r; + char displayname[10]; + int te; + + idx = 0; + while (idx < count && buf[idx] != ' ') + ++idx; + + if (idx == count) + return -EINVAL; + + if (idx >= sizeof(displayname)) + return -EINVAL; + + strncpy(displayname, buf, idx); + displayname[idx] = 0; + idx++; + + omapfb_lock(fbdev); + + display = find_display_by_name(fbdev, displayname); + + if (!display) { + dev_err(dev, "display not found\n"); + r = -EINVAL; + goto err; + } + + width = display->x_res; + height = display->y_res; + enable = display->state != OMAP_DSS_DISPLAY_DISABLED; + if (display->get_update_mode) + mode = display->get_update_mode(display); + else + mode = 0; + + if (display->get_te) + te = display->get_te(display); + else + te = 0; + + while (idx < count) { + char c; + int val; + int len; + + r = sscanf(buf + idx, "%c:%d%n", &c, &val, &len); + + if (r != 2) { + dev_err(dev, "sscanf failed, aborting\n"); + r = -EINVAL; + goto err; + } + + switch (c) { + case 'w': + width = val; + break; + + case 'h': + height = val; + break; + + case 'e': + enable = val; + break; + + case 'u': + mode = val; + break; + + case 't': + te = val; + break; + + default: + dev_err(dev, "unknown option %c\n", c); + r = -EINVAL; + goto err; + } + + idx += len + 1; + } + + /* XXX: setmode */ + if (enable != (display->state != OMAP_DSS_DISPLAY_DISABLED)) { + i... [truncated message content] |
From: Tomi V. <tom...@no...> - 2008-11-04 16:10:47
|
For some reason we can't allocate enough mem for 1280x1024x24bpp, even if there should be enough continuous mem. So 1280x1024 mode defaults to 16bpp for now. You also need DSI PLL to generate pix clock for 1280x1024. Signed-off-by: Tomi Valkeinen <tom...@no...> --- drivers/video/omap2/Kconfig | 20 ++++++ drivers/video/omap2/Makefile | 2 + drivers/video/omap2/panel-dvi.c | 121 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 0 deletions(-) create mode 100644 drivers/video/omap2/panel-dvi.c diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig index 4b72479..4584e1b 100644 --- a/drivers/video/omap2/Kconfig +++ b/drivers/video/omap2/Kconfig @@ -24,6 +24,26 @@ config FB_OMAP2_FORCE_AUTO_UPDATE menu "OMAP2/3 Display Device Drivers" depends on OMAP2_DSS +config PANEL_DVI + tristate "DVI Panel" + help + DVI output, for Beagle and OMAP3 SDP + +choice + prompt "Default DVI Mode" + depends on PANEL_DVI + default PANEL_DVI_HIGHRES + +config PANEL_DVI_LOWRES + bool "800 x 600 @ 60" + +config PANEL_DVI_HIGHRES + bool "1024 x 768 @ 60" + +config PANEL_DVI_VERYHIGHRES + bool "1280 x 1024 @ 57" + +endchoice endmenu diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile index 51c2e00..7c75340 100644 --- a/drivers/video/omap2/Makefile +++ b/drivers/video/omap2/Makefile @@ -1,2 +1,4 @@ obj-$(CONFIG_FB_OMAP2) += omapfb.o omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o + +obj-$(CONFIG_PANEL_DVI) += panel-dvi.o diff --git a/drivers/video/omap2/panel-dvi.c b/drivers/video/omap2/panel-dvi.c new file mode 100644 index 0000000..2d053df --- /dev/null +++ b/drivers/video/omap2/panel-dvi.c @@ -0,0 +1,121 @@ +/* + * DVI panel support + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tom...@no...> + * + * 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/delay.h> + +#include <mach/display.h> + +static int dvi_panel_init(struct omap_display *display) +{ + return 0; +} + +static int dvi_panel_enable(struct omap_display *display) +{ + int r = 0; + + if (display->hw_config.panel_enable) + r = display->hw_config.panel_enable(display); + + return r; +} + +static void dvi_panel_disable(struct omap_display *display) +{ + if (display->hw_config.panel_disable) + display->hw_config.panel_disable(display); +} + +static struct omap_panel dvi_panel = { + .owner = THIS_MODULE, + .name = "panel-dvi", + .init = dvi_panel_init, + /*.remove = dvi_cleanup, */ + .enable = dvi_panel_enable, + .disable = dvi_panel_disable, + /*.set_mode = dvi_set_mode, */ + +#if defined(CONFIG_PANEL_DVI_LOWRES) + .timings = { + /* 800 x 600 @ 60 Hz Reduced blanking VESA CVT 0.48M3-R */ + .pixel_clock = 35500, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 4, + .vbp = 11, + }, + + .x_res = 800, + .y_res = 600, + .bpp = 24, +#elif defined(CONFIG_PANEL_DVI_HIGHRES) + .timings = { + /* 1024 x 768 @ 60 Hz Reduced blanking */ + .pixel_clock = 56000, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 4, + .vbp = 15, + }, + + .x_res = 1024, + .y_res = 768, + .bpp = 24, +#elif defined(CONFIG_PANEL_DVI_VERYHIGHRES) + .timings = { + /* 1280 x 1024 @ 57 Hz Reduced blanking */ + .pixel_clock = 86500, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 4, + .vbp = 15, + }, + + .x_res = 1280, + .y_res = 1024, + .bpp = 16, +#else +#error Undefined default mode +#endif + + .config = OMAP_DSS_LCD_TFT, +}; + + +static int __init dvi_panel_drv_init(void) +{ + omap_dss_register_panel(&dvi_panel); + return 0; +} + +static void __exit dvi_panel_drv_exit(void) +{ + omap_dss_unregister_panel(&dvi_panel); +} + +module_init(dvi_panel_drv_init); +module_exit(dvi_panel_drv_exit); +MODULE_LICENSE("GPL"); |
From: Tomi V. <tom...@no...> - 2008-11-04 16:11:02
|
Signed-off-by: Tomi Valkeinen <tom...@no...> --- arch/arm/mach-omap2/board-3430sdp.c | 234 +++++++++++++++++++++++++++++++++-- drivers/video/omap2/Kconfig | 7 + drivers/video/omap2/Makefile | 1 drivers/video/omap2/panel-sdp3430.c | 110 ++++++++++++++++ 4 files changed, 340 insertions(+), 12 deletions(-) create mode 100644 drivers/video/omap2/panel-sdp3430.c diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c index 8773698..b910bc6 100644 --- a/arch/arm/mach-omap2/board-3430sdp.c +++ b/arch/arm/mach-omap2/board-3430sdp.c @@ -40,6 +40,8 @@ #include <mach/keypad.h> #include <mach/dma.h> #include <mach/gpmc.h> +#include <mach/omapfb.h> +#include <mach/display.h> #include <asm/io.h> #include <asm/delay.h> @@ -239,14 +241,224 @@ static struct spi_board_info sdp3430_spi_board_info[] __initdata = { }, }; -static struct platform_device sdp3430_lcd_device = { - .name = "sdp2430_lcd", - .id = -1, +static struct omap_fbmem_config sdp3430_fbmem0_config = { + .size = 1024*768*4, + .start = OMAPFB_MEMTYPE_SDRAM, +}; + +static struct omap_fbmem_config sdp3430_fbmem1_config = { + .size = 640*480*4, + .start = OMAPFB_MEMTYPE_SDRAM, }; +static struct omap_fbmem_config sdp3430_fbmem2_config = { + .size = 640*480*4, + .start = OMAPFB_MEMTYPE_SDRAM, +}; + + +#define SDP2430_LCD_PANEL_BACKLIGHT_GPIO 91 +#define SDP2430_LCD_PANEL_ENABLE_GPIO 154 +#define SDP3430_LCD_PANEL_BACKLIGHT_GPIO 24 +#define SDP3430_LCD_PANEL_ENABLE_GPIO 28 + +#define PM_RECEIVER TWL4030_MODULE_PM_RECEIVER +#define ENABLE_VAUX2_DEDICATED 0x09 +#define ENABLE_VAUX2_DEV_GRP 0x20 +#define ENABLE_VAUX3_DEDICATED 0x03 +#define ENABLE_VAUX3_DEV_GRP 0x20 + +#define ENABLE_VPLL2_DEDICATED 0x05 +#define ENABLE_VPLL2_DEV_GRP 0xE0 +#define TWL4030_VPLL2_DEV_GRP 0x33 +#define TWL4030_VPLL2_DEDICATED 0x36 + +#define t2_out(c, r, v) twl4030_i2c_write_u8(c, r, v) + +static unsigned backlight_gpio; +static unsigned enable_gpio; +static int lcd_enabled; +static int dvi_enabled; + +static void __init display_init(void) +{ + int r; + + enable_gpio = SDP3430_LCD_PANEL_ENABLE_GPIO; + backlight_gpio = SDP3430_LCD_PANEL_BACKLIGHT_GPIO; + + r = gpio_request(enable_gpio, "OMAP SDP LCD Reset GPIO"); + if (r) { + printk(KERN_ERR "failed to get LCD reset GPIO\n"); + goto err0; + } + + r = gpio_request(backlight_gpio, "OMAP SDP LCD Backlight GPIO"); + if (r) { + printk(KERN_ERR "failed to get LCD backlight GPIO\n"); + goto err1; + } + + gpio_direction_output(enable_gpio, 0); + gpio_direction_output(backlight_gpio, 0); + + return; +err1: + gpio_free(enable_gpio); +err0: + return; +} + + +static int panel_enable_lcd(struct omap_display *display) +{ + u8 ded_val, ded_reg; + u8 grp_val, grp_reg; + + if (dvi_enabled) { + printk(KERN_ERR "cannot enable LCD, DVI is enabled\n"); + return -EINVAL; + } + + if (system_rev > OMAP3430_REV_ES1_0) { + t2_out(PM_RECEIVER, ENABLE_VPLL2_DEDICATED, + TWL4030_VPLL2_DEDICATED); + t2_out(PM_RECEIVER, ENABLE_VPLL2_DEV_GRP, + TWL4030_VPLL2_DEV_GRP); + } + + ded_reg = TWL4030_VAUX3_DEDICATED; + ded_val = ENABLE_VAUX3_DEDICATED; + grp_reg = TWL4030_VAUX3_DEV_GRP; + grp_val = ENABLE_VAUX3_DEV_GRP; + + gpio_direction_output(enable_gpio, 1); + gpio_direction_output(backlight_gpio, 1); + + if (0 != t2_out(PM_RECEIVER, ded_val, ded_reg)) + return -EIO; + if (0 != t2_out(PM_RECEIVER, grp_val, grp_reg)) + return -EIO; + + lcd_enabled = 1; + + return 0; +} + +static void panel_disable_lcd(struct omap_display *display) +{ + lcd_enabled = 0; + + gpio_direction_output(enable_gpio, 0); + gpio_direction_output(backlight_gpio, 0); + + if (system_rev > OMAP3430_REV_ES1_0) { + t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEDICATED); + t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEV_GRP); + mdelay(4); + } +} + +static struct omap_display_data sdp_display_data = { + .type = OMAP_DISPLAY_TYPE_DPI, + .name = "lcd", + .panel_name = "panel-sdp3430", + .u.dpi.data_lines = 16, + .panel_enable = panel_enable_lcd, + .panel_disable = panel_disable_lcd, +}; + +static int panel_enable_dvi(struct omap_display *display) +{ + if (lcd_enabled) { + printk(KERN_ERR "cannot enable DVI, LCD is enabled\n"); + return -EINVAL; + } + + if (system_rev > OMAP3430_REV_ES1_0) { + t2_out(PM_RECEIVER, ENABLE_VPLL2_DEDICATED, + TWL4030_VPLL2_DEDICATED); + t2_out(PM_RECEIVER, ENABLE_VPLL2_DEV_GRP, + TWL4030_VPLL2_DEV_GRP); + } + + dvi_enabled = 1; + + return 0; +} + +static void panel_disable_dvi(struct omap_display *display) +{ + dvi_enabled = 0; + + if (system_rev > OMAP3430_REV_ES1_0) { + t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEDICATED); + t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEV_GRP); + mdelay(4); + } +} + + +static struct omap_display_data sdp_display_data_dvi = { + .type = OMAP_DISPLAY_TYPE_DPI, + .name = "dvi", + .panel_name = "panel-dvi", + .u.dpi.data_lines = 24, + .panel_enable = panel_enable_dvi, + .panel_disable = panel_disable_dvi, +}; + +static int panel_enable_tv(struct omap_display *display) +{ +#define ENABLE_VDAC_DEDICATED 0x03 +#define ENABLE_VDAC_DEV_GRP 0x20 + + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + ENABLE_VDAC_DEDICATED, + TWL4030_VDAC_DEDICATED); + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + ENABLE_VDAC_DEV_GRP, TWL4030_VDAC_DEV_GRP); + + return 0; +} + +static void panel_disable_tv(struct omap_display *display) +{ + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x00, + TWL4030_VDAC_DEDICATED); + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x00, + TWL4030_VDAC_DEV_GRP); +} + +static struct omap_display_data sdp_display_data_tv = { + .type = OMAP_DISPLAY_TYPE_VENC, + .name = "tv", + .u.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO, + .panel_enable = panel_enable_tv, + .panel_disable = panel_disable_tv, +}; + +static struct omap_dss_platform_data sdp3430_dss_data = { + .num_displays = 3, + .displays = { + &sdp_display_data, + &sdp_display_data_dvi, + &sdp_display_data_tv, + } +}; + +static struct platform_device sdp3430_dss_device = { + .name = "omap-dss", + .id = -1, + .dev = { + .platform_data = &sdp3430_dss_data, + }, +}; + + static struct platform_device *sdp3430_devices[] __initdata = { &sdp3430_smc91x_device, - &sdp3430_lcd_device, + &sdp3430_dss_device, }; static inline void __init sdp3430_init_smc91x(void) @@ -293,13 +505,11 @@ static struct omap_uart_config sdp3430_uart_config __initdata = { .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)), }; -static struct omap_lcd_config sdp3430_lcd_config __initdata = { - .ctrl_name = "internal", -}; - static struct omap_board_config_kernel sdp3430_config[] __initdata = { { OMAP_TAG_UART, &sdp3430_uart_config }, - { OMAP_TAG_LCD, &sdp3430_lcd_config }, + { OMAP_TAG_FBMEM, &sdp3430_fbmem0_config }, + { OMAP_TAG_FBMEM, &sdp3430_fbmem1_config }, + { OMAP_TAG_FBMEM, &sdp3430_fbmem2_config }, }; static int sdp3430_batt_table[] = { @@ -450,8 +660,6 @@ static void __init omap_3430sdp_init(void) { omap3430_i2c_init(); platform_add_devices(sdp3430_devices, ARRAY_SIZE(sdp3430_devices)); - omap_board_config = sdp3430_config; - omap_board_config_size = ARRAY_SIZE(sdp3430_config); if (system_rev > OMAP3430_REV_ES1_0) ts_gpio = OMAP34XX_TS_GPIO_IRQ_SDPV2; else @@ -466,10 +674,14 @@ static void __init omap_3430sdp_init(void) usb_musb_init(); usb_ehci_init(); hsmmc_init(); + display_init(); } static void __init omap_3430sdp_map_io(void) { + omap_board_config = sdp3430_config; + omap_board_config_size = ARRAY_SIZE(sdp3430_config); + omap2_set_globals_343x(); omap2_map_common_io(); } diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig index 4584e1b..95691ad 100644 --- a/drivers/video/omap2/Kconfig +++ b/drivers/video/omap2/Kconfig @@ -45,5 +45,10 @@ config PANEL_DVI_VERYHIGHRES endchoice -endmenu +config PANEL_SDP3430 + tristate "SDP3430 Panel" + depends on OMAP2_DSS + help + SDP3430 LCD +endmenu diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile index 7c75340..73ab1c0 100644 --- a/drivers/video/omap2/Makefile +++ b/drivers/video/omap2/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_FB_OMAP2) += omapfb.o omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o obj-$(CONFIG_PANEL_DVI) += panel-dvi.o +obj-$(CONFIG_PANEL_SDP3430) += panel-sdp3430.o diff --git a/drivers/video/omap2/panel-sdp3430.c b/drivers/video/omap2/panel-sdp3430.c new file mode 100644 index 0000000..40fe6f2 --- /dev/null +++ b/drivers/video/omap2/panel-sdp3430.c @@ -0,0 +1,110 @@ +/* + * LCD panel support for the TI 3430SDP board + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tom...@no...> + * + * Derived from drivers/video/omap/lcd_2430sdp.c + * + * 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/delay.h> + +#include <mach/display.h> + +static int sdp3430_panel_init(struct omap_display *display) +{ + return 0; +} + +static void sdp3430_panel_cleanup(struct omap_display *display) +{ +} + +static int sdp3430_panel_enable(struct omap_display *display) +{ + int r = 0; + + if (display->hw_config.panel_enable) + r = display->hw_config.panel_enable(display); + + return r; +} + +static void sdp3430_panel_disable(struct omap_display *display) +{ + if (display->hw_config.panel_disable) + display->hw_config.panel_disable(display); +} + +static int sdp3430_panel_suspend(struct omap_display *display) +{ + sdp3430_panel_disable(display); + return 0; +} + +static int sdp3430_panel_resume(struct omap_display *display) +{ + return sdp3430_panel_enable(display); +} + +static struct omap_panel sdp3430_panel = { + .owner = THIS_MODULE, + .name = "panel-sdp3430", + .init = sdp3430_panel_init, + .cleanup = sdp3430_panel_cleanup, + .enable = sdp3430_panel_enable, + .disable = sdp3430_panel_disable, + .suspend = sdp3430_panel_suspend, + .resume = sdp3430_panel_resume, + /*.set_mode = sdp3430_set_mode, */ + + .timings = { + .pixel_clock = 19200, + + .hsw = 4, + .hfp = 4, + .hbp = 40, + + .vsw = 2, + .vfp = 1, + .vbp = 1, + }, + + .acb = 0x28, + + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + + .x_res = 480, + .y_res = 640, + .bpp = 16, +}; + + +static int __init sdp3430_panel_drv_init(void) +{ + omap_dss_register_panel(&sdp3430_panel); + return 0; +} + +static void __exit sdp3430_panel_drv_exit(void) +{ + omap_dss_unregister_panel(&sdp3430_panel); +} + +module_init(sdp3430_panel_drv_init); +module_exit(sdp3430_panel_drv_exit); +MODULE_LICENSE("GPL"); |
From: Jarkko N. <jar...@no...> - 2008-11-05 10:55:42
|
On Tue, 04 Nov 2008 18:10:45 +0200 "ext Tomi Valkeinen" <tom...@no...> wrote: > Signed-off-by: Tomi Valkeinen <tom...@no...> > --- > > arch/arm/mach-omap2/board-3430sdp.c | 234 +++++++++++++++++++++++++++++++++-- > drivers/video/omap2/Kconfig | 7 + > drivers/video/omap2/Makefile | 1 > drivers/video/omap2/panel-sdp3430.c | 110 ++++++++++++++++ > 4 files changed, 340 insertions(+), 12 deletions(-) > create mode 100644 drivers/video/omap2/panel-sdp3430.c > > +static void __init display_init(void) > I would use unique 3430sdp_display_init even the function is static. Cleaner symbol names and cleaner oops dumps. > +{ > + int r; > + > + enable_gpio = SDP3430_LCD_PANEL_ENABLE_GPIO; > + backlight_gpio = SDP3430_LCD_PANEL_BACKLIGHT_GPIO; > + > + r = gpio_request(enable_gpio, "OMAP SDP LCD Reset GPIO"); > + if (r) { > + printk(KERN_ERR "failed to get LCD reset GPIO\n"); > + goto err0; > + } > + > + r = gpio_request(backlight_gpio, "OMAP SDP LCD Backlight GPIO"); > + if (r) { How about labels "LCD reset" and "LCD backlight" (actually bit too long). > +static int panel_enable_lcd(struct omap_display *display) > +static void panel_disable_lcd(struct omap_display *display) > +static int panel_enable_dvi(struct omap_display *display) Would it better to rename these as sdp3430_enable_lcd etc since otherwise looks somewhat confusing to see them in board-3430sdp.c where there is also panel-sdp3430.c? Jarkko |
From: Tomi V. <tom...@no...> - 2008-11-04 16:11:27
|
Signed-off-by: Tomi Valkeinen <tom...@no...> --- arch/arm/mach-omap2/board-omap3beagle.c | 121 ++++++++++++++++++++++++++++--- 1 files changed, 108 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index ce6c7b4..a6fe63d 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -42,6 +42,8 @@ #include <mach/gpmc.h> #include <mach/nand.h> #include <mach/mux.h> +#include <mach/omapfb.h> +#include <mach/display.h> #include "twl4030-generic-scripts.h" @@ -186,15 +188,6 @@ static void __init omap3_beagle_init_irq(void) omap_gpio_init(); } -static struct platform_device omap3_beagle_lcd_device = { - .name = "omap3beagle_lcd", - .id = -1, -}; - -static struct omap_lcd_config omap3_beagle_lcd_config __initdata = { - .ctrl_name = "internal", -}; - static struct gpio_led gpio_leds[] = { { .name = "beagleboard::usr0", @@ -248,13 +241,114 @@ static struct platform_device keys_gpio = { }, }; +/* DSS */ + +static int beagle_enable_dvi(struct omap_display *display) +{ + if (display->hw_config.panel_reset_gpio != -1) + gpio_direction_output(display->hw_config.panel_reset_gpio, 1); + + return 0; +} + +static void beagle_disable_dvi(struct omap_display *display) +{ + if (display->hw_config.panel_reset_gpio != -1) + gpio_direction_output(display->hw_config.panel_reset_gpio, 0); +} + +static struct omap_display_data beagle_display_data_dvi = { + .type = OMAP_DISPLAY_TYPE_DPI, + .name = "dvi", + .panel_name = "panel-dvi", + .u.dpi.data_lines = 24, + .panel_reset_gpio = 170, + .panel_enable = beagle_enable_dvi, + .panel_disable = beagle_disable_dvi, +}; + + +static int panel_enable_tv(struct omap_display *display) +{ +#define ENABLE_VDAC_DEDICATED 0x03 +#define ENABLE_VDAC_DEV_GRP 0x20 + + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + ENABLE_VDAC_DEDICATED, + TWL4030_VDAC_DEDICATED); + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + ENABLE_VDAC_DEV_GRP, TWL4030_VDAC_DEV_GRP); + + return 0; +} + +static void panel_disable_tv(struct omap_display *display) +{ + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x00, + TWL4030_VDAC_DEDICATED); + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x00, + TWL4030_VDAC_DEV_GRP); +} + +static struct omap_display_data beagle_display_data_tv = { + .type = OMAP_DISPLAY_TYPE_VENC, + .name = "tv", + .u.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO, + .panel_enable = panel_enable_tv, + .panel_disable = panel_disable_tv, +}; + +static struct omap_dss_platform_data beagle_dss_data = { + .num_displays = 2, + .displays = { + &beagle_display_data_dvi, + &beagle_display_data_tv, + } +}; + +static struct platform_device beagle_dss_device = { + .name = "omap-dss", + .id = -1, + .dev = { + .platform_data = &beagle_dss_data, + }, +}; + +static void __init beagle_display_init(void) +{ + int r; + + r = gpio_request(beagle_display_data_dvi.panel_reset_gpio, "DVI reset GPIO"); + if(r < 0) { + printk(KERN_ERR "Unable to get DVI reset GPIO\n"); + } +} + +static struct omap_fbmem_config beagle_fbmem0_config = { + .size = 1024*768*4, + .start = OMAPFB_MEMTYPE_SDRAM, +}; + +static struct omap_fbmem_config beagle_fbmem1_config = { + .size = 1024*768*4, + .start = OMAPFB_MEMTYPE_SDRAM, +}; + +static struct omap_fbmem_config beagle_fbmem2_config = { + .size = 1024*768*4, + .start = OMAPFB_MEMTYPE_SDRAM, +}; + + static struct omap_board_config_kernel omap3_beagle_config[] __initdata = { { OMAP_TAG_UART, &omap3_beagle_uart_config }, - { OMAP_TAG_LCD, &omap3_beagle_lcd_config }, + { OMAP_TAG_FBMEM, &beagle_fbmem0_config }, + { OMAP_TAG_FBMEM, &beagle_fbmem1_config }, + { OMAP_TAG_FBMEM, &beagle_fbmem2_config }, }; static struct platform_device *omap3_beagle_devices[] __initdata = { - &omap3_beagle_lcd_device, + &beagle_dss_device, &leds_gpio, &keys_gpio, }; @@ -302,8 +396,6 @@ static void __init omap3_beagle_init(void) omap3_beagle_i2c_init(); platform_add_devices(omap3_beagle_devices, ARRAY_SIZE(omap3_beagle_devices)); - omap_board_config = omap3_beagle_config; - omap_board_config_size = ARRAY_SIZE(omap3_beagle_config); omap_serial_init(); omap_cfg_reg(AH8_34XX_GPIO29); @@ -319,10 +411,13 @@ static void __init omap3_beagle_init(void) usb_musb_init(); usb_ehci_init(); omap3beagle_flash_init(); + beagle_display_init(); } static void __init omap3_beagle_map_io(void) { + omap_board_config = omap3_beagle_config; + omap_board_config_size = ARRAY_SIZE(omap3_beagle_config); omap2_set_globals_343x(); omap2_map_common_io(); } |
From: Jarkko N. <jar...@no...> - 2008-11-05 10:28:17
|
On Tue, 04 Nov 2008 18:10:38 +0200 "ext Tomi Valkeinen" <tom...@no...> wrote: > Signed-off-by: Tomi Valkeinen <tom...@no...> > --- > > arch/arm/mach-omap2/board-omap3beagle.c | 121 ++++++++++++++++++++++++++++--- > 1 files changed, 108 insertions(+), 13 deletions(-) > > +static void __init beagle_display_init(void) > +{ > + int r; > + > + r = gpio_request(beagle_display_data_dvi.panel_reset_gpio, "DVI reset GPIO"); > + if(r < 0) { GPIO label "DVI reset" is enough. Jarkko |
From: Tony L. <to...@at...> - 2008-11-04 18:24:39
|
Hi, One comment below. * Tomi Valkeinen <tom...@no...> [081104 08:32]: > Signed-off-by: Tomi Valkeinen <tom...@no...> > --- > > arch/arm/mach-omap2/board-omap3beagle.c | 121 ++++++++++++++++++++++++++++--- > 1 files changed, 108 insertions(+), 13 deletions(-) > > diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c > index ce6c7b4..a6fe63d 100644 > --- a/arch/arm/mach-omap2/board-omap3beagle.c > +++ b/arch/arm/mach-omap2/board-omap3beagle.c <snip> > +static struct omap_fbmem_config beagle_fbmem0_config = { > + .size = 1024*768*4, > + .start = OMAPFB_MEMTYPE_SDRAM, > +}; > + > +static struct omap_fbmem_config beagle_fbmem1_config = { > + .size = 1024*768*4, > + .start = OMAPFB_MEMTYPE_SDRAM, > +}; > + > +static struct omap_fbmem_config beagle_fbmem2_config = { > + .size = 1024*768*4, > + .start = OMAPFB_MEMTYPE_SDRAM, > +}; > + > + > static struct omap_board_config_kernel omap3_beagle_config[] __initdata = { > { OMAP_TAG_UART, &omap3_beagle_uart_config }, > - { OMAP_TAG_LCD, &omap3_beagle_lcd_config }, > + { OMAP_TAG_FBMEM, &beagle_fbmem0_config }, > + { OMAP_TAG_FBMEM, &beagle_fbmem1_config }, > + { OMAP_TAG_FBMEM, &beagle_fbmem2_config }, > }; > NAK for adding new OMAP_TAGs. We are in process of removing those. You should be able to use just board specific platform_data. If there are ATAGs that are needed, they must be ARM generic. They also need to be acked by Russell King on the linux-arm-kernel mailing list. I doubt that you'll need those though. Regards, Tony |
From: Tomi V. <tom...@no...> - 2008-11-05 10:09:36
|
On Tue, 2008-11-04 at 10:24 -0800, ext Tony Lindgren wrote: > Hi, > > One comment below. > > * Tomi Valkeinen <tom...@no...> [081104 08:32]: > > Signed-off-by: Tomi Valkeinen <tom...@no...> > > --- > > > > arch/arm/mach-omap2/board-omap3beagle.c | 121 ++++++++++++++++++++++++++++--- > > 1 files changed, 108 insertions(+), 13 deletions(-) > > > > diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c > > index ce6c7b4..a6fe63d 100644 > > --- a/arch/arm/mach-omap2/board-omap3beagle.c > > +++ b/arch/arm/mach-omap2/board-omap3beagle.c > > <snip> > > > +static struct omap_fbmem_config beagle_fbmem0_config = { > > + .size = 1024*768*4, > > + .start = OMAPFB_MEMTYPE_SDRAM, > > +}; > > + > > +static struct omap_fbmem_config beagle_fbmem1_config = { > > + .size = 1024*768*4, > > + .start = OMAPFB_MEMTYPE_SDRAM, > > +}; > > + > > +static struct omap_fbmem_config beagle_fbmem2_config = { > > + .size = 1024*768*4, > > + .start = OMAPFB_MEMTYPE_SDRAM, > > +}; > > + > > + > > static struct omap_board_config_kernel omap3_beagle_config[] __initdata = { > > { OMAP_TAG_UART, &omap3_beagle_uart_config }, > > - { OMAP_TAG_LCD, &omap3_beagle_lcd_config }, > > + { OMAP_TAG_FBMEM, &beagle_fbmem0_config }, > > + { OMAP_TAG_FBMEM, &beagle_fbmem1_config }, > > + { OMAP_TAG_FBMEM, &beagle_fbmem2_config }, > > }; > > > > NAK for adding new OMAP_TAGs. We are in process of removing those. > You should be able to use just board specific platform_data. > > If there are ATAGs that are needed, they must be ARM generic. They > also need to be acked by Russell King on the linux-arm-kernel mailing > list. I doubt that you'll need those though. Okay. But in this case, I think, it's not enough to have the board specific data in board.c's platform_data. It would be good that the bootloader can setup the display, and tell the relevant information to the kernel, so that there are no glitches on the display (although not currently implemented in my driver). But there may be an ATAG that can be used for that, I'll have to check. > Regards, > > Tony Tomi |
From: Geert U. <ge...@li...> - 2008-11-05 07:57:04
|
On Tue, 4 Nov 2008, Tomi Valkeinen wrote: > +Sysfs > +----- > +The sysfs interface is a hack, but works for testing. I don't think sysfs > +interface is the best for this in the final version, but I don't quite know > +what would be the best interfaces for these things. > + > +In /sys/devices/platform/omapfb we have four files: framebuffers, > +overlays, managers and displays. You can read them so see the current > +setup, and change them by writing to it in the form of > +"<item-id> <opt1>:<val1> <opt2>:<val2>..." > + > +"framebuffers" lists all framebuffers. Its format is: > + <fb number> > + t:<target overlay> > + > +"overlays" lists all overlays. Its format is: > + <overlay name> > + t:<target manager> > + x:<xpos> > + y:<ypos> > + iw:<input width, read only> > + ih:<input height, read only> > + w:<output width> > + h:<output height> > + e:<enabled> > + > +"managers" lists all overlay managers. Its format is: > + <manager name> > + t:<target display> > + > +"displays" lists all displays. Its format is: > + <display name> > + w:<width> > + h:<height> > + e:<enabled> > + u:<update mode> > + t:<tear sync on/off> As all of these contain lists of sections (one for each fb/overlay/manager/display), what about making them directories, with each section an individual file? Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- ge...@li... In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds |
From: Tomi V. <tom...@no...> - 2008-11-05 10:13:08
|
On Wed, 2008-11-05 at 08:56 +0100, ext Geert Uytterhoeven wrote: > On Tue, 4 Nov 2008, Tomi Valkeinen wrote: > > +Sysfs > > +----- > > +The sysfs interface is a hack, but works for testing. I don't think sysfs > > +interface is the best for this in the final version, but I don't quite know > > +what would be the best interfaces for these things. > > + > > +In /sys/devices/platform/omapfb we have four files: framebuffers, > > +overlays, managers and displays. You can read them so see the current > > +setup, and change them by writing to it in the form of > > +"<item-id> <opt1>:<val1> <opt2>:<val2>..." > > + > > +"framebuffers" lists all framebuffers. Its format is: > > + <fb number> > > + t:<target overlay> > > + > > +"overlays" lists all overlays. Its format is: > > + <overlay name> > > + t:<target manager> > > + x:<xpos> > > + y:<ypos> > > + iw:<input width, read only> > > + ih:<input height, read only> > > + w:<output width> > > + h:<output height> > > + e:<enabled> > > + > > +"managers" lists all overlay managers. Its format is: > > + <manager name> > > + t:<target display> > > + > > +"displays" lists all displays. Its format is: > > + <display name> > > + w:<width> > > + h:<height> > > + e:<enabled> > > + u:<update mode> > > + t:<tear sync on/off> > > As all of these contain lists of sections (one for each > fb/overlay/manager/display), what about making them directories, with each > section an individual file? I've thought that also. The reason for the current form is that it was simpler to implement, and is not meant to be in the final version. If the sysfs interface stays there, then I agree that they files have to be divided. > > Gr{oetje,eeting}s, > > Geert Tomi |
From: Koen K. <k....@st...> - 2008-11-04 18:07:51
Attachments:
PGP.sig
|
Op 4 nov 2008, om 17:10 heeft Tomi Valkeinen het volgende geschreven: > Signed-off-by: Tomi Valkeinen <tom...@no...> > --- > > arch/arm/mach-omap2/board-omap3beagle.c | 121 ++++++++++++++++++++++ > ++++++--- > 1 files changed, 108 insertions(+), 13 deletions(-) > > +static struct omap_display_data beagle_display_data_dvi = { > + .type = OMAP_DISPLAY_TYPE_DPI, > + .name = "dvi", > + .panel_name = "panel-dvi", > + .u.dpi.data_lines = 24, > + .panel_reset_gpio = 170, > + .panel_enable = beagle_enable_dvi, > + .panel_disable = beagle_disable_dvi, gpio 170 is the enable (or disable, I forget which) for the DVI framer (TFP410), not a reset gpio. It would be nice to have a way to enable/ disable the framer from userspace since it burns 60-100mA. regards, Koen |
From: Tomi V. <tom...@no...> - 2008-11-05 10:06:40
|
On Tue, 2008-11-04 at 18:28 +0100, ext Koen Kooi wrote: > Op 4 nov 2008, om 17:10 heeft Tomi Valkeinen het volgende geschreven: > > > Signed-off-by: Tomi Valkeinen <tom...@no...> > > --- > > > > arch/arm/mach-omap2/board-omap3beagle.c | 121 ++++++++++++++++++++++ > > ++++++--- > > 1 files changed, 108 insertions(+), 13 deletions(-) > > > > +static struct omap_display_data beagle_display_data_dvi = { > > + .type = OMAP_DISPLAY_TYPE_DPI, > > + .name = "dvi", > > + .panel_name = "panel-dvi", > > + .u.dpi.data_lines = 24, > > + .panel_reset_gpio = 170, > > + .panel_enable = beagle_enable_dvi, > > + .panel_disable = beagle_disable_dvi, > > gpio 170 is the enable (or disable, I forget which) for the DVI framer > (TFP410), not a reset gpio. It would be nice to have a way to enable/ > disable the framer from userspace since it burns 60-100mA. Well, is there a big difference between reset and enable? Anyway, you can do echo "dvi e:0" > /sys/devices/platform/omapfb/displays and it will turn off the power. Or, alternatively, use the framebuffer blanking, although that has not been implemented to the panel-dvi.c (it's in panel-sdp3430.c). > regards, > > Koen Tom |
From: Koen K. <k....@st...> - 2008-11-05 21:16:10
Attachments:
PGP.sig
|
Op 5 nov 2008, om 11:05 heeft Tomi Valkeinen het volgende geschreven: > On Tue, 2008-11-04 at 18:28 +0100, ext Koen Kooi wrote: >> Op 4 nov 2008, om 17:10 heeft Tomi Valkeinen het volgende geschreven: >> >>> Signed-off-by: Tomi Valkeinen <tom...@no...> >>> --- >>> >>> arch/arm/mach-omap2/board-omap3beagle.c | 121 ++++++++++++++++++++ >>> ++ >>> ++++++--- >>> 1 files changed, 108 insertions(+), 13 deletions(-) >>> >>> +static struct omap_display_data beagle_display_data_dvi = { >>> + .type = OMAP_DISPLAY_TYPE_DPI, >>> + .name = "dvi", >>> + .panel_name = "panel-dvi", >>> + .u.dpi.data_lines = 24, >>> + .panel_reset_gpio = 170, >>> + .panel_enable = beagle_enable_dvi, >>> + .panel_disable = beagle_disable_dvi, >> >> gpio 170 is the enable (or disable, I forget which) for the DVI >> framer >> (TFP410), not a reset gpio. It would be nice to have a way to enable/ >> disable the framer from userspace since it burns 60-100mA. > > Well, is there a big difference between reset and enable? Reset implies that power stays on (like the reset button on a desktop), while enable and disable imply power on and power off. Maybe that's just me :) regards, Koen > Anyway, you > can do > > echo "dvi e:0" > /sys/devices/platform/omapfb/displays > > and it will turn off the power. > > Or, alternatively, use the framebuffer blanking, although that has not > been implemented to the panel-dvi.c (it's in panel-sdp3430.c). > >> regards, >> >> Koen > > Tom > > -- > To unsubscribe from this list: send the line "unsubscribe linux- > omap" in > the body of a message to maj...@vg... > More majordomo info at http://vger.kernel.org/majordomo-info.html > |
From: David B. <da...@pa...> - 2008-11-05 23:21:55
|
On Wednesday 05 November 2008, Jarkko Nikula wrote: > > + r = gpio_request(beagle_display_data_dvi.panel_reset_gpio, "DVI reset GPIO"); > > + if(r < 0) { > > GPIO label "DVI reset" is enough. Surely this ** doesn't work ** at least against the OMAP git tree which already has: omap_cfg_reg(J25_34XX_GPIO170); gpio_request(170, "DVI_nPD"); /* REVISIT leave DVI powered down until it's needed ... */ gpio_direction_output(170, true); Label matches the DVI chip, not board schematics. Agreed, it's not a reset. I'd like to see any FB/DSS/... updates have the property that the TFP410 chip is powered down *unless* both (a) the frame buffer/DSS/... driver is active, and (b) the DVI channel is in use. So for example a kernel without FB/DSS/... configured, that DVI chip is never powered up. And when only the S-Video channel is used, DVI isn't powered up either. - Dave |
From: Tomi V. <tom...@no...> - 2008-11-06 08:23:25
|
On Thu, 2008-11-06 at 01:21 +0200, ext David Brownell wrote: > On Wednesday 05 November 2008, Jarkko Nikula wrote: > > > + r = gpio_request(beagle_display_data_dvi.panel_reset_gpio, > "DVI reset GPIO"); > > > + if(r < 0) { > > > > GPIO label "DVI reset" is enough. > > Surely this ** doesn't work ** at least against the OMAP git tree > which already has: Well, it surely does! =) But good catch, I'll fix it. There's probably a warning on kernel log about "can't get a gpio". > > omap_cfg_reg(J25_34XX_GPIO170); > gpio_request(170, "DVI_nPD"); > /* REVISIT leave DVI powered down until it's needed ... */ > gpio_direction_output(170, true); > > Label matches the DVI chip, not board schematics. Agreed, > it's not a reset. > > I'd like to see any FB/DSS/... updates have the property that > the TFP410 chip is powered down *unless* both (a) the frame > buffer/DSS/... driver is active, and (b) the DVI channel is > in use. > > So for example a kernel without FB/DSS/... configured, that > DVI chip is never powered up. And when only the S-Video channel > is used, DVI isn't powered up either. > Well, my drivers do not enable the DVI chip unless DVI is in use. It is in use by default, but you can turn it off. And if the whole DSS is not compiled in, then no body will turn the DVI chip on. However, the bootloader turns the DVI chip on (at least the default beagle board u-boot). If the kernel by default turns the DVI chip off, and then later on when the DSS starts, we'll have a glitch on the display. Not that there currently is support for glitchless transition from bootloader display to kernel display, but still... > - Dave > Tomi |
From: Koen K. <k....@st...> - 2008-11-06 08:31:13
Attachments:
PGP.sig
|
Op 6 nov 2008, om 09:23 heeft Tomi Valkeinen het volgende geschreven: > > However, the bootloader turns the DVI chip on (at least the default > beagle board u-boot). Only the old and broken TI u-boots do that, recent ones (2008.10) with the patches that are going upstream don't touch the DVI chip (nor the audio chips for that matter. regards, Koen |
From: Shah, H. <har...@ti...> - 2008-11-10 04:03:58
|
> -----Original Message----- > From: Tomi Valkeinen [mailto:tom...@no...] > Sent: Tuesday, November 04, 2008 9:40 PM > To: lin...@li... > Cc: lin...@vg... > Subject: [Linux-fbdev-devel] [REVIEW PATCH 0/9] DSS: Series description > > New Display Subsystem for OMAP2/3 > --------------------------------- > > This patch set implements new Display Subsystem (DSS) for OMAP2/3 processors. > The DSS is still under work and these patches are for review. Note that there > is also another new DSS implementation from TI. > > The DSS has been tested on OMAP3 SDP board, Beagle Board and on two unreleased > boards with DSI and SDI displays. The DSS used to work on OMAP2 board also, > but > it's been a while since I was able to test with OMAP2 board. > > The first patch is a doc file that tries to explain a bit how the drivers > work. > > The patch set is based on the current linux-omap tree. > > You can find the patches also from a git tree at > http://www.bat.org/~tomba/git/linux-omap-dss.git/ > > Note that you also need two patches from Mans Rullgard to make things work. > These > are needed to be able to reconfigure DSS functional clock. > http://git.mansr.com/?p=linux- > omap;a=commit;h=e2de5e5578fbaa9b4b75074796da0608fc93e6ae > http://git.mansr.com/?p=linux- > omap;a=commit;h=2b7b958dc79e51127d7a4ecf88ce12dbc6c31426 > > Questions/problems > ------------------ > > Perhaps the biggest problems I have is the interface to user space. OMAP > hardware doesn't quite fit in to the framebuffer framework. The problem is > that > a framebuffer can go to multiple overlays, and also the target display, to > which a framebuffer goes, can change. In addition, the framebuffer size is > used as overlay size, not display resolution. So a framebuffer != display. > > I believe DRM modesetting could be of help here, at least partially, but I > haven't tried that approach yet. > > But DRM modesetting wouldn't solve all the problems. For example, we still > need > to configure overlays and overlay managers, and they don't quite belong to > either the framebuffer side or the display side. Currently you configure them > via a hackish sysfs interface, but I've been wondering if a /dev/omap-dss > device with ioctls would be a better choice. > > There's currently no V4L2 support, but I have been thinking about that. I > don't > want to make any hardcoded configuration for those, because sometimes you want > to use framebuffer devices for video overlays. So what I'd like to have is a > way, compile time or run time, to configure which overlays go to framebuffer > devices and which go to V4L2 devices. > > --- > > Tomi Valkeinen (9): > DSS: support for OMAP3 SDP board > DSS: support for Beagle Board > DSS: Add generic DVI panel > DSS: OMAPFB: fb driver for new display subsystem > DSS: DSI support for OMAP2/3 DSS > DSS: TV-out support for OMAP2/3 DSS > DSS: RFBI support for OMAP2/3 DSS > DSS: New display subsystem driver for OMAP2/3 > DSS: Documentation for OMAP2/3 display subsystem > [Shah, Hardik] We have reviewed the patches in detail and even made modifications to make it work on the OMAP3EVM. Here are our observations: 1) The overall implementation is very much in line with our earlier proposal to provide generic API that supports both V4L2 and FBDEV. 2) The implementation supports multiple output interfaces e.g. RFBI, SDI, DSI, ... that we did not have in our plans 3) You have also validated it on lot many platforms that we do not have access. We believe patches are a good step in right direction. We plan to use this patchset as base for supporting these additional use cases: a) Adding the V4L2 user interface to the video pipelines (I believe video pipelines are natural fit for the V4L2 frame work. If use case is there for FBDEV interface we can have compile time option to expose the video pipelines as the either FBDEV or v4L2 interface). b) Modification of the DSS/DISPC library for supporting V4L2 user interface. c) Supporting the functionality like alpha blending, global alpha blending, setting of various TV standards etc. d) Supporting the mirroring and rotation in both FBDEV and V4L2 driver. Do let us know if you aren't already working on any of these. BTW, patch for supporting omap3evm will be submitted shortly. Regards, Vaibhav Hiremath <hva...@ti...> Hardik Shah <har...@ti...> > > > -- > Tomi Valkeinen > > > ------------------------------------------------------------------------- > This SF.Net email is sponsored by the Moblin Your Move Developer's challenge > Build the coolest Linux based applications with Moblin SDK & win great prizes > Grand prize is a trip for two to an Open Source event anywhere in the world > http://moblin-contest.org/redirect.php?banner_id=100&url=/ > _______________________________________________ > Linux-fbdev-devel mailing list > Lin...@li... > https://lists.sourceforge.net/lists/listinfo/linux-fbdev-devel |