Adds support for OMAP framebuffer.
Signed-off-by: Imre Deak <imre.deak@nokia.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
working with S1D13806). Product specs at
<http://www.erd.epson.com/vdc/html/legacy_13xxx.htm>
+config FB_OMAP
+ tristate "OMAP frame buffer support (EXPERIMENTAL)"
+ depends on FB && ARCH_OMAP1
+ select FB_SOFT_CURSOR
+ help
+ This is the new frame buffer device driver with 2D acceleration
+ for OMAP boards.
+
+config FB_OMAP_INTERNAL_LCDC
+ bool "OMAP internal LCD controller support"
+ depends on FB_OMAP
+ default y
+ help
+ Say Y here, if you want to have support for the internal OMAP
+ LCD controller. If unsure, say Y.
+
+config FB_OMAP_EXTERNAL_LCDC
+ bool "OMAP external LCD controller support"
+ depends on FB_OMAP
+ help
+ Say Y here, if you want to have support for boards with an
+ external LCD controller connected to the SoSSI interface.
+
+config FB_OMAP_MANUAL_UPDATE
+ bool "Default to manual update mode"
+ depends on FB_OMAP_EXTERNAL_LCDC
+ help
+ Say Y here, if your user-space applications are capable of
+ notifying the frame buffer driver when a change has occured in
+ the frame buffer content and thus a reload of the image data is
+ required. If unsure, say N.
+
+config FB_OMAP_DMA_TUNE
+ bool "Set DMA SDRAM access priority high"
+ depends on FB_OMAP
+ help
+ On systems in which video memory is in system memory
+ (SDRAM) this will speed up graphics DMA operations.
+ If you have such a system and want to use rotation
+ answer yes. Answer no if you have a dedicated video
+ memory, or don't use any of the accelerated features.
+
config FB_VIRTUAL
tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
depends on FB
obj-$(CONFIG_FB_VESA) += vesafb.o
obj-$(CONFIG_FB_VGA16) += vga16fb.o vgastate.o
obj-$(CONFIG_FB_OF) += offb.o
+obj-$(CONFIG_FB_OMAP) += omap/ cfbcopyarea.o cfbfillrect.o cfbimgblt.o
# the test framebuffer is last
obj-$(CONFIG_FB_VIRTUAL) += vfb.o
--- /dev/null
+#
+# Makefile for the new OMAP framebuffer device driver
+#
+
+obj-$(CONFIG_FB_OMAP) += omapfb.o
+
+objs-yy := omapfb_main.o
+objs-y$(CONFIG_FB_OMAP_INTERNAL_LCDC) += omap_lcdc.o
+objs-y$(CONFIG_FB_OMAP_EXTERNAL_LCDC) += sossi.o
+objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_H3) += lcd_h3.o
+objs-y$(CONFIG_MACH_OMAP_H2) += lcd_h2.o
+objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o
+objs-$(CONFIG_ARCH_OMAP1510)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o
+objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o
+objs-$(CONFIG_ARCH_OMAP730)$(CONFIG_MACH_OMAP_PERSEUS2) += lcd_p2.o
+
+omapfb-objs := $(objs-yy)
+
--- /dev/null
+/*
+ * File: drivers/video/omap_new/debug.c
+ *
+ * Debug support for the omapfb driver
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __OMAPFB_DEBUG_H
+#define __OMAPFB_DEBUG_H
+
+#ifdef OMAPFB_DBG
+
+#define DBG_BUF_SIZE 2048
+#define MAX_DBG_INDENT_LEVEL 5
+#define DBG_INDENT_SIZE 3
+#define MAX_DBG_MESSAGES 0
+
+static int dbg_indent;
+static int dbg_cnt;
+static char dbg_buf[DBG_BUF_SIZE];
+static spinlock_t dbg_spinlock = SPIN_LOCK_UNLOCKED;
+
+static inline void dbg_print(int level, const char *fmt, ...)
+{
+ if (level <= OMAPFB_DBG) {
+ if (!MAX_DBG_MESSAGES || dbg_cnt < MAX_DBG_MESSAGES) {
+ va_list args;
+ int ind = dbg_indent;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dbg_spinlock, flags);
+ dbg_cnt++;
+ if (ind > MAX_DBG_INDENT_LEVEL)
+ ind = MAX_DBG_INDENT_LEVEL;
+
+ printk("%*s", ind * DBG_INDENT_SIZE, "");
+ va_start(args, fmt);
+ vsnprintf(dbg_buf, sizeof(dbg_buf), fmt, args);
+ printk(dbg_buf);
+ va_end(args);
+ spin_unlock_irqrestore(&dbg_spinlock, flags);
+ }
+ }
+}
+
+#define DBGPRINT dbg_print
+
+#define DBGENTER(level) do { \
+ dbg_print(level, "%s: Enter\n", __FUNCTION__); \
+ dbg_indent++; \
+ } while (0)
+
+#define DBGLEAVE(level) do { \
+ dbg_indent--; \
+ dbg_print(level, "%s: Leave\n", __FUNCTION__); \
+ } while (0)
+
+static inline void dump_dma_regs(int lch)
+{
+#define _R(x) __REG16(OMAP_DMA_##x(lch))
+
+ dbg_print(4, "\nCSDP :%#06x CCR :%#06x CSSA_U :%#06x "
+ "\nCDSA_L:%#06x CDSA_U :%#06x CEN :%#06x "
+ "\nCFN :%#06x CSFI :%#06x CSEI :%#06x "
+ "\nCSAC :%#06x CICR :%#06x CSR :%04x "
+ "\nCSSA_L:%#06x CDAC :%#06x CDEI :%#06x "
+ "\nCDFI :%#06x COLOR_L :%#06x COLOR_U :%#06x "
+ "\nCCR2 :%#06x CLNK_CTRL:%#06x LCH_CTRL:%#06x\n",
+ _R(CSDP), _R(CCR), _R(CSSA_U),
+ _R(CDSA_L), _R(CDSA_U), _R(CEN),
+ _R(CFN), _R(CSFI), _R(CSEI),
+ _R(CSAC), _R(CICR), 0, /* _R(CSR), */
+ _R(CSSA_L), _R(CDAC), _R(CDEI),
+ _R(CDFI), _R(COLOR_L), _R(COLOR_U),
+ _R(CCR2), _R(CLNK_CTRL), _R(LCH_CTRL));
+#undef _R
+}
+
+#define DUMP_DMA_REGS(lch) dump_dma_regs(lch)
+
+#else /* OMAPFB_DBG */
+
+#define DBGPRINT(level, format, ...)
+#define DBGENTER(level)
+#define DBGLEAVE(level)
+#define DUMP_DMA_REGS(lch)
+
+#endif /* OMAPFB_DBG */
+
+#endif /* __OMAPFB_DEBUG_H */
--- /dev/null
+/*
+ * File: drivers/video/omap_new/lcd-h2.c
+ *
+ * LCD panel support for the TI OMAP H2 board
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+
+#include <asm/arch/mux.h>
+
+#include "omapfb.h"
+
+// #define OMAPFB_DBG 1
+
+#include "debug.h"
+#include "../drivers/ssi/omap-uwire.h"
+
+#define TSC2101_UWIRE_CS 1
+
+static int tsc2101_write_reg(int page, int reg, u16 data)
+{
+ u16 cmd;
+ int r;
+
+ DBGENTER(1);
+
+ cmd = ((page & 3) << 11) | ((reg & 0x3f) << 5);
+ if (omap_uwire_data_transfer(TSC2101_UWIRE_CS, cmd, 16, 0, NULL, 1))
+ r = -1;
+ else
+ r = omap_uwire_data_transfer(TSC2101_UWIRE_CS, data, 16, 0, NULL, 0);
+
+ DBGLEAVE(1);
+ return r;
+}
+
+static int h2_panel_init(struct lcd_panel *panel)
+{
+ unsigned long uwire_flags;
+ DBGENTER(1);
+
+ /* Configure N15 pin to be uWire CS1 */
+ omap_cfg_reg(N15_1610_UWIRE_CS1);
+ uwire_flags = UWIRE_READ_RISING_EDGE | UWIRE_WRITE_RISING_EDGE;
+ uwire_flags |= UWIRE_FREQ_DIV_8;
+ omap_uwire_configure_mode(TSC2101_UWIRE_CS, uwire_flags);
+
+ DBGLEAVE(1);
+ return 0;
+}
+
+static void h2_panel_cleanup(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+ DBGLEAVE(1);
+}
+
+static int h2_panel_enable(struct lcd_panel *panel)
+{
+ int r;
+
+ DBGENTER(1);
+
+ /* Assert LCD_EN, BKLIGHT_EN pins on LCD panel
+ * page2, GPIO config reg, GPIO(0,1) to out and asserted
+ */
+ r = tsc2101_write_reg(2, 0x23, 0xCC00) ? -1 : 0;
+
+ DBGLEAVE(1);
+ return r;
+}
+
+static void h2_panel_disable(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+
+ /* Deassert LCD_EN and BKLIGHT_EN pins on LCD panel
+ * page2, GPIO config reg, GPIO(0,1) to out and deasserted
+ */
+ if (tsc2101_write_reg(2, 0x23, 0x8800))
+ PRNERR("failed to disable LCD panel\n");
+
+ DBGLEAVE(1);
+}
+
+static unsigned long h2_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+static struct lcdc_video_mode mode240x320 = {
+ .x_res = 240,
+ .y_res = 320,
+ .pixel_clock = 12500,
+ .bpp = 16,
+ .hsw = 12,
+ .hfp = 14,
+ .hbp = 72 - 12,
+ .vsw = 1,
+ .vfp = 1,
+ .vbp = 0,
+ .pcd = 12,
+};
+
+struct lcd_panel h2_panel = {
+ .name = "h2",
+ .config = LCD_PANEL_TFT,
+ .video_mode = &mode240x320,
+
+ .init = h2_panel_init,
+ .cleanup = h2_panel_cleanup,
+ .enable = h2_panel_enable,
+ .disable = h2_panel_disable,
+ .get_caps= h2_panel_get_caps,
+};
+
--- /dev/null
+/*
+ * File: drivers/video/omap_new/lcd-h3.c
+ *
+ * LCD panel support for the TI OMAP H3 board
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+
+#include <asm/arch/gpio.h>
+#include <asm/arch/tps65010.h>
+
+#include "omapfb.h"
+
+// #define OMAPFB_DBG 1
+
+#include "debug.h"
+
+static int h3_panel_init(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+ DBGLEAVE(1);
+ return 0;
+}
+
+static void h3_panel_cleanup(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+ DBGLEAVE(1);
+}
+
+static int h3_panel_enable(struct lcd_panel *panel)
+{
+ int r = 0;
+
+ DBGENTER(1);
+
+ /* GPIO1 and GPIO2 of TPS65010 send LCD_ENBKL and LCD_ENVDD signals */
+ r = tps65010_set_gpio_out_value(GPIO1, HIGH);
+ if (!r)
+ r = tps65010_set_gpio_out_value(GPIO2, HIGH);
+ if (r)
+ PRNERR("Unable to turn on LCD panel\n");
+
+ DBGLEAVE(1);
+ return r;
+}
+
+static void h3_panel_disable(struct lcd_panel *panel)
+{
+ int r = 0;
+
+ DBGENTER(1);
+
+ /* GPIO1 and GPIO2 of TPS65010 send LCD_ENBKL and LCD_ENVDD signals */
+ r = tps65010_set_gpio_out_value(GPIO1, LOW);
+ if (!r)
+ tps65010_set_gpio_out_value(GPIO2, LOW);
+ if (r)
+ PRNERR("Unable to turn off LCD panel\n");
+
+ DBGLEAVE(1);
+}
+
+static unsigned long h3_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+static struct lcdc_video_mode mode240x320 = {
+ .x_res = 240,
+ .y_res = 320,
+ .pixel_clock = 12500,
+ .bpp = 16,
+ .hsw = 12,
+ .hfp = 14,
+ .hbp = 72 - 12,
+ .vsw = 1,
+ .vfp = 1,
+ .vbp = 0,
+ .pcd = 4,
+};
+
+struct lcd_panel h3_panel = {
+ .name = "h3",
+ .config = LCD_PANEL_TFT,
+ .video_mode = &mode240x320,
+
+ .init = h3_panel_init,
+ .cleanup = h3_panel_cleanup,
+ .enable = h3_panel_enable,
+ .disable = h3_panel_disable,
+ .get_caps= h3_panel_get_caps,
+};
+
--- /dev/null
+/*
+ * File: drivers/video/omap_new/lcd-inn1510.c
+ *
+ * LCD panel support for the TI OMAP1510 Innovator board
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/fpga.h>
+
+#include "omapfb.h"
+
+// #define OMAPFB_DBG 1
+
+#include "debug.h"
+
+static int innovator1510_panel_init(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+ DBGLEAVE(1);
+ return 0;
+}
+
+static void innovator1510_panel_cleanup(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+ DBGLEAVE(1);
+}
+
+static int innovator1510_panel_enable(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+
+ fpga_write(0x7, OMAP1510_FPGA_LCD_PANEL_CONTROL);
+
+ DBGLEAVE(1);
+ return 0;
+}
+
+static void innovator1510_panel_disable(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+
+ fpga_write(0x0, OMAP1510_FPGA_LCD_PANEL_CONTROL);
+
+ DBGLEAVE(1);
+}
+
+static unsigned long innovator1510_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+static struct lcdc_video_mode mode240x320 = {
+ .x_res = 240,
+ .y_res = 320,
+ .pixel_clock = 12500,
+ .bpp = 16,
+ .hsw = 40,
+ .hfp = 40,
+ .hbp = 72,
+ .vsw = 1,
+ .vfp = 1,
+ .vbp = 0,
+ .pcd = 12,
+};
+
+struct lcd_panel innovator1510_panel = {
+ .name = "inn1510",
+ .config = LCD_PANEL_TFT,
+ .video_mode = &mode240x320,
+
+ .init = innovator1510_panel_init,
+ .cleanup = innovator1510_panel_cleanup,
+ .enable = innovator1510_panel_enable,
+ .disable = innovator1510_panel_disable,
+ .get_caps= innovator1510_panel_get_caps,
+};
+
--- /dev/null
+/*
+ * File: drivers/video/omap_new/lcd-inn1610.c
+ *
+ * LCD panel support for the TI OMAP1610 Innovator board
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+
+#include <asm/arch/gpio.h>
+
+#include "omapfb.h"
+
+// #define OMAPFB_DBG 1
+
+#include "debug.h"
+
+static int innovator1610_panel_init(struct lcd_panel *panel)
+{
+ int r = 0;
+
+ DBGENTER(1);
+
+ if (omap_request_gpio(14)) {
+ PRNERR("can't request GPIO 14\n");
+ r = -1;
+ goto exit;
+ }
+ if (omap_request_gpio(15)) {
+ PRNERR("can't request GPIO 15\n");
+ omap_free_gpio(14);
+ r = -1;
+ goto exit;
+ }
+ /* configure GPIO(14, 15) as outputs */
+ omap_set_gpio_direction(14, 0);
+ omap_set_gpio_direction(15, 0);
+exit:
+ DBGLEAVE(1);
+ return r;
+}
+
+static void innovator1610_panel_cleanup(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+
+ omap_free_gpio(15);
+ omap_free_gpio(14);
+
+ DBGLEAVE(1);
+}
+
+static int innovator1610_panel_enable(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+
+ /* set GPIO14 and GPIO15 high */
+ omap_set_gpio_dataout(14, 1);
+ omap_set_gpio_dataout(15, 1);
+
+ DBGLEAVE(1);
+ return 0;
+}
+
+static void innovator1610_panel_disable(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+
+ /* set GPIO13, GPIO14 and GPIO15 low */
+ omap_set_gpio_dataout(14, 0);
+ omap_set_gpio_dataout(15, 0);
+
+ DBGLEAVE(1);
+}
+
+static unsigned long innovator1610_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+static struct lcdc_video_mode mode320x240 = {
+ .x_res = 320,
+ .y_res = 240,
+ .pixel_clock = 12500,
+ .bpp = 16,
+ .hsw = 40,
+ .hfp = 40,
+ .hbp = 72,
+ .vsw = 1,
+ .vfp = 1,
+ .vbp = 0,
+ .pcd = 12,
+};
+
+struct lcd_panel innovator1610_panel = {
+ .name = "inn1610",
+ .config = LCD_PANEL_TFT,
+ .video_mode = &mode320x240,
+
+ .init = innovator1610_panel_init,
+ .cleanup = innovator1610_panel_cleanup,
+ .enable = innovator1610_panel_enable,
+ .disable = innovator1610_panel_disable,
+ .get_caps= innovator1610_panel_get_caps,
+};
+
--- /dev/null
+/*
+ * File: drivers/video/omap_new/lcd-osk.c
+ *
+ * LCD panel support for the TI OMAP OSK board
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ * Adapted for OSK by <dirk.behme@de.bosch.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/mux.h>
+
+#include "omapfb.h"
+
+// #define OMAPFB_DBG 1
+
+#include "debug.h"
+
+static int osk_panel_init(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+ DBGLEAVE(1);
+ return 0;
+}
+
+static void osk_panel_cleanup(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+ DBGLEAVE(1);
+}
+
+static int osk_panel_enable(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+
+ /* configure PWL pin */
+ omap_cfg_reg(PWL);
+
+ /* Enable PWL unit */
+ omap_writeb(0x01, OMAP16XX_PWL_CLK_ENABLE);
+
+ /* Set PWL level */
+ omap_writeb(0xFF, OMAP16XX_PWL_ENABLE);
+
+ /* configure GPIO2 as output */
+ omap_set_gpio_direction(2, 0);
+
+ /* set GPIO2 high */
+ omap_set_gpio_dataout(2, 1);
+
+ DBGLEAVE(1);
+ return 0;
+}
+
+static void osk_panel_disable(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+
+ /* Set PWL level to zero */
+ omap_writeb(0x00, OMAP16XX_PWL_ENABLE);
+
+ /* Disable PWL unit */
+ omap_writeb(0x00, OMAP16XX_PWL_CLK_ENABLE);
+
+ /* set GPIO2 low */
+ omap_set_gpio_dataout(2, 0);
+
+ DBGLEAVE(1);
+}
+
+static unsigned long osk_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+static struct lcdc_video_mode mode240x320 = {
+ .x_res = 240,
+ .y_res = 320,
+ .pixel_clock = 12500,
+ .bpp = 16,
+ .hsw = 40,
+ .hfp = 40,
+ .hbp = 72,
+ .vsw = 1,
+ .vfp = 1,
+ .vbp = 0,
+ .pcd = 12,
+};
+
+struct lcd_panel osk_panel = {
+ .name = "osk",
+ .config = LCD_PANEL_TFT,
+ .video_mode = &mode240x320,
+
+ .init = osk_panel_init,
+ .cleanup = osk_panel_cleanup,
+ .enable = osk_panel_enable,
+ .disable = osk_panel_disable,
+ .get_caps= osk_panel_get_caps,
+};
+
--- /dev/null
+/*
+ * File: drivers/video/omap_new/lcd-p2.c
+ *
+ * LCD panel support for the TI OMAP P2 board
+ *
+ * Authors:
+ * jekyll <jekyll@mail.jekyll.idv.tw>
+ * B Jp <lastjp_fr@yahoo.fr>
+ * Brian Swetland <swetland@android.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/gpio.h>
+
+#include "omapfb.h"
+
+/*
+ * File: epson-md-tft.h
+ *
+ * This file contains definitions for Epsons MD-TF LCD Module
+ *
+ * Copyright (C) 2004 MPC-Data Limited (http://www.mpc-data.co.uk)
+ * Author: Dave Peverley <dpeverley at mpc-data.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Please report all bugs and problems to the author.
+ *
+ */
+
+/* LCD uWire commands & params
+ * All values from Epson
+ */
+#define LCD_DISON 0xAF
+#define LCD_DISOFF 0xAE
+#define LCD_DISNOR 0xA6
+#define LCD_DISINV 0xA7
+#define LCD_DISCTL 0xCA
+#define LCD_GCP64 0xCB
+#define LCD_GCP16 0xCC
+#define LCD_GSSET 0xCD
+#define LCD_SLPIN 0x95
+#define LCD_SLPOUT 0x94
+#define LCD_SD_PSET 0x75
+#define LCD_MD_PSET 0x76
+#define LCD_SD_CSET 0x15
+#define LCD_MD_CSET 0x16
+#define LCD_DATCTL 0xBC
+#define LCD_RAMWR 0x5C
+#define LCD_RAMRD 0x5D
+#define LCD_PTLIN 0xA8
+#define LCD_PTLOUT 0xA9
+#define LCD_ASCSET 0xAA
+#define LCD_SCSTART 0xAB
+#define LCD_VOLCTL 0xC6
+#define LCD_NOP 0x25
+#define LCD_OSCISEL 0x7
+#define LCD_3500KSET 0xD1
+#define LCD_3500KEND 0xD2
+#define LCD_14MSET 0xD3
+#define LCD_14MEND 0xD4
+
+#define INIT_3500KSET 0x45
+#define INIT_14MSET 0x4B
+#define INIT_DATCTL 0x08 /* 6.6.6 bits for D-Sample */
+
+#define INIT_OSCISEL 0x05
+
+#define INIT_VOLCTL 0x77 /* Nominel "volume" */
+
+#define INIT_VOLCTL_Ton 0x98 /* Activate power-IC timer */
+#define INIT_GSSET 0x00
+
+const unsigned short INIT_DISCTL[11] =
+{
+ 0xDE, 0x01, 0x64, 0x00, 0x1B, 0xF4, 0x00, 0xDC, 0x00, 0x02, 0x00
+};
+
+const unsigned short INIT_GCP64[126] =
+{
+ 0x3B,0x00,0x42,0x00,0x4A,0x00,0x51,0x00,
+ 0x58,0x00,0x5F,0x00,0x66,0x00,0x6E,0x00,
+ 0x75,0x00,0x7C,0x00,0x83,0x00,0x8A,0x00,
+ 0x92,0x00,0x99,0x00,0xA0,0x00,0xA7,0x00,
+ 0xAE,0x00,0xB6,0x00,0xBD,0x00,0xC4,0x00,
+ 0xCB,0x00,0xD2,0x00,0xDA,0x00,0xE1,0x00,
+ 0xE8,0x00,0xEF,0x00,0xF6,0x00,0xFE,0x00,
+ 0x05,0x01,0x0C,0x01,0x13,0x01,0x1A,0x01,
+ 0x22,0x01,0x29,0x01,0x30,0x01,0x37,0x01,
+ 0x3E,0x01,0x46,0x01,0x4D,0x01,0x54,0x01,
+ 0x5B,0x01,0x62,0x01,0x6A,0x01,0x71,0x01,
+ 0x78,0x01,0x7F,0x01,0x86,0x01,0x8E,0x01,
+ 0x95,0x01,0x9C,0x01,0xA3,0x01,0xAA,0x01,
+ 0xB2,0x01,0xB9,0x01,0xC0,0x01,0xC7,0x01,
+ 0xCE,0x01,0xD6,0x01,0xDD,0x01,0xE4,0x01,
+ 0xEB,0x01,0xF2,0x01,0xFA,0x01
+};
+
+const unsigned short INIT_GCP16[15] =
+{
+ 0x1A,0x31,0x48,0x54,0x5F,0x67,0x70,0x76,0x7C,0x80,0x83,0x84,0x85,0x87,0x96
+};
+
+const unsigned short INIT_MD_PSET[4] = { 0, 0, 219, 0 };
+const unsigned short INIT_MD_CSET[4] = { 2, 0, 177, 0 };
+
+const unsigned short INIT_SD_PSET[4] = { 0x00, 0x01, 0x00, 0x01 };
+const unsigned short INIT_SD_CSET[4] = { 0x00, 0x02, 0x00, 0x02 };
+
+const unsigned short INIT_ASCSET[7] = { 0x00, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0x01 };
+const unsigned short INIT_SCSTART[2] = { 0x00, 0x00 };
+
+/* ----- end of epson_md_tft.h ----- */
+
+
+#include "debug.h"
+#include "../drivers/ssi/omap-uwire.h"
+
+#define LCD_UWIRE_CS 0
+
+static int p2_panel_init(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+ DBGLEAVE(1);
+ return 0;
+}
+
+static void p2_panel_cleanup(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+ DBGLEAVE(1);
+}
+
+static int p2_panel_enable(struct lcd_panel *panel)
+{
+ int i;
+ unsigned long value;
+ DBGENTER(1);
+
+ /* thwack the reset line */
+ omap_set_gpio_direction(19, 0);
+ omap_set_gpio_dataout(19, 0);
+ mdelay(2);
+ omap_set_gpio_dataout(19, 1);
+
+ /* bits 31:28 -> 0 LCD_PXL_15 .. 12 */
+ value = omap_readl(OMAP730_IO_CONF_3) & 0x0FFFFFFF;
+ omap_writel(value, OMAP730_IO_CONF_3);
+
+ /* bits 19:0 -> 0 LCD_VSYNC, AC, PXL_0, PCLK, HSYNC,
+ ** PXL_9..1, PXL_10, PXL_11
+ */
+ value = omap_readl(OMAP730_IO_CONF_4) & 0xFFF00000;
+ omap_writel(value, OMAP730_IO_CONF_4);
+
+ omap_uwire_configure_mode(0,16);
+
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISOFF, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SLPIN, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISNOR, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_GSSET, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_GSSET | 0x100), 9, 0,NULL,1);
+
+ /* DISCTL */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISCTL, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_DISCTL)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_DISCTL[i] | 0x100), 9, 0,NULL,1);
+
+ /* GCP64 */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_GCP64, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_GCP64)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_GCP64[i] | 0x100), 9, 0,NULL,1);
+
+ /* GCP16 */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_GCP16, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_GCP16)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_GCP16[i] | 0x100), 9, 0,NULL,1);
+
+ /* MD_CSET */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_MD_CSET, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_MD_CSET)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_MD_CSET[i] | 0x100), 9, 0,NULL,1);
+
+ /* MD_PSET */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_MD_PSET, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_MD_PSET)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_MD_PSET[i] | 0x100), 9, 0,NULL,1);
+
+ /* SD_CSET */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SD_CSET, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_SD_CSET)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_SD_CSET[i] | 0x100), 9, 0,NULL,1);
+
+ /* SD_PSET */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SD_PSET, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_SD_PSET)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_SD_PSET[i] | 0x100), 9, 0,NULL,1);
+
+ /* DATCTL */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DATCTL, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_DATCTL | 0x100), 9, 0,NULL,1);
+
+ /* OSSISEL = d'5 */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_OSCISEL, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_OSCISEL | 0x100), 9, 0,NULL,1);
+
+ /* 14MSET = d'74 */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_14MSET, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_14MSET | 0x100), 9, 0,NULL,1);
+
+ /* 14MEND */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_14MEND, 9, 0,NULL,1);
+
+ /* 3500KSET = d'69 */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_3500KSET, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_3500KSET | 0x100), 9, 0,NULL,1);
+
+ /* 3500KEND */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_3500KEND, 9, 0,NULL,1);
+
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SLPOUT, 9, 0,NULL,1);
+
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_VOLCTL, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_VOLCTL_Ton | 0x100), 9, 0,NULL,1);
+
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_VOLCTL, 9, 0,NULL,1);
+
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_VOLCTL | 0x100), 9, 0,NULL,1);
+
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISON, 9, 0,NULL,1);
+
+ /* enable backlight */
+ omap_set_gpio_direction(134, 0);
+ omap_set_gpio_dataout(134, 1);
+
+ DBGLEAVE(1);
+ return 0;
+}
+
+static void p2_panel_disable(struct lcd_panel *panel)
+{
+ DBGENTER(1);
+ DBGLEAVE(1);
+}
+
+static unsigned long p2_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+static struct lcdc_video_mode mode176x220 = {
+ .x_res = 176,
+ .y_res = 220,
+ .pixel_clock = 12500,
+ .bpp = 16,
+ .hsw = 5,
+ .hfp = 1,
+ .hbp = 1,
+ .vsw = 2,
+ .vfp = 12,
+ .vbp = 1,
+ .pcd = 4,
+ .flags = OMAP_LCDC_INV_PIX_CLOCK,
+};
+
+struct lcd_panel p2_panel = {
+ .name = "p2",
+ .config = LCD_PANEL_TFT,
+ .video_mode = &mode176x220,
+
+ .init = p2_panel_init,
+ .cleanup = p2_panel_cleanup,
+ .enable = p2_panel_enable,
+ .disable = p2_panel_disable,
+ .get_caps= p2_panel_get_caps,
+};
+
--- /dev/null
+/*
+ * linux/arch/arm/mach-omap/omap_lcdc.c
+ *
+ * OMAP internal LCD controller
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+
+#include <asm/arch/dma.h>
+#include <asm/mach-types.h>
+#include <asm/hardware/clock.h>
+
+#include "omapfb.h"
+#include "debug.h"
+
+#define OMAP_LCDC_BASE 0xfffec000
+#define OMAP_LCDC_SIZE 256
+#define OMAP_LCDC_IRQ INT_LCD_CTRL
+
+#define OMAP_LCDC_CONTROL (OMAP_LCDC_BASE + 0x00)
+#define OMAP_LCDC_TIMING0 (OMAP_LCDC_BASE + 0x04)
+#define OMAP_LCDC_TIMING1 (OMAP_LCDC_BASE + 0x08)
+#define OMAP_LCDC_TIMING2 (OMAP_LCDC_BASE + 0x0c)
+#define OMAP_LCDC_STATUS (OMAP_LCDC_BASE + 0x10)
+#define OMAP_LCDC_SUBPANEL (OMAP_LCDC_BASE + 0x14)
+#define OMAP_LCDC_LINE_INT (OMAP_LCDC_BASE + 0x18)
+#define OMAP_LCDC_DISPLAY_STATUS (OMAP_LCDC_BASE + 0x1c)
+
+#define OMAP_LCDC_STAT_DONE (1 << 0)
+#define OMAP_LCDC_STAT_VSYNC (1 << 1)
+#define OMAP_LCDC_STAT_SYNC_LOST (1 << 2)
+#define OMAP_LCDC_STAT_ABC (1 << 3)
+#define OMAP_LCDC_STAT_LINE_INT (1 << 4)
+#define OMAP_LCDC_STAT_FUF (1 << 5)
+#define OMAP_LCDC_STAT_LOADED_PALETTE (1 << 6)
+
+#define OMAP_LCDC_CTRL_LCD_EN (1 << 0)
+#define OMAP_LCDC_CTRL_LCD_TFT (1 << 7)
+#define OMAP_LCDC_CTRL_LINE_IRQ_CLR_SEL (1 << 10)
+
+#define OMAP_LCDC_IRQ_VSYNC (1 << 2)
+#define OMAP_LCDC_IRQ_DONE (1 << 3)
+#define OMAP_LCDC_IRQ_LOADED_PALETTE (1 << 4)
+#define OMAP_LCDC_IRQ_LINE_NIRQ (1 << 5)
+#define OMAP_LCDC_IRQ_LINE (1 << 6)
+#define OMAP_LCDC_IRQ_MASK (((1 << 5) - 1) << 2)
+
+#define MAX_PALETTE_SIZE PAGE_SIZE
+
+enum lcdc_load_mode {
+ OMAP_LCDC_LOAD_PALETTE,
+ OMAP_LCDC_LOAD_FRAME,
+ OMAP_LCDC_LOAD_PALETTE_AND_FRAME
+};
+
+static struct omap_lcd_controller {
+ enum fb_update_mode update_mode;
+ unsigned int irq_mask;
+ struct completion last_frame_complete;
+ struct completion palette_load_complete;
+ struct clk *lcd_ck;
+} omap_lcdc;
+
+static void inline enable_irqs(int mask)
+{
+ omap_lcdc.irq_mask |= mask;
+}
+
+static void inline disable_irqs(int mask)
+{
+ omap_lcdc.irq_mask &= ~mask;
+}
+
+static void set_load_mode(enum lcdc_load_mode mode)
+{
+ u32 l;
+
+ l = omap_readl(OMAP_LCDC_CONTROL);
+ l &= ~(3 << 20);
+ switch (mode) {
+ case OMAP_LCDC_LOAD_PALETTE:
+ l |= 1 << 20;
+ break;
+ case OMAP_LCDC_LOAD_FRAME:
+ l |= 2 << 20;
+ break;
+ case OMAP_LCDC_LOAD_PALETTE_AND_FRAME:
+ break;
+ default:
+ BUG();
+ }
+ omap_writel(l, OMAP_LCDC_CONTROL);
+}
+
+static void enable_controller(void)
+{
+ u32 l;
+
+ l = omap_readl(OMAP_LCDC_CONTROL);
+ l |= OMAP_LCDC_CTRL_LCD_EN;
+ l &= ~OMAP_LCDC_IRQ_MASK;
+ l |= omap_lcdc.irq_mask | OMAP_LCDC_IRQ_DONE; /* enabled IRQs */
+ omap_writel(l, OMAP_LCDC_CONTROL);
+}
+
+static void disable_controller_async(void)
+{
+ u32 l;
+ u32 mask;
+
+ init_completion(&omap_lcdc.last_frame_complete);
+ l = omap_readl(OMAP_LCDC_CONTROL);
+ mask = OMAP_LCDC_CTRL_LCD_EN | OMAP_LCDC_IRQ_MASK;
+ /* Preserve the DONE mask, since we still want to get the
+ * final DONE irq. It will be disabled in the IRQ handler.
+ */
+ mask &= ~OMAP_LCDC_IRQ_DONE;
+ l &= ~mask;
+ omap_writel(l, OMAP_LCDC_CONTROL);
+}
+
+static void last_frame_timeout(unsigned long data)
+{
+ printk(KERN_ERR "omap_lcdc: timeout waiting for DONE flag\n");
+ complete(&omap_lcdc.last_frame_complete);
+}
+
+static void inline wait_for_frame_done(void)
+{
+ struct timer_list timeout;
+
+ init_timer(&timeout);
+ timeout.function = last_frame_timeout;
+ timeout.expires = jiffies + msecs_to_jiffies(500);
+ add_timer(&timeout);
+ wait_for_completion(&omap_lcdc.last_frame_complete);
+ del_timer_sync(&timeout);
+}
+
+static void disable_controller(void)
+{
+ disable_controller_async();
+ wait_for_frame_done();
+}
+
+static void reset_controller(u32 status)
+{
+ static unsigned long reset_count = 0;
+ static unsigned long last_jiffies = 0;
+
+ disable_controller_async();
+ reset_count++;
+ if (reset_count == 1 ||
+ time_after(jiffies, last_jiffies + HZ)) {
+ printk(KERN_ERR "omap_lcdc: resetting "
+ "(status %#010x,reset count %lu)\n",
+ status, reset_count);
+ last_jiffies = jiffies;
+ }
+ if (reset_count < 100) {
+ enable_controller();
+ } else {
+ reset_count = 0;
+ printk(KERN_ERR "omap_lcdc: too many reset attempts, "
+ "giving up.\n");
+ }
+}
+
+/* Configure the LCD DMA according to the current mode specified by parameters
+ * in fbdev and fbdev->var.
+ */
+static void setup_lcd_dma(struct omapfb_device *fbdev)
+{
+ static const int dma_elem_type[] = {
+ 0,
+ OMAP_DMA_DATA_TYPE_S8,
+ OMAP_DMA_DATA_TYPE_S16,
+ 0,
+ OMAP_DMA_DATA_TYPE_S32,
+ };
+ struct fb_var_screeninfo *var = &fbdev->fb_info->var;
+ unsigned long src;
+ int esize, xelem, yelem;
+
+ src = fbdev->lcddma_handle + fbdev->vis_frame_org + fbdev->view_org;
+ switch (var->rotate) {
+ case 0:
+ esize = fbdev->mirror || (src & 3) ? 2 : 4;
+ xelem = var->xres * var->bits_per_pixel / 8 / esize;
+ yelem = var->yres;
+ break;
+ case 90:
+ case 180:
+ case 270:
+ esize = 2;
+ xelem = var->xres * var->bits_per_pixel / 16;
+ yelem = var->yres;
+ break;
+ default:
+ BUG();
+ return;
+ }
+ DBGPRINT(1, "setup_dma: src=%#010x esize=%d xelem=%d yelem=%d\n",
+ src, esize, xelem, yelem);
+ omap_set_lcd_dma_b1(src, xelem, yelem, dma_elem_type[esize]);
+ omap_set_lcd_dma_single_transfer(0);
+ if (!cpu_is_omap1510()) {
+ /* Set virtual xres elem size */
+ omap_set_lcd_dma_b1_vxres(
+ fbdev->fb_info->fix.line_length / esize);
+ /* Setup transformations */
+ omap_set_lcd_dma_b1_rotation(var->rotate);
+ omap_set_lcd_dma_b1_mirror(fbdev->mirror);
+ omap_set_lcd_dma_b1_scale(fbdev->xscale, fbdev->yscale);
+ }
+ omap_setup_lcd_dma();
+}
+
+static irqreturn_t lcdc_irq_handler(int irq, void *dev_id,
+ struct pt_regs *fp)
+{
+ u32 status;
+
+ status = omap_readl(OMAP_LCDC_STATUS);
+
+ if (status & (OMAP_LCDC_STAT_FUF | OMAP_LCDC_STAT_SYNC_LOST))
+ reset_controller(status);
+ else {
+ if (status & OMAP_LCDC_STAT_DONE) {
+ u32 l;
+
+ /* Disable IRQ_DONE. The status bit will be cleared
+ * only when the controller is reenabled and we don't
+ * want to get more interrupts.
+ */
+ l = omap_readl(OMAP_LCDC_CONTROL);
+ l &= ~OMAP_LCDC_IRQ_DONE;
+ omap_writel(l, OMAP_LCDC_CONTROL);
+ complete(&omap_lcdc.last_frame_complete);
+ }
+ if (status & OMAP_LCDC_STAT_LOADED_PALETTE) {
+ disable_controller_async();
+ complete(&omap_lcdc.palette_load_complete);
+ }
+ }
+
+ /* Clear these interrupt status bits.
+ * Sync_lost, FUF bits were cleared by disabling the LCD controller
+ * LOADED_PALETTE can be cleared this way only in palette only
+ * load mode. In other load modes it's cleared by disabling the
+ * controller.
+ */
+ status &= ~(OMAP_LCDC_STAT_VSYNC |
+ OMAP_LCDC_STAT_LOADED_PALETTE |
+ OMAP_LCDC_STAT_ABC |
+ OMAP_LCDC_STAT_LINE_INT);
+ omap_writel(status, OMAP_LCDC_STATUS);
+ return IRQ_HANDLED;
+}
+
+/* Change to a new video mode. We defer this to a later time to avoid any
+ * flicker and not to mess up the current LCD DMA context. For this we disable
+ * the LCD controler, which will generate a DONE irq after the last frame has
+ * been transferred. Then it'll be safe to reconfigure both the LCD controller
+ * as well as the LCD DMA.
+ */
+static void omap_lcdc_change_mode(struct omapfb_device *fbdev)
+{
+ DBGENTER(1);
+
+ omap_stop_lcd_dma();
+ disable_controller();
+ setup_lcd_dma(fbdev);
+ enable_controller();
+
+ DBGLEAVE(1);
+}
+
+/* Configure the LCD DMA for a palette load operation and do the palette
+ * downloading synchronously. We don't use the frame+palette load mode of
+ * the controller, since the palette can always be downloaded seperately.
+ */
+static void load_palette(struct omapfb_device *fbdev)
+{
+ u8 *palette;
+ u32 code;
+ unsigned long size;
+ unsigned long palette_org;
+
+ DBGENTER(1);
+
+ switch (fbdev->panel->video_mode->bpp) {
+ case 1:
+ /* 0 is already set */
+ code = 0;
+ size = 256;
+ break;
+ case 2:
+ code = 0x1000;
+ size = 256;
+ break;
+ case 4:
+ code = 0x2000;
+ size = 256;
+ break;
+ case 8:
+ code = 0x3000;
+ size = 256;
+ break;
+ case 12:
+ case 16:
+ code = 0x4000;
+ size = 32;
+ break;
+ default:
+ BUG();
+ return;
+ }
+ palette_org = MAX_PALETTE_SIZE - size;
+ palette = fbdev->lcddma_base + palette_org;
+ memset(palette, 0, size);
+ *(u32 *)palette = code;
+
+ omap_set_lcd_dma_b1(fbdev->lcddma_handle + palette_org,
+ size / 4 + 1, 1, OMAP_DMA_DATA_TYPE_S32);
+ omap_set_lcd_dma_single_transfer(1);
+ omap_setup_lcd_dma();
+
+ init_completion(&omap_lcdc.palette_load_complete);
+ enable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE);
+ set_load_mode(OMAP_LCDC_LOAD_PALETTE);
+ enable_controller();
+ wait_for_completion(&omap_lcdc.palette_load_complete);
+ /* The controller gets disabled in the irq handler */
+ disable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE);
+ omap_stop_lcd_dma();
+
+ DBGLEAVE(1);
+}
+
+static void inline setup_regs(struct omapfb_device *fbdev)
+{
+ u32 l;
+ struct lcdc_video_mode *mode = fbdev->panel->video_mode;
+ int tft = fbdev->panel->config & LCD_PANEL_TFT;
+ int signal_levels = fbdev->panel->signals;
+
+ l = omap_readl(OMAP_LCDC_CONTROL);
+ l &= ~OMAP_LCDC_CTRL_LCD_TFT;
+ l |= tft ? OMAP_LCDC_CTRL_LCD_TFT : 0;
+ omap_writel(l, OMAP_LCDC_CONTROL);
+
+ l = omap_readl(OMAP_LCDC_TIMING2);
+ l &= ~(((1 << 6) - 1) << 20);
+ l |= signal_levels << 20;
+ omap_writel(l, OMAP_LCDC_TIMING2);
+
+ l = mode->x_res - 1;
+ l |= (mode->hsw - 1) << 10;
+ l |= (mode->hfp - 1) << 16;
+ l |= (mode->hbp - 1) << 24;
+ omap_writel(l, OMAP_LCDC_TIMING0);
+
+ l = mode->y_res - 1;
+ l |= (mode->vsw - 1) << 10;
+ l |= mode->vfp << 16;
+ l |= mode->vbp << 24;
+ omap_writel(l, OMAP_LCDC_TIMING1);
+
+ l = omap_readl(OMAP_LCDC_TIMING2);
+ l &= ~0xff;
+
+ if (!cpu_is_omap730())
+ l |= mode->pcd;
+
+ if (machine_is_omap_perseus2()) {
+ u32 clock1, clock2;
+ int pcd;
+
+ clock1 = mode->pixel_clock * 1000;
+ clock2 = clk_get_rate(omap_lcdc.lcd_ck);
+
+ if (clock1 != 0) {
+ pcd = clock2 / clock1;
+ if (pcd > 255)
+ pcd = 0;
+ } else {
+ pcd = 0;
+ }
+
+ if (pcd == 0)
+ l |= mode->pcd;
+ else
+ l |= pcd;
+
+// printk("%% ck1: %d ck2: %d pcd: %d %%\n",clock1, clock2, pcd);
+ }
+
+ l |= mode->acb << 8;
+ if (mode->flags & OMAP_LCDC_INV_PIX_CLOCK)
+ l |= 1 << 22;
+ omap_writel(l, OMAP_LCDC_TIMING2);
+}
+
+/* Configure the LCD controller, download the color palette and start a looped
+ * DMA transfer of the frame image data. */
+static int omap_lcdc_set_update_mode(struct omapfb_device *fbdev,
+ enum fb_update_mode mode)
+{
+ int r = 0;
+
+ DBGENTER(1);
+
+ if (mode != omap_lcdc.update_mode) {
+ switch (mode) {
+ case FB_AUTO_UPDATE:
+ setup_regs(fbdev);
+ load_palette(fbdev);
+
+ /* Setup and start LCD DMA */
+ setup_lcd_dma(fbdev);
+
+ set_load_mode(OMAP_LCDC_LOAD_FRAME);
+ enable_irqs(OMAP_LCDC_IRQ_DONE);
+ /* This will start the actual DMA transfer */
+ enable_controller();
+ omap_lcdc.update_mode = mode;
+ break;
+ case FB_UPDATE_DISABLED:
+ disable_controller();
+ omap_stop_lcd_dma();
+ omap_lcdc.update_mode = mode;
+ break;
+ default:
+ r = -EINVAL;
+ }
+ }
+
+ DBGLEAVE(1);
+ return r;
+}
+
+static enum fb_update_mode omap_lcdc_get_update_mode(struct omapfb_device *fbdev)
+{
+ return omap_lcdc.update_mode;
+}
+
+static void omap_lcdc_suspend(struct omapfb_device *fbdev)
+{
+ if (omap_lcdc.update_mode == FB_AUTO_UPDATE) {
+ disable_controller();
+ omap_stop_lcd_dma();
+ }
+}
+
+static void omap_lcdc_resume(struct omapfb_device *fbdev)
+{
+ if (omap_lcdc.update_mode == FB_AUTO_UPDATE) {
+ setup_regs(fbdev);
+ load_palette(fbdev);
+ setup_lcd_dma(fbdev);
+ set_load_mode(OMAP_LCDC_LOAD_FRAME);
+ enable_irqs(OMAP_LCDC_IRQ_DONE);
+ enable_controller();
+ }
+}
+
+static int omap_lcdc_init(struct omapfb_device *fbdev)
+{
+ int r;
+ u32 l;
+ int rate;
+ struct clk *tc_ck;
+
+ DBGENTER(1);
+
+ omap_lcdc.irq_mask = 0;
+
+ l = 0;
+ omap_writel(l, OMAP_LCDC_CONTROL);
+
+ /* FIXME:
+ * According to errata some platforms have a clock rate limitiation
+ */
+ omap_lcdc.lcd_ck = clk_get(NULL, "lcd_ck");
+ if (IS_ERR(omap_lcdc.lcd_ck)) {
+ printk(KERN_ERR "omap_lcdc: unable to access LCD clock\n");
+ r = PTR_ERR(omap_lcdc.lcd_ck);
+ goto fail0;
+ }
+
+ tc_ck = clk_get(NULL, "tc_ck");
+ if (IS_ERR(tc_ck)) {
+ printk(KERN_ERR "omap_lcdc: unable to access TC clock\n");
+ r = PTR_ERR(tc_ck);
+ goto fail1;
+ }
+
+ rate = clk_get_rate(tc_ck);
+ clk_put(tc_ck);
+
+ if (machine_is_omap_h3())
+ rate /= 3;
+ r = clk_set_rate(omap_lcdc.lcd_ck, rate);
+ if (r) {
+ printk(KERN_ERR "omap_lcdc: failed to adjust LCD rate\n");
+ goto fail1;
+ }
+ clk_use(omap_lcdc.lcd_ck);
+
+ r = request_irq(OMAP_LCDC_IRQ, lcdc_irq_handler, 0, "omap-lcdc", fbdev);
+ if (r) {
+ printk(KERN_ERR "omap_lcdc: unable to get IRQ\n");
+ goto fail2;
+ }
+
+ r = omap_request_lcd_dma(NULL, NULL);
+ if (r) {
+ printk(KERN_ERR "omap_lcdc: unable to get LCD DMA\n");
+ goto fail3;
+ }
+
+ printk(KERN_INFO "OMAP LCD controller initialized.\n");
+ DBGLEAVE(1);
+ return 0;
+fail3:
+ free_irq(OMAP_LCDC_IRQ, fbdev);
+fail2:
+ clk_unuse(omap_lcdc.lcd_ck);
+fail1:
+ clk_put(omap_lcdc.lcd_ck);
+fail0:
+ DBGLEAVE(1);
+ return r;
+}
+
+static void omap_lcdc_cleanup(struct omapfb_device *fbdev)
+{
+ omap_free_lcd_dma();
+ free_irq(OMAP_LCDC_IRQ, fbdev);
+ clk_unuse(omap_lcdc.lcd_ck);
+ clk_put(omap_lcdc.lcd_ck);
+}
+
+static void omap_lcdc_get_mem_layout(struct omapfb_device *fbdev,
+ unsigned long *size, unsigned long *fb_org)
+{
+ struct lcdc_video_mode *mode = fbdev->panel->video_mode;
+
+ *size = MAX_PALETTE_SIZE;
+ *fb_org = *size;
+ *size += mode->x_res * mode->bpp / 8 * mode->y_res;
+}
+
+static unsigned long omap_lcdc_get_caps(struct omapfb_device *fbdev)
+{
+ return 0;
+}
+
+struct lcd_ctrl omapfb_lcdc_ctrl = {
+ .name = "internal",
+ .init = omap_lcdc_init,
+ .cleanup = omap_lcdc_cleanup,
+ .get_mem_layout = omap_lcdc_get_mem_layout,
+ .get_caps = omap_lcdc_get_caps,
+ .set_update_mode = omap_lcdc_set_update_mode,
+ .get_update_mode = omap_lcdc_get_update_mode,
+ .update_window = NULL,
+ .suspend = omap_lcdc_suspend,
+ .resume = omap_lcdc_resume,
+ .change_mode = omap_lcdc_change_mode,
+};
+
+MODULE_DESCRIPTION("TI OMAP LCDC controller");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * File: drivers/video/omap_new/omapfb.c
+ *
+ * Framebuffer driver for TI OMAP boards
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __OMAPFB_H
+#define __OMAPFB_H
+
+/* IOCTL commands. */
+
+#define OMAP_IOW(num, dtype) _IOW('O', num, dtype)
+#define OMAP_IOR(num, dtype) _IOR('O', num, dtype)
+#define OMAP_IOWR(num, dtype) _IOWR('O', num, dtype)
+#define OMAP_IO(num) _IO('O', num)
+
+#define OMAPFB_FILLRECT OMAP_IOW(0, struct fb_fillrect)
+#define OMAPFB_COPYAREA OMAP_IOW(1, struct fb_copyarea)
+#define OMAPFB_IMAGEBLIT OMAP_IOW(2, struct fb_image)
+
+#define OMAPFB_TRANSPARENT_BLIT OMAP_IOW(30, struct fb_image)
+#define OMAPFB_MIRROR OMAP_IOW(31, int)
+#define OMAPFB_SCALE OMAP_IOW(32, struct fb_scale)
+#define OMAPFB_SELECT_VIS_FRAME OMAP_IOW(33, int)
+#define OMAPFB_SELECT_SRC_FRAME OMAP_IOW(34, int)
+#define OMAPFB_SELECT_DST_FRAME OMAP_IOW(35, int)
+#define OMAPFB_GET_FRAME_OFFSET OMAP_IOWR(36, struct fb_frame_offset)
+#define OMAPFB_SYNC_GFX OMAP_IO(37)
+#define OMAPFB_VSYNC OMAP_IO(38)
+#define OMAPFB_LATE_ACTIVATE OMAP_IO(39)
+#define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, enum fb_update_mode)
+#define OMAPFB_UPDATE_WINDOW OMAP_IOW(41, struct fb_update_window)
+#define OMAPFB_GET_CAPS OMAP_IOR(42, unsigned long)
+#define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, enum fb_update_mode)
+#define OMAPFB_GET_GFX_STATUS OMAP_IOR(44, unsigned long)
+
+#define FBCAPS_GENERIC_MASK 0x00000fff
+#define FBCAPS_LCDC_MASK 0x00fff000
+#define FBCAPS_PANEL_MASK 0xff000000
+
+#define FBCAPS_MANUAL_UPDATE 0x00001000
+#define FBCAPS_SET_BACKLIGHT 0x01000000
+
+enum omapfb_gfx_status {
+ OMAPFB_GFX_STATUS_OK = 0,
+ OMAPFB_GFX_STATUS_CHANGED
+};
+
+#define OMAPFB_UPDATE_FAILED 0x01
+#define OMAPFB_FILLRECT_FAILED 0x02
+#define OMAPFB_COPYAREA_FAILED 0x04
+#define OMAPFB_IMGBLIT_FAILED 0x08
+
+struct fb_copyarea_ext {
+ __u32 dx;
+ __u32 dy;
+ __u32 width;
+ __u32 height;
+ __u32 sx;
+ __u32 sy;
+ __u32 trans_color;
+ __u32 rev_dir;
+};
+
+struct fb_scale {
+ unsigned int xscale, yscale;
+};
+
+struct fb_frame_offset {
+ unsigned int idx;
+ unsigned long offset;
+};
+
+struct fb_update_window {
+ unsigned int x, y;
+ unsigned int width, height;
+};
+
+enum fb_update_mode {
+ FB_UPDATE_DISABLED = 0,
+ FB_AUTO_UPDATE,
+ FB_MANUAL_UPDATE
+};
+
+#ifdef __KERNEL__
+
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+
+#define OMAPFB_DEVICE "omapfb"
+#define OMAPFB_DRIVER "omapfb"
+
+#define PRNERR(fmt, args...) printk(KERN_ERR OMAPFB_DRIVER ": " fmt, ## args)
+
+#define GFX_FIFO_SIZE 2
+
+#define LCD_PANEL_TFT 0x01
+
+#define OMAP_LCDC_INV_VSYNC 0x01
+#define OMAP_LCDC_INV_HSYNC 0x02
+#define OMAP_LCDC_INV_PIX_CLOCK 0x04
+#define OMAP_LCDC_INV_OUTPUT_EN 0x08
+#define OMAP_LCDC_HSVS_RISING_EDGE 0x10
+#define OMAP_LCDC_HSVS_OPPOSITE 0x20
+
+struct lcdc_video_mode {
+ u16 x_res, y_res;
+ u32 pixel_clock; /* In kHz */
+ int bpp;
+ u8 hsw; /* Horizontal synchronization pulse width */
+ u8 hfp; /* Horizontal front porch */
+ u8 hbp; /* Horizontal back porch */
+ u8 vsw; /* Vertical synchronization pulse width */
+ u8 vfp; /* Vertical front porch */
+ u8 vbp; /* Vertical back porch */
+ u8 acb; /* ac-bias pin frequency */
+ u8 pcd; /* Pixel clock divider (this will change) */
+ u8 flags;
+};
+
+struct lcd_panel {
+ const char *name;
+ int config;
+ int signals;
+ struct lcdc_video_mode *video_mode;
+
+ int (*init) (struct lcd_panel *panel);
+ void (*cleanup) (struct lcd_panel *panel);
+ int (*enable) (struct lcd_panel *panel);
+ void (*disable) (struct lcd_panel *panel);
+ unsigned long (*get_caps)(struct lcd_panel *panel);
+ int (*set_bklight_level)(struct lcd_panel *panel,
+ unsigned int level);
+ unsigned int (*get_bklight_level)(struct lcd_panel *panel);
+ unsigned int (*get_bklight_max) (struct lcd_panel *panel);
+};
+
+struct omapfb_device;
+
+struct lcd_ctrl {
+ const char *name;
+ void *data;
+ int (*init) (struct omapfb_device *fbdev);
+ void (*cleanup) (struct omapfb_device *fbdev);
+ void (*get_mem_layout) (struct omapfb_device *fbdev,
+ unsigned long *size,
+ unsigned long *fb_org);
+ unsigned long (*get_caps) (struct omapfb_device *fbdev);
+ int (*set_update_mode)(struct omapfb_device *fbdev,
+ enum fb_update_mode mode);
+ enum fb_update_mode (*get_update_mode)(struct omapfb_device *fbdev);
+ int (*update_window) (struct omapfb_device *fbdev,
+ struct fb_update_window *win);
+ void (*suspend) (struct omapfb_device *fbdev);
+ void (*resume) (struct omapfb_device *fbdev);
+ void (*change_mode) (struct omapfb_device *fbdev);
+};
+
+enum omapfb_state {
+ OMAPFB_DISABLED = 0,
+ OMAPFB_SUSPENDED= 99,
+ OMAPFB_ACTIVE = 100
+};
+
+struct gfx_lchannel {
+ int lch_num;
+ struct gfx_lchannel *next, *prev;
+};
+
+struct gfx_dma {
+ spinlock_t spinlock;
+
+ struct completion sync_complete; /* Signalled when the
+ fifo gets empty */
+ volatile int done; /* Indicates the
+ end of a DMA chain
+ transfer */
+ struct gfx_lchannel fifo[GFX_FIFO_SIZE];
+ struct gfx_lchannel *f_head, *f_tail; /* Process and insert
+ points on the
+ fifo */
+ struct gfx_lchannel *f_chain_end; /* Points to the new
+ chain end */
+ struct semaphore f_free; /* # of free lch-s */
+ int f_run; /* # of active lch-s */
+ int f_wait; /* # of lch-s
+ waiting */
+ struct tasklet_struct dequeue_tasklet; /* Processes new DMA
+ chain transfers on
+ the fifo */
+};
+
+#define OMAPFB_RQUEUE_SIZE 20
+
+struct omapfb_fillrect_params
+{
+ struct fb_info *fbi;
+ struct fb_fillrect rect;
+};
+
+struct omapfb_copyarea_params
+{
+ struct fb_info *fbi;
+ struct fb_copyarea_ext area;
+};
+
+struct omapfb_update_window_params
+{
+ struct fb_info *fbi;
+ struct fb_update_window win;
+};
+
+struct omapfb_imageblit_params
+{
+ struct fb_info *fbi;
+ struct fb_image image;
+ int flags;
+};
+
+union req_params
+{
+ /* All possible requests are to be listed here */
+ struct omapfb_fillrect_params fillrect;
+ struct omapfb_copyarea_params copyarea;
+ struct omapfb_update_window_params update_window;
+ struct omapfb_imageblit_params imageblit;
+};
+
+struct omapfb_request
+{
+ struct list_head entry;
+ int (*function)(void *par);
+ union req_params par;
+};
+
+struct omapfb_rqueue
+{
+ spinlock_t lock;
+ struct list_head free_list;
+ struct list_head pending_list;
+ struct completion rqueue_empty;
+ struct semaphore free_sema;
+ struct omapfb_request req_pool[OMAPFB_RQUEUE_SIZE];
+ struct work_struct work;
+ unsigned long status;
+};
+
+struct omapfb_device {
+ int state;
+ int ext_lcdc; /* Using external
+ LCD controller */
+ void *lcddma_base; /* MPU virtual
+ address */
+ dma_addr_t lcddma_handle; /* Bus physical
+ address */
+ unsigned long lcddma_mem_size;
+ unsigned long palette_org; /* Palette offset into
+ lcddma_base/handle */
+ unsigned long frame0_org, frame1_org; /* Frame offsets for
+ back and front
+ frame buffers into
+ lcddma_base/handle */
+ unsigned long vis_frame_org; /* Offset of visible
+ frame buffer.
+ = frame0/1_org */
+ unsigned long src_frame_org; /* Offset of source
+ frame for drawing
+ operations.
+ = frame0/1_org */
+ unsigned long dst_frame_org; /* Offset of dest
+ frame for drawing
+ operations.
+ = frame0/1_org */
+ unsigned long view_org; /* View offset into
+ lcddma_base/handle+
+ vis_frame_org.
+ Used for panning */
+ unsigned long palette_size;
+ int xscale, yscale, mirror; /* transformations.
+ rotate is stored in
+ fb_info->var */
+
+ u32 pseudo_palette[17];
+
+ struct gfx_dma gfx; /* Accelerator */
+ struct omapfb_rqueue rqueue;
+ struct lcd_panel *panel; /* LCD panel */
+ struct lcd_ctrl *ctrl; /* LCD controller */
+
+ struct fb_info *fb_info; /* Linux fbdev
+ framework data */
+ struct device *dev;
+};
+
+extern struct lcd_panel h3_panel;
+extern struct lcd_panel h2_panel;
+extern struct lcd_panel p2_panel;
+extern struct lcd_panel osk_panel;
+extern struct lcd_panel innovator1610_panel;
+extern struct lcd_panel innovator1510_panel;
+
+extern struct lcd_ctrl omapfb_lcdc_ctrl;
+
+#endif /* __KERNEL__ */
+
+#endif /* __OMAPFB_H */
--- /dev/null
+/*
+ * File: drivers/video/omap/omapfb_main.c
+ *
+ * Framebuffer driver for TI OMAP boards
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * Acknowledgements:
+ * Alex McMains <aam@ridgerun.com> - Original driver
+ * Juha Yrjola <juha.yrjola@nokia.com> - Original driver and improvements
+ * Dirk Behme <dirk.behme@de.bosch.com> - changes for 2.6 kernel API
+ * Texas Instruments - H3 support
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/dma.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/board.h>
+
+#include "omapfb.h"
+
+// #define OMAPFB_DBG_FIFO 1
+// #define OMAPFB_DBG 1
+
+#include "debug.h"
+
+#define COPY_MODE_REV_DIR 0x01
+#define COPY_MODE_TRANSPARENT 0x02
+#define COPY_MODE_IMAGE 0x04
+
+#ifdef OMAPFB_DBG_FIFO
+struct gfx_stat {
+ unsigned long f_run[GFX_FIFO_SIZE];
+} stat;
+#endif
+
+static unsigned int def_accel;
+static unsigned long def_vram;
+static long def_vxres;
+static long def_vyres;
+static unsigned int def_rotate;
+static unsigned int def_mirror;
+
+#ifdef CONFIG_FB_OMAP_MANUAL_UPDATE
+static int manual_update = 1;
+#else
+static int manual_update;
+#endif
+
+static struct caps_table_struct {
+ unsigned long flag;
+ const char *name;
+} omapfb_caps_table[] = {
+ { FBCAPS_MANUAL_UPDATE, "manual update" },
+ { FBCAPS_SET_BACKLIGHT, "backlight setting" },
+};
+
+/*
+ * ---------------------------------------------------------------------------
+ * LCD panel
+ * ---------------------------------------------------------------------------
+ */
+static struct lcd_panel *panels[] = {
+#ifdef CONFIG_MACH_OMAP_H2
+ &h2_panel,
+#endif
+#ifdef CONFIG_MACH_OMAP_H3
+ &h3_panel,
+#endif
+#ifdef CONFIG_MACH_OMAP_PERSEUS2
+ &p2_panel,
+#endif
+#ifdef CONFIG_MACH_OMAP_OSK
+ &osk_panel,
+#endif
+#ifdef CONFIG_MACH_OMAP_INNOVATOR
+#ifdef CONFIG_ARCH_OMAP1510
+ &innovator1510_panel,
+#endif
+#ifdef CONFIG_ARCH_OMAP16XX
+ &innovator1610_panel,
+#endif
+#endif
+};
+
+static struct lcd_ctrl *ctrls[] = {
+#ifdef CONFIG_FB_OMAP_INTERNAL_LCDC
+ &omapfb_lcdc_ctrl,
+#endif
+};
+
+static struct omapfb_request *omapfb_rqueue_alloc_req(struct omapfb_rqueue *rq)
+{
+ struct omapfb_request *req;
+
+ down(&rq->free_sema);
+ spin_lock(&rq->lock);
+ req = list_entry(rq->free_list.next, struct omapfb_request, entry);
+ list_del(&req->entry);
+ spin_unlock(&rq->lock);
+ return req;
+}
+
+static void __omapfb_rqueue_free_req(struct omapfb_rqueue *rq,
+ struct omapfb_request *req)
+{
+ list_add(&req->entry, &rq->free_list);
+ up(&rq->free_sema);
+}
+
+static void omapfb_rqueue_process(void *data)
+{
+ struct omapfb_rqueue *rq = data;
+
+ spin_lock(&rq->lock);
+ while (!list_empty(&rq->pending_list)) {
+ struct omapfb_request *req;
+
+ req = list_entry(rq->pending_list.next,
+ struct omapfb_request, entry);
+ list_del(&req->entry);
+ spin_unlock(&rq->lock);
+ rq->status |= req->function(&req->par);
+ spin_lock(&rq->lock);
+ __omapfb_rqueue_free_req(rq, req);
+ }
+ complete(&rq->rqueue_empty);
+ spin_unlock(&rq->lock);
+}
+
+static void omapfb_rqueue_schedule_req(struct omapfb_rqueue *rq,
+ struct omapfb_request *req)
+{
+ spin_lock(&rq->lock);
+ list_add_tail(&req->entry, &rq->pending_list);
+ spin_unlock(&rq->lock);
+ schedule_work(&rq->work);
+}
+
+static void omapfb_rqueue_sync(struct omapfb_rqueue *rq)
+{
+ int wait = 0;
+
+ spin_lock(&rq->lock);
+ if (!list_empty(&rq->pending_list)) {
+ wait = 1;
+ init_completion(&rq->rqueue_empty);
+ }
+ spin_unlock(&rq->lock);
+ if (wait)
+ wait_for_completion(&rq->rqueue_empty);
+}
+
+static void omapfb_rqueue_reset(struct omapfb_rqueue *rq, unsigned long *status)
+{
+ omapfb_rqueue_sync(rq);
+ spin_lock(&rq->lock);
+ *status = rq->status;
+ rq->status = 0;
+ spin_unlock(&rq->lock);
+}
+
+static void omapfb_rqueue_init(struct omapfb_rqueue *rq)
+{
+ int i;
+
+ spin_lock_init(&rq->lock);
+ sema_init(&rq->free_sema, OMAPFB_RQUEUE_SIZE);
+ init_completion(&rq->rqueue_empty);
+ INIT_WORK(&rq->work, omapfb_rqueue_process, rq);
+ INIT_LIST_HEAD(&rq->free_list);
+ INIT_LIST_HEAD(&rq->pending_list);
+ for (i = 0; i < OMAPFB_RQUEUE_SIZE; i++)
+ list_add(&rq->req_pool[i].entry, &rq->free_list);
+}
+
+static void omapfb_rqueue_cleanup(struct omapfb_rqueue *rq)
+{
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * gfx DMA
+ * ---------------------------------------------------------------------------
+ */
+/* Get a new logical channel from the gfx fifo. */
+static void inline gfxdma_get_lch(struct gfx_dma *gfx, int *lch)
+{
+ DBGENTER(3);
+
+ down(&gfx->f_free);
+
+ spin_lock_bh(&gfx->spinlock);
+
+ *lch = gfx->f_tail->lch_num;
+ gfx->f_tail = gfx->f_tail->next;
+
+ spin_unlock_bh(&gfx->spinlock);
+
+ DBGLEAVE(3);
+}
+
+/* Set basic transfer params for the logical channel */
+static inline void gfxdma_set_lch_params(int lch, int data_type,
+ int enumber, int fnumber,
+ unsigned long src_start, int src_amode,
+ unsigned long dst_start, int dst_amode)
+{
+ omap_set_dma_transfer_params(lch, data_type, enumber, fnumber, 0);
+ omap_set_dma_src_params(lch, OMAP_DMA_PORT_EMIFF,
+ src_amode, src_start);
+ omap_set_dma_dest_params(lch, OMAP_DMA_PORT_EMIFF,
+ dst_amode, dst_start);
+}
+
+/* Set element and frame indexes for the logical channel, to support
+ * image transformations
+ */
+static inline void gfxdma_set_lch_index(int lch, int src_eidx, int src_fidx,
+ int dst_eidx, int dst_fidx)
+{
+ omap_set_dma_src_index(lch, src_eidx, src_fidx);
+ omap_set_dma_dest_index(lch, dst_eidx, dst_fidx);
+}
+
+/* Set color parameter for the logical channel, to support constant fill and
+ * transparent copy operations
+ */
+static inline void gfxdma_set_lch_color(int lch, u32 color,
+ enum omap_dma_color_mode mode)
+{
+ omap_set_dma_color_mode(lch, mode, color);
+}
+
+
+/* Start a new transfer consisting of a single DMA logical channel,
+ * or a chain (f_run > 1). Can be called in interrupt context.
+ * gfx->spinlock must be held.
+ */
+static void inline gfxdma_start_chain(struct gfx_dma *gfx)
+{
+ DBGENTER(3);
+
+ gfx->f_run = gfx->f_wait;
+#ifdef OMAPFB_DBG_FIFO
+ stat.f_run[gfx->f_run - 1]++;
+#endif
+ gfx->f_wait = 0;
+ omap_enable_dma_irq(gfx->f_chain_end->lch_num, OMAP_DMA_BLOCK_IRQ);
+ /* Let it go */
+ DBGPRINT(1, "start %d\n", gfx->f_head->lch_num);
+ omap_start_dma(gfx->f_head->lch_num);
+ gfx->f_chain_end = gfx->f_chain_end->next;
+
+ DBGLEAVE(3);
+}
+
+/* Enqueue a logical channel, that has been set up. If no other transfers
+ * are pending start this new one right away. */
+static void inline gfxdma_enqueue(struct gfx_dma *gfx, int lch)
+{
+ DBGENTER(3);
+
+ spin_lock_bh(&gfx->spinlock);
+ DBGPRINT(3, "run:%d wait:%d\n", gfx->f_run, gfx->f_wait);
+ if (gfx->f_wait) {
+ DBGPRINT(1, "link %d, %d\n", gfx->f_chain_end->lch_num, lch);
+ omap_dma_link_lch(gfx->f_chain_end->lch_num, lch);
+ gfx->f_chain_end = gfx->f_chain_end->next;
+ }
+ omap_disable_dma_irq(lch, OMAP_DMA_BLOCK_IRQ);
+
+ gfx->f_wait++;
+
+ if (!gfx->f_run)
+ gfxdma_start_chain(gfx);
+ spin_unlock_bh(&gfx->spinlock);
+
+ DBGLEAVE(3);
+}
+
+/* Called by DMA core when the last transfer ended, or there is an error
+ * condition. We dispatch handling of the end of transfer case to a tasklet.
+ * Called in interrupt context.
+ */
+static void gfxdma_handler(int lch, u16 ch_status, void *data)
+{
+ struct gfx_dma *gfx = (struct gfx_dma *)data;
+ int done = 0;
+
+ DBGENTER(3);
+
+ DBGPRINT(4, "lch=%d status=%#010x\n", lch, ch_status);
+ if (unlikely(ch_status & (OMAP_DMA_TOUT_IRQ | OMAP_DMA_DROP_IRQ))) {
+ PRNERR("gfx DMA error. status=%#010x\n", ch_status);
+ done = 1;
+ } else if (likely(ch_status & OMAP_DMA_BLOCK_IRQ))
+ done = 1;
+ if (likely(done)) {
+ gfx->done = 1;
+ tasklet_schedule(&gfx->dequeue_tasklet);
+ }
+
+ DBGLEAVE(3);
+}
+
+/* Let the DMA core know that the last transfer has ended. If there are
+ * pending transfers in the fifo start them now.
+ * Called in interrupt context.
+ */
+static void gfxdma_dequeue_tasklet(unsigned long data)
+{
+ struct gfx_dma *gfx = (struct gfx_dma *)data;
+ struct gfx_lchannel *f_chain;
+
+ DBGENTER(3);
+
+ /* start an already programmed transfer
+ */
+ while (likely(gfx->done)) {
+ gfx->done = 0;
+ spin_lock(&gfx->spinlock);
+ f_chain = gfx->f_head;
+ omap_stop_dma(f_chain->lch_num);
+ /* Would be better w/o a loop.. */
+ while (gfx->f_run--) {
+ if (gfx->f_run)
+ omap_dma_unlink_lch(f_chain->lch_num,
+ f_chain->next->lch_num);
+ f_chain = f_chain->next;
+ up(&gfx->f_free);
+ }
+ gfx->f_run = 0;
+ gfx->f_head = f_chain;
+ if (likely(gfx->f_wait))
+ gfxdma_start_chain(gfx);
+ else
+ complete(&gfx->sync_complete);
+ spin_unlock(&gfx->spinlock);
+ }
+
+ DBGLEAVE(3);
+}
+
+/* Wait till any pending transfers end. */
+static void gfxdma_sync(struct gfx_dma *gfx)
+{
+ int wait = 0;
+
+ DBGENTER(1);
+
+ for (;;) {
+ spin_lock_bh(&gfx->spinlock);
+ if (gfx->f_run + gfx->f_wait) {
+ wait = 1;
+ init_completion(&gfx->sync_complete);
+ }
+ spin_unlock_bh(&gfx->spinlock);
+ if (wait) {
+ wait_for_completion(&gfx->sync_complete);
+ wait = 0;
+ } else
+ break;
+ }
+
+ DBGLEAVE(1);
+}
+
+/* Initialize the gfx DMA object.
+ * Allocate DMA logical channels according to the fifo size.
+ * Set the channel parameters that will be the same for all transfers.
+ */
+static int gfxdma_init(struct gfx_dma *gfx)
+{
+ int r = 0;
+ int i;
+
+ DBGENTER(1);
+
+ for (i = 0; i < GFX_FIFO_SIZE; i++) {
+ int next_idx;
+ int lch_num;
+
+ r = omap_request_dma(0, OMAPFB_DRIVER,
+ gfxdma_handler, gfx, &lch_num);
+ if (r) {
+ int j;
+
+ PRNERR("unable to get GFX DMA %d\n", i);
+ for (j = 0; j < i; j++)
+ omap_free_dma(lch_num);
+ r = -1;
+ goto exit;
+ }
+ omap_set_dma_src_data_pack(lch_num, 1);
+ omap_set_dma_src_burst_mode(lch_num, OMAP_DMA_DATA_BURST_4);
+ omap_set_dma_dest_data_pack(lch_num, 1);
+ omap_set_dma_dest_burst_mode(lch_num, OMAP_DMA_DATA_BURST_4);
+
+ gfx->fifo[i].lch_num = lch_num;
+
+ next_idx = i < GFX_FIFO_SIZE - 1 ? i + 1 : 0;
+ gfx->fifo[next_idx].prev = &gfx->fifo[i];
+ gfx->fifo[i].next = &gfx->fifo[next_idx];
+ }
+ gfx->f_head = gfx->f_tail = gfx->f_chain_end = &gfx->fifo[0];
+ sema_init(&gfx->f_free, GFX_FIFO_SIZE);
+
+ spin_lock_init(&gfx->spinlock);
+
+ tasklet_init(&gfx->dequeue_tasklet, gfxdma_dequeue_tasklet,
+ (unsigned long)gfx);
+
+ init_completion(&gfx->sync_complete);
+exit:
+ DBGLEAVE(1);
+ return r;
+}
+
+/* Clean up the gfx DMA object */
+static void gfxdma_cleanup(struct gfx_dma *gfx)
+{
+ int i;
+
+ DBGENTER(1);
+
+ for (i = 0; i < GFX_FIFO_SIZE; i++)
+ omap_free_dma(gfx->fifo[i].lch_num);
+
+ DBGLEAVE(1);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * LCD controller and LCD DMA
+ * ---------------------------------------------------------------------------
+ */
+/* Lookup table to map elem size to elem type. */
+static const int dma_elem_type[] = {
+ 0,
+ OMAP_DMA_DATA_TYPE_S8,
+ OMAP_DMA_DATA_TYPE_S16,
+ 0,
+ OMAP_DMA_DATA_TYPE_S32,
+};
+
+/* Allocate resources needed for LCD controller and LCD DMA operations. Video
+ * memory is allocated from system memory according to the virtual display
+ * size, except if a bigger memory size is specified explicitly as a kernel
+ * parameter.
+ */
+static int ctrl_init(struct omapfb_device *fbdev)
+{
+ unsigned long mem_size;
+ int r;
+
+ DBGENTER(1);
+
+ r = fbdev->ctrl->init(fbdev);
+ if (r < 0)
+ goto exit;
+ fbdev->ctrl->get_mem_layout(fbdev, &mem_size, &fbdev->frame0_org);
+
+ if (def_vram) {
+ if (mem_size > def_vram) {
+ PRNERR("specified frame buffer memory too small\n");
+ r = -ENOMEM;
+ goto cleanup_ctrl;
+ }
+ mem_size = def_vram;
+ }
+ fbdev->lcddma_mem_size = PAGE_SIZE << get_order(mem_size);
+ fbdev->lcddma_base = dma_alloc_writecombine(fbdev->dev,
+ fbdev->lcddma_mem_size,
+ &fbdev->lcddma_handle,
+ GFP_KERNEL);
+ if (fbdev->lcddma_base == NULL) {
+ PRNERR("unable to allocate fb DMA memory\n");
+ r = -ENOMEM;
+ goto cleanup_ctrl;
+ }
+
+ memset(fbdev->lcddma_base, 0, fbdev->lcddma_mem_size);
+
+ DBGPRINT(1, "lcddma_base=%#10x lcddma_handle=%#10x lcddma_mem_size=%d"
+ "palette_size=%d frame0_org=%d palette_org=%d\n",
+ fbdev->lcddma_base, fbdev->lcddma_handle,
+ fbdev->lcddma_mem_size,
+ fbdev->palette_size,
+ fbdev->frame0_org, fbdev->palette_org);
+
+ DBGLEAVE(1);
+ return 0;
+cleanup_ctrl:
+ fbdev->ctrl->cleanup(fbdev);
+exit:
+ DBGLEAVE(1);
+ return r;
+}
+
+static void ctrl_cleanup(struct omapfb_device *fbdev)
+{
+ fbdev->ctrl->cleanup(fbdev);
+ dma_free_writecombine(fbdev->dev, fbdev->lcddma_mem_size,
+ fbdev->lcddma_base, fbdev->lcddma_handle);
+}
+
+static void ctrl_change_mode(struct omapfb_device *fbdev)
+{
+ fbdev->ctrl->change_mode(fbdev);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * fbdev framework callbacks and the ioctl interface
+ * ---------------------------------------------------------------------------
+ */
+/* Called each time the omapfb device is opened */
+static int omapfb_open(struct fb_info *info, int user)
+{
+ DBGENTER(1);
+
+#ifdef OMAPFB_DBG_FIFO
+ memset(&stat, 0, sizeof(stat));
+#endif
+
+ DBGLEAVE(1);
+ return 0;
+}
+
+/* Called when the omapfb device is closed. We make sure that any pending
+ * gfx DMA operations are ended, before we return. */
+static int omapfb_release(struct fb_info *info, int user)
+{
+ struct omapfb_device *dev = (struct omapfb_device *)info->par;
+ int sync = 0;
+
+ DBGENTER(1);
+
+ spin_lock_bh(&dev->gfx.spinlock);
+ if (dev->gfx.f_run)
+ sync = 1;
+ spin_unlock_bh(&dev->gfx.spinlock);
+ if (sync) {
+ gfxdma_sync(&dev->gfx);
+ }
+#ifdef OMAPFB_DBG_FIFO
+ {
+ int i;
+ for (i = 0; i < GFX_FIFO_SIZE; i++)
+ printk(KERN_INFO "f_run[%d]=%lu\n", i + 1,
+ stat.f_run[i]);
+ }
+#endif
+
+ DBGLEAVE(1);
+ return 0;
+}
+
+/* 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 omapfb_setcolreg(u_int regno, u_int red, u_int green,
+ u_int blue, u_int transp,
+ struct fb_info *info)
+{
+ u16 pal;
+ int r = 0;
+
+ DBGENTER(2);
+
+ if (regno >= 16) {
+ r = -1;
+ goto exit;
+ }
+ pal = ((red >> 11) << 11) | ((green >> 10) << 5) | (blue >> 11);
+ ((u32 *)(info->pseudo_palette))[regno] = pal;
+
+exit:
+ DBGLEAVE(2);
+ return r;
+}
+
+static int omapfb_suspend(struct device *dev, pm_message_t mesg, u32 level);
+static int omapfb_resume(struct device *dev, u32 level);
+
+static int omapfb_blank(int blank, struct fb_info *info)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)info->par;
+ int r = 0;
+
+ DBGENTER(1);
+ switch (blank) {
+ case VESA_NO_BLANKING:
+ omapfb_resume(fbdev->dev, 0);
+ break;
+ case VESA_POWERDOWN:
+ omapfb_suspend(fbdev->dev, 0, 0);
+ break;
+ default:
+ r = -EINVAL;
+ }
+
+ DBGLEAVE(1);
+ return r;
+}
+
+/* Setup a constant fill DMA transfer. Destination must be elem size aligned. */
+static inline void fill_block(struct omapfb_device *fbdev,
+ unsigned long dst, unsigned long enumber,
+ unsigned long height, int esize, u32 color)
+{
+ unsigned long fidx;
+ int lch;
+
+ DBGPRINT(2, "dst:%#010x enumber:%d height:%d esize:%d color:%#010x\n",
+ dst, enumber, height, esize, color);
+ fidx = fbdev->fb_info->fix.line_length - enumber * esize + 1;
+ gfxdma_get_lch(&fbdev->gfx, &lch);
+ gfxdma_set_lch_params(lch, dma_elem_type[esize], enumber, height,
+ 0, OMAP_DMA_AMODE_CONSTANT,
+ dst, OMAP_DMA_AMODE_DOUBLE_IDX);
+ gfxdma_set_lch_index(lch, 0, 0, 1, fidx);
+ gfxdma_set_lch_color(lch, color, OMAP_DMA_CONSTANT_FILL);
+ gfxdma_enqueue(&fbdev->gfx, lch);
+
+ DUMP_DMA_REGS(lch);
+}
+
+/* Fill the specified rectangle with a solid color.
+ * ROP_XOR and bpp<8 can't be handled by the DMA hardware.
+ * When frame flipping is in effect use the destination frame.
+ * We'll make our best to use the largest possible elem size, doing the fill
+ * in more parts if alignment requires us to do so.
+ */
+static int omapfb_fillrect(void *data)
+{
+ struct omapfb_fillrect_params *par = data;
+ struct omapfb_device *fbdev = (struct omapfb_device *)par->fbi->par;
+
+ int dx = par->rect.dx, dy = par->rect.dy;
+ int vxres = par->fbi->var.xres_virtual;
+ int vyres = par->fbi->var.yres_virtual;
+ int width = par->rect.width, height = par->rect.height;
+ unsigned long dst;
+ u32 color;
+ int bpp;
+ int enumber, esize;
+ int r = 0;
+
+ DBGENTER(2);
+ bpp = par->fbi->var.bits_per_pixel;
+ /* bpp < 8 is tbd.
+ * We can't do ROP_XOR with DMA
+ * If IRQs are disabled we can't use DMA
+ */
+ if (bpp < 8 || par->rect.rop == ROP_XOR || irqs_disabled()) {
+ r = OMAPFB_FILLRECT_FAILED;
+ goto exit;
+ }
+ /* Clipping */
+ if (!width || !height || dx > vxres || dy > vyres)
+ goto exit;
+ if (dx + width > vxres)
+ width = vxres - dx;
+ if (dy + height > vyres)
+ height = vyres - dy;
+
+ if (bpp == 12)
+ bpp = 16;
+ width = width * bpp / 8;
+
+ dst = fbdev->lcddma_handle + fbdev->dst_frame_org;
+ dst += dx * bpp / 8 + dy * par->fbi->fix.line_length;
+
+ color = par->rect.color;
+ switch (bpp) {
+ case 8:
+ color |= color << 8;
+ /* Fall through */
+ case 16:
+ color |= color << 16;
+ }
+
+ if ((dst & 3) || width < 4) {
+ if (!(dst & 1) && width > 1) {
+ esize = 2;
+ enumber = 1;
+ width -= 2;
+ } else {
+ esize = 1;
+ enumber = 4 - (esize & 3);
+ if (enumber > width)
+ enumber = width;
+ width -= enumber;
+ }
+ fill_block(fbdev, dst, enumber, height, esize, color);
+ dst = (dst + 3) & ~3;
+ }
+ if (width) {
+ enumber = width / 4;
+ fill_block(fbdev, dst, enumber, height, 4, color);
+ dst += enumber * 4;
+ width -= enumber * 4;
+ }
+ if (width) {
+ if (width == 2) {
+ esize = 2;
+ enumber = 1;
+ } else {
+ esize = 1;
+ enumber = width;
+ }
+ fill_block(fbdev, dst, enumber, height, esize, color);
+ }
+
+exit:
+ DBGLEAVE(2);
+ return r;
+}
+
+static int omapfb_schedule_fillrect(struct fb_info *fbi,
+ const struct fb_fillrect *rect)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+ struct omapfb_request *req;
+
+ if ((req = omapfb_rqueue_alloc_req(&fbdev->rqueue)) == NULL)
+ return -ENOMEM;
+ req->function = omapfb_fillrect;
+ req->par.fillrect.fbi = fbi;
+ req->par.fillrect.rect = *rect;
+ omapfb_rqueue_schedule_req(&fbdev->rqueue, req);
+ return fbdev->rqueue.status ? OMAPFB_GFX_STATUS_CHANGED : 0;
+}
+
+/* Setup a gfx DMA transfer to a rectangular area.
+ * A color parameter can be specified for transparent copy.
+ * Transfer direction can be setup to use either incremental or decremental
+ * addresses.
+ * Source and destination must be elem size aligned.
+ */
+static inline void transfer_block(struct omapfb_device *fbdev,
+ unsigned long src, unsigned long dst,
+ unsigned long img_width,
+ unsigned long enumber, unsigned long height,
+ int esize, u32 trans_color, int flags)
+{
+ s16 eidx;
+ s16 s_fidx, d_fidx;
+ int lch;
+
+ eidx = 1;
+ s_fidx = img_width - enumber * esize + 1;
+ d_fidx = fbdev->fb_info->fix.line_length - enumber * esize + 1;
+ if (flags & COPY_MODE_REV_DIR) {
+ eidx = -2 * esize + 1;
+ s_fidx = -s_fidx + eidx + 1;
+ d_fidx = -d_fidx + eidx + 1;
+ }
+
+ DBGPRINT(2, "src:%#010x dst:%#010x enumber:%d height:%d "
+ "esize:%d eidx:%d s_fidx:%d d_fidx bg_color:%#010x flags:%d\n",
+ src, dst, enumber, height, esize, eidx, s_fidx, d_fidx,
+ bg_color, flags);
+
+ gfxdma_get_lch(&fbdev->gfx, &lch);
+ gfxdma_set_lch_params(lch, dma_elem_type[esize], enumber, height,
+ src, OMAP_DMA_AMODE_DOUBLE_IDX,
+ dst, OMAP_DMA_AMODE_DOUBLE_IDX);
+ gfxdma_set_lch_index(lch, eidx, s_fidx, eidx, d_fidx);
+ if (flags & COPY_MODE_TRANSPARENT)
+ gfxdma_set_lch_color(lch, trans_color,
+ OMAP_DMA_TRANSPARENT_COPY);
+ else
+ gfxdma_set_lch_color(lch, 0, OMAP_DMA_COLOR_DIS);
+ gfxdma_enqueue(&fbdev->gfx, lch);
+
+ DUMP_DMA_REGS(lch);
+}
+
+/* Copy a rectangular area or an image to another rectangular area.
+ * A color parameter can be specified for transparent copy.
+ * Transfer direction can be setup to use either incremental or decremental
+ * addresses.
+ * Currently both source and destination area must be entirely contained in
+ * frame buffer memory.
+ * The largest possible transfer elem size will be determined according to
+ * source and destination address alignment, dividing the transfer into more
+ * parts if necessary.
+ */
+static inline void copy_data(struct omapfb_device *fbdev,
+ unsigned long src, unsigned long dst,
+ unsigned long width, unsigned long height,
+ u32 trans_color, int flags)
+{
+ struct fb_info *fbi = fbdev->fb_info;
+ int esize, stripe_esize;
+ int step, rest, enumber;
+ unsigned long img_width;
+ static const int esize_arr[] = {4, 1, 2, 1};
+ int rev;
+
+ /* Check alignment constraints */
+ esize = esize_arr[(src ^ dst) & 3];
+
+ rev = flags & COPY_MODE_REV_DIR;
+ if (rev) {
+ rest = src & (esize - 1);
+ if (rest > width)
+ rest = width;
+ src -= rest ? rest : esize;
+ dst -= rest ? rest : esize;
+ } else {
+ rest = esize - (src & (esize - 1));
+ if (rest > width)
+ rest = width;
+ }
+ if (width < esize)
+ rest = width;
+
+ img_width = flags & COPY_MODE_IMAGE ? width : fbi->fix.line_length;
+
+ DBGPRINT(2, "\nrev=%d src=%#010lx dst=%#010lx \n"
+ "esize=%d width=%d rest=%d\n",
+ rev, src, dst, esize, width, rest);
+ if (rest) {
+ /* Transfer this unaligned stripe, so that afterwards
+ * we have both src and dst 16bit or 32bit aligned.
+ */
+ if (rest == 2) {
+ /* Area body is 32bit aligned */
+ stripe_esize = 2;
+ enumber = 1;
+ step = rev ? -esize : 2;
+ width -= 2;
+ } else {
+ stripe_esize = 1;
+ enumber = rest;
+ step = rev ? -esize : rest;
+ }
+ transfer_block(fbdev, src, dst, img_width, enumber, height,
+ stripe_esize, trans_color, flags);
+ src += step;
+ dst += step;
+ }
+ if (width) {
+ /* Transfer area body */
+ enumber = (width & ~(esize - 1)) / esize;
+ transfer_block(fbdev, src, dst, img_width, enumber, height,
+ esize, trans_color, flags);
+ step = enumber * esize;
+ width -= step;
+ if (rev)
+ step = -step + esize - width;
+ src += step;
+ dst += step;
+ }
+ if (width) {
+ /* Transfer the remaining unaligned stripe */
+ if (width == 2) {
+ stripe_esize = 2;
+ enumber = 1;
+ } else {
+ stripe_esize = 1;
+ enumber = width;
+ }
+ transfer_block(fbdev, src, dst, img_width, enumber, height,
+ stripe_esize, trans_color, flags);
+ }
+
+ DBGLEAVE(2);
+}
+
+/* Copy a rectangular area in the frame buffer to another rectangular area.
+ * Calculate the source and destination addresses.
+ * Transfer direction will be determined taking care of possible area
+ * overlapping.
+ * Currently both source and destination area must be entirely contained in
+ * frame buffer memory, in case of frame flipping source and destination frame
+ * respectively.
+ */
+static int omapfb_copyarea(void *data)
+{
+ struct omapfb_copyarea_params *par = data;
+ struct omapfb_device *fbdev = (struct omapfb_device *)par->fbi->par;
+
+ int width = par->area.width, height = par->area.height;
+ int sx = par->area.sx, sy = par->area.sy;
+ int dx = par->area.dx, dy = par->area.dy;
+ unsigned long dst, dst_ofs, src, src_ofs;
+ unsigned long end_ofs;
+ int bpp = par->fbi->var.bits_per_pixel;
+ int flags;
+ int r = 0;
+
+ DBGENTER(2);
+
+ if (!width || !height)
+ goto exit;
+
+ /* Bpp < 8 is tbd. If IRQs are disabled we can't use DMA */
+ if (bpp < 8 || irqs_disabled()) {
+ r = OMAPFB_COPYAREA_FAILED;
+ goto exit;
+ }
+
+ src = fbdev->lcddma_handle;
+ dst = src;
+ src_ofs = fbdev->src_frame_org + sx * bpp / 8 +
+ sy * par->fbi->fix.line_length;
+ dst_ofs = fbdev->dst_frame_org + dx * bpp / 8 +
+ dy * par->fbi->fix.line_length;
+ end_ofs = (height - 1) * par->fbi->fix.line_length + width * bpp / 8;
+ src += src_ofs;
+ dst += dst_ofs;
+
+ DBGPRINT(2, "src:%#010lx dst:%#010lx end_ofs:%#010lx\n",
+ src, dst, end_ofs);
+
+ /* Currently we support only transfers where both source and destination
+ * area is contained entirely in fbmem. This is because of DMA memory
+ * constraints.
+ */
+ if (src_ofs + end_ofs > fbdev->lcddma_mem_size ||
+ dst_ofs + end_ofs > fbdev->lcddma_mem_size) {
+ r = OMAPFB_COPYAREA_FAILED;
+ goto exit;
+ }
+
+ flags = 0;
+ if (par->area.rev_dir) {
+ flags = COPY_MODE_REV_DIR;
+ src += end_ofs;
+ dst += end_ofs;
+ }
+ if (par->area.trans_color != -1)
+ flags |= COPY_MODE_TRANSPARENT;
+
+ width = width * bpp / 8;
+ copy_data(fbdev, src, dst, width, height, par->area.trans_color, flags);
+exit:
+ DBGLEAVE(2);
+ return r;
+}
+
+static int omapfb_schedule_copyarea(struct fb_info *fbi,
+ const struct fb_copyarea_ext *area)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+ struct omapfb_request *req;
+
+ if ((req = omapfb_rqueue_alloc_req(&fbdev->rqueue)) == NULL)
+ return -ENOMEM;
+ req->function = omapfb_copyarea;
+ req->par.copyarea.fbi = fbi;
+ req->par.copyarea.area = *area;
+ omapfb_rqueue_schedule_req(&fbdev->rqueue, req);
+ return fbdev->rqueue.status ? OMAPFB_GFX_STATUS_CHANGED : 0;
+}
+
+/* Copy an image to a rectangular area in the frame buffer.
+ * A color parameter can be specified for transparent copy.
+ * Calculate the source and destination addresses.
+ * Transfer direction will be determined taking care of possible area
+ * overlapping.
+ * Currently both source and destination area must be entirely contained in
+ * frame buffer memory, in case of frame flipping source and destination frame
+ * respectively.
+ */
+static int do_imageblit(void *data)
+{
+ struct omapfb_imageblit_params *par = data;
+ struct omapfb_device *fbdev = (struct omapfb_device *)par->fbi->par;
+
+ int width = par->image.width, height = par->image.height;
+ int dx = par->image.dx, dy = par->image.dy;
+ const char *img_data = par->image.data;
+ unsigned long dst, dst_ofs;
+ unsigned long dst_end_ofs;
+ int bpp = par->fbi->var.bits_per_pixel;
+ u32 bg_color;
+ int r = 0;
+
+ DBGENTER(2);
+
+ if (!width || !height)
+ goto exit;
+
+ /* bpp conversion is not supported, let the default function handle it.
+ * Note that image->depth is either 1 for monochrome image, or equals
+ * bpp of the current video mode, so we can't rely on it.
+ * If IRQs are disabled we can't use DMA.
+ */
+ if (bpp < 8 || par->image.depth != bpp || irqs_disabled()) {
+ r = OMAPFB_IMGBLIT_FAILED;
+ goto exit;
+ }
+
+ dst = fbdev->lcddma_handle;
+ dst_ofs = fbdev->dst_frame_org +
+ dx * bpp / 8 + dy * par->fbi->fix.line_length;
+ dst_end_ofs = (height - 1) * par->fbi->fix.line_length +
+ width * bpp / 8;
+ dst += dst_ofs;
+
+ DBGPRINT(2, "data:%#010lx dst:%#010lx dst_end_ofs:%#010lx\n",
+ img_data, dst, dst_end_ofs);
+
+ /* Check that both source and destination is DMA -able */
+ if (dst_ofs + dst_end_ofs > fbdev->lcddma_mem_size) {
+ r = OMAPFB_IMGBLIT_FAILED;
+ goto exit;
+ }
+
+ if (((unsigned long)img_data < (unsigned long)fbdev->lcddma_base) ||
+ ((unsigned long)img_data + width * bpp / 8 * height >
+ (unsigned long)fbdev->lcddma_base + fbdev->lcddma_mem_size)) {
+ r = OMAPFB_IMGBLIT_FAILED;
+ goto exit;
+ }
+
+ bg_color = par->image.bg_color;
+ if (par->flags & COPY_MODE_TRANSPARENT) {
+ switch (bpp) {
+ case 8:
+ bg_color |= bg_color << 8;
+ /* Fall through */
+ case 16:
+ bg_color |= bg_color << 16;
+ }
+ }
+
+ width = width * bpp / 8;
+ copy_data(fbdev, (unsigned long)img_data, dst, width, height,
+ bg_color, par->flags | COPY_MODE_IMAGE);
+exit:
+ DBGLEAVE(2);
+ return r;
+}
+
+static int omapfb_schedule_imageblit(struct fb_info *fbi,
+ const struct fb_image *image, int flags)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+ struct omapfb_request *req;
+
+ if ((req = omapfb_rqueue_alloc_req(&fbdev->rqueue)) == NULL)
+ return -ENOMEM;
+ req->function = do_imageblit;
+ req->par.imageblit.fbi = fbi;
+ req->par.imageblit.image = *image;
+ req->par.imageblit.flags = flags;
+ omapfb_rqueue_schedule_req(&fbdev->rqueue, req);
+ return fbdev->rqueue.status ? OMAPFB_GFX_STATUS_CHANGED : 0;
+}
+
+/* Set fb_info.fix fields and also updates fbdev.
+ * When calling this fb_info.var must be set up already.
+ */
+static void set_fb_fix(struct omapfb_device *fbdev)
+{
+ struct fb_info *fbi = fbdev->fb_info;
+ struct fb_fix_screeninfo *fix = &fbi->fix;
+ struct fb_var_screeninfo *var = &fbi->var;
+ int frame_size;
+
+ strncpy(fix->id, OMAPFB_DRIVER, sizeof(fix->id));
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ switch (var->bits_per_pixel) {
+ case 16:
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ break;
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ break;
+ }
+ fix->accel = FB_ACCEL_OMAP1610;
+ fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+ fix->smem_len = fbdev->lcddma_mem_size - fbdev->frame0_org;
+ fix->smem_start = fbdev->lcddma_handle + fbdev->frame0_org;
+
+ /* Set the second frame buffer offset for flipping if there is
+ * room for it. */
+ frame_size = fix->line_length * var->yres;
+ fbdev->frame1_org = fbdev->frame0_org + frame_size;
+ if (fbdev->frame1_org + frame_size > fbdev->lcddma_mem_size)
+ fbdev->frame1_org = 0;
+ fbdev->vis_frame_org = fbdev->src_frame_org = fbdev->dst_frame_org =
+ fbdev->frame0_org;
+
+ fbdev->view_org = var->yoffset * fix->line_length +
+ var->xoffset * var->bits_per_pixel / 8;
+}
+
+/* Check the values in var against our capabilities and in case of out of
+ * bound values try to adjust them.
+ */
+static int set_fb_var(struct omapfb_device *fbdev,
+ struct fb_var_screeninfo *var)
+{
+ int bpp;
+ unsigned long max_frame_size;
+ unsigned long line_size;
+
+ bpp = var->bits_per_pixel = fbdev->panel->video_mode->bpp;
+ if (bpp != 16)
+ /* Not yet supported */
+ return -1;
+ switch (var->rotate) {
+ case 0:
+ case 180:
+ var->xres = fbdev->panel->video_mode->x_res;
+ var->yres = fbdev->panel->video_mode->y_res;
+ break;
+ case 90:
+ case 270:
+ var->xres = fbdev->panel->video_mode->y_res;
+ var->yres = fbdev->panel->video_mode->x_res;
+ break;
+ default:
+ return -1;
+ }
+ 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 = fbdev->lcddma_mem_size - fbdev->frame0_org;
+ line_size = var->xres_virtual * bpp / 8;
+ 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 / bpp;
+ if (var->xres_virtual < var->xres) {
+ /* Still doesn't fit. Shrink yres_virtual too */
+ var->xres_virtual = var->xres;
+ line_size = var->xres * bpp / 8;
+ var->yres_virtual = max_frame_size / line_size;
+ }
+ }
+ 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;
+ line_size = var->xres * bpp / 8;
+
+ 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;
+
+ var->height = -1;
+ var->width = -1;
+ var->grayscale = 0;
+ var->nonstd = 0;
+
+ /* TODO: video timing params, sync */
+ var->pixclock = -1;
+ var->left_margin = -1;
+ var->right_margin = -1;
+ var->upper_margin = -1;
+ var->lower_margin = -1;
+ var->hsync_len = -1;
+ var->vsync_len = -1;
+
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->sync = 0;
+
+ return 0;
+}
+
+static struct fb_var_screeninfo new_var;
+
+/* Set rotation (0, 90, 180, 270 degree), and switch to the new mode. */
+static void omapfb_rotate(struct fb_info *fbi, int rotate)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+
+ DBGENTER(1);
+
+ if (cpu_is_omap1510() && rotate != fbdev->fb_info->var.rotate) {
+ memcpy(&new_var, &fbi->var, sizeof(new_var));
+ new_var.rotate = rotate;
+ if (set_fb_var(fbdev, &new_var) == 0 &&
+ memcmp(&new_var, &fbi->var, sizeof(new_var))) {
+ memcpy(&fbi->var, &new_var, sizeof(new_var));
+ set_fb_fix(fbdev);
+ ctrl_change_mode(fbdev);
+ }
+ }
+
+ DBGLEAVE(1);
+}
+
+/* Set new x,y offsets in the virtual display for the visible area and switch
+ * to the new mode.
+ */
+static int omapfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *fbi)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+ int r = 0;
+
+ DBGENTER(1);
+
+ if (var->xoffset != fbi->var.xoffset ||
+ var->yoffset != fbi->var.yoffset) {
+ memcpy(&new_var, &fbi->var, sizeof(new_var));
+ new_var.xoffset = var->xoffset;
+ new_var.yoffset = var->yoffset;
+ if (set_fb_var(fbdev, &new_var))
+ r = -EINVAL;
+ else {
+ memcpy(&fbi->var, &new_var, sizeof(new_var));
+ set_fb_fix(fbdev);
+ ctrl_change_mode(fbdev);
+ }
+ }
+
+ DBGLEAVE(1);
+ return r;
+}
+
+/* Set mirror to vertical axis and switch to the new mode. */
+static int omapfb_mirror(struct fb_info *fbi, int mirror)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+ int r = 0;
+
+ DBGENTER(1);
+
+ mirror = mirror ? 1 : 0;
+ if (cpu_is_omap1510())
+ r = -EINVAL;
+ else if (mirror != fbdev->mirror) {
+ fbdev->mirror = mirror;
+ ctrl_change_mode(fbdev);
+ }
+
+ DBGLEAVE(1);
+ return r;
+}
+
+/* Set x,y scale and switch to the new mode */
+static int omapfb_scale(struct fb_info *fbi,
+ unsigned int xscale, unsigned int yscale)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+ int r = 0;
+
+ DBGENTER(1);
+
+ if (cpu_is_omap1510())
+ r = -EINVAL;
+ else if (xscale != fbdev->xscale || yscale != fbdev->yscale) {
+ if (fbi->var.xres * xscale > fbi->var.xres_virtual ||
+ fbi->var.yres * yscale > fbi->var.yres_virtual)
+ r = -EINVAL;
+ else {
+ fbdev->xscale = xscale;
+ fbdev->yscale = yscale;
+ ctrl_change_mode(fbdev);
+ }
+ }
+
+ DBGLEAVE(1);
+ return r;
+}
+
+/* Check values in var, try to adjust them in case of out of bound values if
+ * possible, or return error.
+ */
+static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+ int r;
+
+ DBGENTER(1);
+
+ r = set_fb_var(fbdev, var);
+
+ DBGLEAVE(1);
+ return r;
+}
+
+/* Switch to a new mode. The parameters for it has been check already by
+ * omapfb_check_var.
+ */
+static int omapfb_set_par(struct fb_info *fbi)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+
+ DBGENTER(1);
+
+ set_fb_fix(fbdev);
+ ctrl_change_mode(fbdev);
+
+ DBGLEAVE(1);
+ return 0;
+}
+
+/* Frame flipping support. Assign the primary or the secondary frame to the
+ * visible frame, as well as the source and destination frames for graphics
+ * operations like rectangle fill and area copy. Flipping is only possible
+ * if we have enough video memory for the secondary frame.
+ */
+static int omapfb_select_vis_frame(struct fb_info *fbi, unsigned int vis_idx)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+
+ if (vis_idx > 1 || (vis_idx == 1 && !fbdev->frame1_org))
+ return -EINVAL;
+ fbdev->vis_frame_org = vis_idx ? fbdev->frame1_org : fbdev->frame0_org;
+ ctrl_change_mode(fbdev);
+ return 0;
+}
+
+static int omapfb_select_src_frame(struct fb_info *fbi, unsigned int src_idx)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+
+ if (src_idx > 1 || (src_idx == 1 && !fbdev->frame1_org))
+ return -EINVAL;
+ fbdev->src_frame_org = src_idx ? fbdev->frame1_org : fbdev->frame0_org;
+ return 0;
+}
+
+static int omapfb_select_dst_frame(struct fb_info *fbi, unsigned int dst_idx)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+
+ if (dst_idx > 1 || (dst_idx == 1 && !fbdev->frame1_org))
+ return -EINVAL;
+ fbdev->dst_frame_org = dst_idx ? fbdev->frame1_org : fbdev->frame0_org;
+ DBGPRINT(1, "dst_frame_org=%#010x\n", fbdev->dst_frame_org);
+ return 0;
+}
+
+/* Get the address of the primary and secondary frames */
+static int omapfb_get_frame_offset(struct fb_info *fbi,
+ struct fb_frame_offset *fb_offset)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+
+ if (fb_offset->idx > 1)
+ return -EINVAL;
+ if (fb_offset->idx == 1 && !fbdev->frame1_org)
+ return -EINVAL;
+ fb_offset->offset = fb_offset->idx ? fbdev->frame1_org :
+ fbdev->frame0_org;
+ return 0;
+}
+
+static int omapfb_update_window(void *data)
+{
+ struct omapfb_update_window_params *par = data;
+ struct omapfb_device *fbdev = (struct omapfb_device *)par->fbi->par;
+
+ gfxdma_sync(&fbdev->gfx);
+ if (fbdev->ctrl->update_window(fbdev, &par->win))
+ return OMAPFB_UPDATE_FAILED;
+ else
+ return 0;
+}
+
+static int omapfb_schedule_update_window(struct fb_info *fbi,
+ struct fb_update_window *win)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+ struct omapfb_request *req;
+
+ if (!fbdev->ctrl->update_window ||
+ win->x >= fbi->var.xres || win->y >= fbi->var.yres)
+ return -EINVAL;
+ if (win->x + win->width >= fbi->var.xres)
+ win->width = fbi->var.xres - win->x;
+ if (win->y + win->height >= fbi->var.yres)
+ win->height = fbi->var.yres - win->y;
+ if (!win->width || !win->height)
+ return 0;
+ if ((req = omapfb_rqueue_alloc_req(&fbdev->rqueue)) == NULL)
+ return -ENOMEM;
+ req->function = omapfb_update_window;
+ req->par.update_window.fbi = fbi;
+ req->par.update_window.win = *win;
+ omapfb_rqueue_schedule_req(&fbdev->rqueue, req);
+ return fbdev->rqueue.status ? OMAPFB_GFX_STATUS_CHANGED : 0;
+}
+
+static int omapfb_schedule_full_update(struct fb_info *fbi)
+{
+ struct fb_update_window win;
+
+ win.x = 0;
+ win.y = 0;
+ win.width = fbi->var.xres;
+ win.height = fbi->var.yres;
+ return omapfb_schedule_update_window(fbi, &win);
+}
+
+static unsigned long omapfb_get_caps(struct fb_info *fbi)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+ unsigned long caps;
+
+ caps = 0;
+ caps |= fbdev->panel->get_caps(fbdev->panel);
+ caps |= fbdev->ctrl->get_caps(fbdev);
+ return caps;
+}
+
+static int omapfb_set_update_mode(struct omapfb_device *fbdev,
+ enum fb_update_mode new_mode);
+
+static enum fb_update_mode omapfb_get_update_mode(struct omapfb_device *fbdev);
+
+/* Ioctl interface. Part of the kernel mode frame buffer API is duplicated
+ * here to be accessible by user mode code. In addition transparent copy
+ * graphics transformations, frame flipping support is provided through this
+ * interface.
+ */
+static int omapfb_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg,
+ struct fb_info *fbi)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
+ struct fb_ops *ops = fbi->fbops;
+ union {
+ struct fb_fillrect rect;
+ struct fb_copyarea_ext area;
+ struct fb_image image;
+ struct fb_scale scale;
+ struct fb_frame_offset frame_offset;
+ struct fb_update_window update_window;
+ unsigned int frame_idx;
+ unsigned int mirror;
+ enum fb_update_mode update_mode;
+ unsigned long caps;
+ unsigned long rqueue_status;
+ } p;
+ int r = 0;
+
+ DBGENTER(2);
+
+ BUG_ON(!ops);
+ DBGPRINT(2, "cmd=%010x\n", cmd);
+ switch (cmd)
+ {
+ case OMAPFB_FILLRECT:
+ if (copy_from_user(&p.rect, (void __user *)arg, sizeof(p.rect)))
+ r = -EFAULT;
+ else
+ r = omapfb_schedule_fillrect(fbi, &p.rect);
+ break;
+ case OMAPFB_COPYAREA:
+ if (copy_from_user(&p.area, (void __user *)arg, sizeof(p.area)))
+ r = -EFAULT;
+ else
+ r = omapfb_schedule_copyarea(fbi, &p.area);
+ break;
+ case OMAPFB_IMAGEBLIT:
+ if (copy_from_user(&p.image, (void __user *)arg,
+ sizeof(p.image)))
+ r = -EFAULT;
+ else
+ r = omapfb_schedule_imageblit(fbi, &p.image, 0);
+ break;
+ case OMAPFB_TRANSPARENT_BLIT:
+ if (copy_from_user(&p.image, (void __user *)arg,
+ sizeof(p.image)))
+ r = -EFAULT;
+ else
+ r = omapfb_schedule_imageblit(fbi, &p.image,
+ COPY_MODE_TRANSPARENT);
+ break;
+ case OMAPFB_MIRROR:
+ if (get_user(p.mirror, (int __user *)arg))
+ r = -EFAULT;
+ else
+ omapfb_mirror(fbi, p.mirror);
+ break;
+ case OMAPFB_SCALE:
+ if (copy_from_user(&p.scale, (void __user *)arg,
+ sizeof(p.scale)))
+ r = -EFAULT;
+ else
+ r = omapfb_scale(fbi, p.scale.xscale, p.scale.yscale);
+ break;
+ case OMAPFB_SELECT_VIS_FRAME:
+ if (get_user(p.frame_idx, (int __user *)arg))
+ r = -EFAULT;
+ else
+ r = omapfb_select_vis_frame(fbi, p.frame_idx);
+ break;
+ case OMAPFB_SELECT_SRC_FRAME:
+ if (get_user(p.frame_idx, (int __user *)arg))
+ r = - EFAULT;
+ else
+ r = omapfb_select_src_frame(fbi, p.frame_idx);
+ break;
+ case OMAPFB_SELECT_DST_FRAME:
+ if (get_user(p.frame_idx, (int __user *)arg))
+ r = -EFAULT;
+ else
+ r = omapfb_select_dst_frame(fbi, p.frame_idx);
+ break;
+ case OMAPFB_GET_FRAME_OFFSET:
+ if (copy_from_user(&p.frame_offset, (void __user *)arg,
+ sizeof(p.frame_offset)))
+ r = -EFAULT;
+ else {
+ r = omapfb_get_frame_offset(fbi, &p.frame_offset);
+ if (copy_to_user((void __user *)arg, &p.frame_offset,
+ sizeof(p.frame_offset)))
+ r = -EFAULT;
+ }
+ break;
+ case OMAPFB_SYNC_GFX:
+ omapfb_rqueue_sync(&fbdev->rqueue);
+ break;
+ case OMAPFB_VSYNC:
+ break;
+ case OMAPFB_LATE_ACTIVATE:
+ printk(KERN_WARNING OMAPFB_DRIVER
+ ": LATE_ACTIVATE obsoleted by SET_UPDATE_MODE.\n");
+// r = -EINVAL;
+ break;
+ case OMAPFB_SET_UPDATE_MODE:
+ if (get_user(p.update_mode, (int __user *)arg))
+ r = -EFAULT;
+ else
+ r = omapfb_set_update_mode(fbdev, p.update_mode);
+ break;
+ case OMAPFB_GET_UPDATE_MODE:
+ p.update_mode = omapfb_get_update_mode(fbdev);
+ if (put_user(p.update_mode, (enum fb_update_mode __user *)arg))
+ r = -EFAULT;
+ break;
+ case OMAPFB_UPDATE_WINDOW:
+ if (copy_from_user(&p.update_window, (void __user *)arg,
+ sizeof(p.update_window)))
+ r = -EFAULT;
+ else
+ r = omapfb_schedule_update_window(fbi, &p.update_window);
+ break;
+ case OMAPFB_GET_CAPS:
+ p.caps = omapfb_get_caps(fbi);
+ if (put_user(p.caps, (unsigned long __user *)arg))
+ r = -EFAULT;
+ break;
+ case OMAPFB_GET_GFX_STATUS:
+ omapfb_rqueue_reset(&fbdev->rqueue, &p.rqueue_status);
+ if (put_user(p.rqueue_status, (unsigned long *)arg))
+ r = -EFAULT;
+ break;
+ default:
+ r = -EINVAL;
+ }
+
+ DBGLEAVE(2);
+ return r;
+}
+
+/* Callback table for the frame buffer framework. Some of these pointers
+ * will be changed according to the current setting of fb_info->accel_flags.
+ */
+static struct fb_ops omapfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = omapfb_open,
+ .fb_release = omapfb_release,
+ .fb_setcolreg = omapfb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_cursor = soft_cursor,
+ .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,
+};
+
+/*
+ * ---------------------------------------------------------------------------
+ * Sysfs interface
+ * ---------------------------------------------------------------------------
+ */
+/* omapfbX sysfs entries */
+static ssize_t omapfb_show_caps_num(struct device *dev, char *buf)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+
+ return snprintf(buf, PAGE_SIZE, "%#010lx\n",
+ omapfb_get_caps(fbdev->fb_info));
+}
+
+static ssize_t omapfb_show_caps_text(struct device *dev, char *buf)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+ int pos = 0;
+ int i;
+ unsigned long caps;
+
+ caps = omapfb_get_caps(fbdev->fb_info);
+ for (i = 0; i < ARRAY_SIZE(omapfb_caps_table) && pos < PAGE_SIZE; i++) {
+ if (omapfb_caps_table[i].flag & caps) {
+ pos += snprintf(&buf[pos], PAGE_SIZE - pos, "%s\n",
+ omapfb_caps_table[i].name);
+ }
+ }
+ return min((int)PAGE_SIZE, pos);
+}
+
+static DEVICE_ATTR(caps_num, 0444, omapfb_show_caps_num, NULL);
+static DEVICE_ATTR(caps_text, 0444, omapfb_show_caps_text, NULL);
+
+/* panel sysfs entries */
+static ssize_t omapfb_show_panel_name(struct device *dev, char *buf)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->panel->name);
+}
+
+static ssize_t omapfb_show_bklight_level(struct device *dev, char *buf)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+ int r;
+
+ if (fbdev->panel->get_bklight_level) {
+ r = snprintf(buf, PAGE_SIZE, "%d\n",
+ fbdev->panel->get_bklight_level(fbdev->panel));
+ } else
+ r = -ENODEV;
+ return r;
+}
+
+static ssize_t omapfb_store_bklight_level(struct device *dev, const char *buf,
+ size_t size)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+ int r;
+
+ if (fbdev->panel->set_bklight_level) {
+ unsigned int level;
+
+ if (sscanf(buf, "%10d", &level) == 1) {
+ r = fbdev->panel->set_bklight_level(fbdev->panel,
+ level);
+ } else
+ r = -EINVAL;
+ } else
+ r = -ENODEV;
+ return r ? r : size;
+}
+
+static ssize_t omapfb_show_bklight_max(struct device *dev, char *buf)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+ int r;
+
+ if (fbdev->panel->get_bklight_level) {
+ r = snprintf(buf, PAGE_SIZE, "%d\n",
+ fbdev->panel->get_bklight_max(fbdev->panel));
+ } else
+ r = -ENODEV;
+ return r;
+}
+
+static struct device_attribute dev_attr_panel_name =
+ __ATTR(name, 0444, omapfb_show_panel_name, NULL);
+static DEVICE_ATTR(backlight_level, 0664,
+ omapfb_show_bklight_level, omapfb_store_bklight_level);
+static DEVICE_ATTR(backlight_max, 0444, omapfb_show_bklight_max, NULL);
+
+static struct attribute *panel_attrs[] = {
+ &dev_attr_panel_name.attr,
+ &dev_attr_backlight_level.attr,
+ &dev_attr_backlight_max.attr,
+ NULL,
+};
+
+static struct attribute_group panel_attr_grp = {
+ .name = "panel",
+ .attrs = panel_attrs,
+};
+
+/* ctrl sysfs entries */
+static ssize_t omapfb_show_ctrl_name(struct device *dev, char *buf)
+{
+ struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->ctrl->name);
+}
+
+static struct device_attribute dev_attr_ctrl_name =
+ __ATTR(name, 0444, omapfb_show_ctrl_name, NULL);
+
+static struct attribute *ctrl_attrs[] = {
+ &dev_attr_ctrl_name.attr,
+ NULL,
+};
+
+static struct attribute_group ctrl_attr_grp = {
+ .name = "ctrl",
+ .attrs = ctrl_attrs,
+};
+
+static int omapfb_register_sysfs(struct omapfb_device *fbdev)
+{
+ int r;
+
+ if ((r = device_create_file(fbdev->dev, &dev_attr_caps_num)))
+ goto fail0;
+
+ if ((r = device_create_file(fbdev->dev, &dev_attr_caps_text)))
+ goto fail1;
+
+ if ((r = sysfs_create_group(&fbdev->dev->kobj, &panel_attr_grp)))
+ goto fail2;
+
+ if ((r = sysfs_create_group(&fbdev->dev->kobj, &ctrl_attr_grp)))
+ goto fail3;
+
+ return 0;
+fail3:
+ sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp);
+fail2:
+ device_remove_file(fbdev->dev, &dev_attr_caps_text);
+fail1:
+ device_remove_file(fbdev->dev, &dev_attr_caps_num);
+fail0:
+ PRNERR("unable to register sysfs interface\n");
+ return r;
+}
+
+static void omapfb_unregister_sysfs(struct omapfb_device *fbdev)
+{
+ sysfs_remove_group(&fbdev->dev->kobj, &ctrl_attr_grp);
+ sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp);
+ device_remove_file(fbdev->dev, &dev_attr_caps_num);
+ device_remove_file(fbdev->dev, &dev_attr_caps_text);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * LDM callbacks
+ * ---------------------------------------------------------------------------
+ */
+/* Initialize system fb_info object and set the default video mode.
+ * The frame buffer memory already allocated by lcddma_init
+ */
+static int fbinfo_init(struct omapfb_device *fbdev)
+{
+ struct fb_info *info = fbdev->fb_info;
+ struct fb_var_screeninfo *var = &info->var;
+ int r = 0;
+
+ DBGENTER(1);
+
+ BUG_ON(!fbdev->lcddma_base);
+ info->fbops = &omapfb_ops;
+ info->flags = FBINFO_FLAG_DEFAULT;
+ info->screen_base = (char __iomem *) fbdev->lcddma_base
+ + fbdev->frame0_org;
+ info->pseudo_palette = fbdev->pseudo_palette;
+
+ var->accel_flags = def_accel ? FB_ACCELF_TEXT : 0;
+ var->xres_virtual = def_vxres;
+ var->yres_virtual = def_vyres;
+ var->rotate = def_rotate;
+
+ fbdev->mirror = def_mirror;
+
+ set_fb_var(fbdev, var);
+ set_fb_fix(fbdev);
+
+ r = fb_alloc_cmap(&info->cmap, 16, 0);
+ if (r != 0)
+ PRNERR("unable to allocate color map memory\n");
+
+ DBGLEAVE(1);
+ return r;
+}
+
+/* Release the fb_info object */
+static void fbinfo_cleanup(struct omapfb_device *fbdev)
+{
+ DBGENTER(1);
+
+ fb_dealloc_cmap(&fbdev->fb_info->cmap);
+
+ DBGLEAVE(1);
+}
+
+/* Free driver resources. Can be called to rollback an aborted initialization
+ * sequence.
+ */
+static void omapfb_free_resources(struct omapfb_device *fbdev, int state)
+{
+ switch (state) {
+ case OMAPFB_ACTIVE:
+ unregister_framebuffer(fbdev->fb_info);
+ case 8:
+ omapfb_unregister_sysfs(fbdev);
+ case 7:
+ omapfb_set_update_mode(fbdev, FB_UPDATE_DISABLED);
+ case 6:
+ fbdev->panel->disable(fbdev->panel);
+ case 5:
+ gfxdma_cleanup(&fbdev->gfx);
+ case 4:
+ fbinfo_cleanup(fbdev);
+ case 3:
+ ctrl_cleanup(fbdev);
+ case 2:
+ fbdev->panel->cleanup(fbdev->panel);
+ case 1:
+ omapfb_rqueue_cleanup(&fbdev->rqueue);
+ dev_set_drvdata(fbdev->dev, NULL);
+ framebuffer_release(fbdev->fb_info);
+ case 0:
+ /* nothing to free */
+ break;
+ default:
+ BUG();
+ }
+}
+
+static int omapfb_find_panel(struct omapfb_device *fbdev)
+{
+ const struct omap_lcd_config *cfg;
+ char name[17];
+ int i;
+
+ fbdev->panel = NULL;
+ cfg = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
+ if (cfg == NULL) {
+ const char *def_name = NULL;
+
+ if (machine_is_omap_h2())
+ def_name = "h2";
+ if (machine_is_omap_h3())
+ def_name = "h3";
+ if (machine_is_omap_perseus2())
+ def_name = "p2";
+ if (machine_is_omap_osk())
+ def_name = "osk";
+ if (machine_is_omap_innovator() && cpu_is_omap1610())
+ def_name = "inn1610";
+ if (machine_is_omap_innovator() && cpu_is_omap1510())
+ def_name = "inn1510";
+ if (def_name == NULL)
+ return -1;
+ strncpy(name, def_name, sizeof(name) - 1);
+ } else
+ strncpy(name, cfg->panel_name, sizeof(name) - 1);
+ name[sizeof(name) - 1] = 0;
+ for (i = 0; i < ARRAY_SIZE(panels); i++) {
+ if (strcmp(panels[i]->name, name) == 0) {
+ fbdev->panel = panels[i];
+ break;
+ }
+ }
+ if (fbdev->panel == NULL)
+ return -1;
+ return 0;
+}
+
+static int omapfb_find_ctrl(struct omapfb_device *fbdev)
+{
+ const struct omap_lcd_config *cfg;
+ char name[17];
+ int i;
+
+ fbdev->ctrl = NULL;
+ cfg = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
+ if (cfg == NULL) {
+ strcpy(name, "internal");
+ } else
+ strncpy(name, cfg->ctrl_name, sizeof(name) - 1);
+ name[sizeof(name) - 1] = 0;
+ for (i = 0; i < ARRAY_SIZE(ctrls); i++) {
+ if (strcmp(ctrls[i]->name, name) == 0) {
+ fbdev->ctrl = ctrls[i];
+ break;
+ }
+ }
+ if (fbdev->ctrl == NULL)
+ return -1;
+ return 0;
+}
+
+static void check_required_callbacks(struct omapfb_device *fbdev)
+{
+#define _C(x) (fbdev->ctrl->x)
+#define _P(x) (fbdev->panel->x)
+ BUG_ON(!(_C(init) && _C(cleanup) && _C(get_caps) &&
+ _C(get_mem_layout) && _C(set_update_mode) && _C(change_mode) &&
+ _P(init) && _P(cleanup) && _P(enable) && _P(disable) &&
+ _P(get_caps)));
+#undef _P
+#undef _C
+}
+
+static int omapfb_set_update_mode(struct omapfb_device *fbdev,
+ enum fb_update_mode mode)
+{
+ return fbdev->ctrl->set_update_mode(fbdev, mode);
+}
+
+static enum fb_update_mode omapfb_get_update_mode(struct omapfb_device *fbdev)
+{
+ return fbdev->ctrl->get_update_mode(fbdev);
+}
+
+/* Called by LDM binding to probe and attach a new device.
+ * Initialization sequence:
+ * 1. allocate system fb_info structure
+ * select panel type according to machine type
+ * 2. init LCD panel
+ * 3. init LCD controller and LCD DMA
+ * 4. init system fb_info structure
+ * 5. init gfx DMA
+ * 6. enable LCD panel
+ * start LCD frame transfer
+ * 7. register system fb_info structure
+ */
+static int omapfb_probe(struct device *dev)
+{
+ struct platform_device *pdev;
+ struct omapfb_device *fbdev = NULL;
+ struct fb_info *fbi;
+ int init_state;
+ int r = 0;
+
+ DBGENTER(1);
+
+ init_state = 0;
+
+ pdev = to_platform_device(dev);
+ if (pdev->num_resources != 0) {
+ PRNERR("probed for an unknown device\n");
+ r = -ENODEV;
+ goto cleanup;
+ }
+
+ fbi = framebuffer_alloc(sizeof(struct omapfb_device), dev);
+ if (!fbi) {
+ PRNERR("unable to allocate memory for device info\n");
+ r = -ENOMEM;
+ goto cleanup;
+ }
+
+ fbdev = (struct omapfb_device *)fbi->par;
+ fbdev->fb_info = fbi;
+ fbdev->dev = dev;
+ dev_set_drvdata(dev, fbdev);
+
+ init_state++;
+ if (omapfb_find_ctrl(fbdev) < 0) {
+ PRNERR("LCD controller not found, board not supported\n");
+ r = -ENODEV;
+ goto cleanup;
+ }
+ if (omapfb_find_panel(fbdev) < 0) {
+ PRNERR("LCD panel not found, board not supported\n");
+ r = -ENODEV;
+ goto cleanup;
+ }
+
+ check_required_callbacks(fbdev);
+
+ printk(KERN_INFO OMAPFB_DRIVER ": configured for panel %s\n",
+ fbdev->panel->name);
+
+ omapfb_rqueue_init(&fbdev->rqueue);
+
+ r = fbdev->panel->init(fbdev->panel);
+ if (r)
+ goto cleanup;
+ init_state++;
+
+ r = ctrl_init(fbdev);
+ if (r)
+ goto cleanup;
+ init_state++;
+
+ r = fbinfo_init(fbdev);
+ if (r)
+ goto cleanup;
+ init_state++;
+
+ r = gfxdma_init(&fbdev->gfx);
+ if (r)
+ goto cleanup;
+ init_state++;
+
+#ifdef CONFIG_FB_OMAP_DMA_TUNE
+ /* Set DMA priority for EMIFF access to highest */
+ omap_set_dma_priority(OMAP_DMA_PORT_EMIFF, 15);
+#endif
+
+ r = fbdev->panel->enable(fbdev->panel);
+ if (r)
+ goto cleanup;
+ init_state++;
+
+ r = omapfb_set_update_mode(fbdev, manual_update ?
+ FB_MANUAL_UPDATE : FB_AUTO_UPDATE);
+ if (r)
+ goto cleanup;
+ init_state++;
+
+ r = omapfb_register_sysfs(fbdev);
+ if (r)
+ goto cleanup;
+ init_state++;
+
+ r = register_framebuffer(fbdev->fb_info);
+ if (r != 0) {
+ PRNERR("register_framebuffer failed\n");
+ goto cleanup;
+ }
+
+ fbdev->state = OMAPFB_ACTIVE;
+
+ printk(KERN_INFO "OMAP framebuffer initialized vram=%lu\n",
+ fbdev->lcddma_mem_size);
+
+ DBGLEAVE(1);
+ return 0;
+
+cleanup:
+ omapfb_free_resources(fbdev, init_state);
+
+ DBGLEAVE(1);
+ return r;
+}
+
+/* Called when the device is being detached from the driver */
+static int omapfb_remove(struct device *dev)
+{
+ struct omapfb_device *fbdev = dev_get_drvdata(dev);
+ enum omapfb_state saved_state = fbdev->state;
+
+ DBGENTER(1);
+ /* FIXME: wait till completion of pending events */
+
+ fbdev->state = OMAPFB_DISABLED;
+ omapfb_free_resources(fbdev, saved_state);
+
+ DBGLEAVE(1);
+ return 0;
+}
+
+/* PM suspend */
+static int omapfb_suspend(struct device *dev, pm_message_t mesg, u32 level)
+{
+ struct omapfb_device *fbdev = dev_get_drvdata(dev);
+
+ DBGENTER(1);
+
+ if (fbdev->state == OMAPFB_ACTIVE) {
+ if (fbdev->ctrl->suspend)
+ fbdev->ctrl->suspend(fbdev);
+ fbdev->panel->disable(fbdev->panel);
+ fbdev->state = OMAPFB_SUSPENDED;
+ }
+
+ DBGLEAVE(1);
+ return 0;
+}
+
+/* PM resume */
+static int omapfb_resume(struct device *dev, u32 level)
+{
+ struct omapfb_device *fbdev = dev_get_drvdata(dev);
+
+ DBGENTER(1);
+
+ if (fbdev->state == OMAPFB_SUSPENDED) {
+ fbdev->panel->enable(fbdev->panel);
+ if (fbdev->ctrl->resume)
+ fbdev->ctrl->resume(fbdev);
+ fbdev->state = OMAPFB_ACTIVE;
+ if (manual_update)
+ omapfb_schedule_full_update(fbdev->fb_info);
+ }
+
+ DBGLEAVE(1);
+ return 0;
+}
+
+static void omapfb_release_dev(struct device *dev)
+{
+ DBGENTER(1);
+ DBGLEAVE(1);
+}
+
+static u64 omapfb_dmamask = ~(u32)0;
+
+static struct platform_device omapfb_device = {
+ .name = OMAPFB_DEVICE,
+ .id = -1,
+ .dev = {
+ .release = omapfb_release_dev,
+ .dma_mask = &omapfb_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .num_resources = 0,
+};
+
+static struct device_driver omapfb_driver = {
+ .name = OMAPFB_DRIVER,
+ .bus = &platform_bus_type,
+ .probe = omapfb_probe,
+ .remove = omapfb_remove,
+ .suspend = omapfb_suspend,
+ .resume = omapfb_resume
+};
+
+#ifndef MODULE
+
+/* Process kernel command line parameters */
+static int __init omapfb_setup(char *options)
+{
+ char *this_opt = NULL;
+ int r = 0;
+
+ DBGENTER(1);
+
+ if (!options || !*options)
+ goto exit;
+
+ while (!r && (this_opt = strsep(&options, ",")) != NULL) {
+ if (!strncmp(this_opt, "accel", 5))
+ def_accel = 1;
+ else if (!strncmp(this_opt, "vram:", 5)) {
+ char *suffix;
+ def_vram = (simple_strtoul(this_opt + 5, &suffix, 0));
+ switch (suffix[0]) {
+ case 'm':
+ case 'M':
+ def_vram *= 1024 * 1024;
+ break;
+ case 'k':
+ case 'K':
+ def_vram *= 1024;
+ break;
+ default:
+ PRNERR("invalid vram suffix\n");
+ r = -1;
+ }
+ }
+ else if (!strncmp(this_opt, "vxres:", 6))
+ def_vxres = simple_strtoul(this_opt + 6, NULL, 0);
+ else if (!strncmp(this_opt, "vyres:", 6))
+ def_vyres = simple_strtoul(this_opt + 6, NULL, 0);
+ else if (!strncmp(this_opt, "rotate:", 7))
+ def_rotate = (simple_strtoul(this_opt + 7, NULL, 0));
+ else if (!strncmp(this_opt, "mirror:", 7))
+ def_mirror = (simple_strtoul(this_opt + 7, NULL, 0));
+ else if (!strncmp(this_opt, "manual_update", 13))
+ manual_update = 1;
+ else {
+ PRNERR("invalid option\n");
+ r = -1;
+ }
+ }
+exit:
+ DBGLEAVE(1);
+ return r;
+}
+
+#endif
+
+/* Register both the driver and the device */
+static int __init omapfb_init(void)
+{
+ int r = 0;
+
+ DBGENTER(1);
+
+#ifndef MODULE
+ {
+ char *option;
+
+ if (fb_get_options("omapfb", &option)) {
+ r = -ENODEV;
+ goto exit;
+ }
+ omapfb_setup(option);
+ }
+#endif
+ /* Register the device with LDM */
+ if (platform_device_register(&omapfb_device)) {
+ PRNERR("failed to register omapfb device\n");
+ r = -ENODEV;
+ goto exit;
+ }
+ /* Register the driver with LDM */
+ if (driver_register(&omapfb_driver)) {
+ PRNERR("failed to register omapfb driver\n");
+ platform_device_unregister(&omapfb_device);
+ r = -ENODEV;
+ goto exit;
+ }
+
+exit:
+ DBGLEAVE(1);
+ return r;
+}
+
+static void __exit omapfb_cleanup(void)
+{
+ DBGENTER(1);
+
+ driver_unregister(&omapfb_driver);
+ platform_device_unregister(&omapfb_device);
+
+ DBGLEAVE(1);
+}
+
+module_param_named(accel, def_accel, uint, 0664);
+module_param_named(vram, def_vram, ulong, 0664);
+module_param_named(vxres, def_vxres, long, 0664);
+module_param_named(vyres, def_vyres, long, 0664);
+module_param_named(rotate, def_rotate, uint, 0664);
+module_param_named(mirror, def_mirror, uint, 0664);
+module_param_named(manual_update, manual_update, bool, 0664);
+
+module_init(omapfb_init);
+module_exit(omapfb_cleanup);
+
+MODULE_DESCRIPTION("TI OMAP framebuffer driver");
+MODULE_AUTHOR("Imre Deak <imre.deak@nokia.com>");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * File: drivers/video/omap_new/omapfb_main.c
+ *
+ * Special optimiSed Screen Interface driver for TI OMAP boards
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Juha Yrjölä <juha.yrjola@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+
+#include "sossi.h"
+
+#define OMAP_SOSSI_BASE 0xfffbac00
+#define SOSSI_ID_REG 0x00
+#define SOSSI_INIT1_REG 0x04
+#define SOSSI_INIT2_REG 0x08
+#define SOSSI_INIT3_REG 0x0c
+#define SOSSI_FIFO_REG 0x10
+#define SOSSI_REOTABLE_REG 0x14
+#define SOSSI_TEARING_REG 0x18
+#define SOSSI_INIT1B_REG 0x1c
+#define SOSSI_FIFOB_REG 0x20
+
+#define DMA_GSCR 0xfffedc04
+#define DMA_LCD_CCR 0xfffee3c2
+#define DMA_LCD_CTRL 0xfffee3c4
+#define DMA_LCD_LCH_CTRL 0xfffee3ea
+
+static int sossi_base = IO_ADDRESS(OMAP_SOSSI_BASE);
+
+static inline u32 sossi_read_reg(int reg)
+{
+ return readl(sossi_base + reg);
+}
+
+static inline u16 sossi_read_reg16(int reg)
+{
+ return readw(sossi_base + reg);
+}
+
+static inline u8 sossi_read_reg8(int reg)
+{
+ return readb(sossi_base + reg);
+}
+
+static inline void sossi_write_reg(int reg, u32 value)
+{
+ writel(value, sossi_base + reg);
+}
+
+static inline void sossi_write_reg16(int reg, u16 value)
+{
+ writew(value, sossi_base + reg);
+}
+
+static inline void sossi_write_reg8(int reg, u8 value)
+{
+ writeb(value, sossi_base + reg);
+}
+
+static void sossi_set_bits(int reg, u32 bits)
+{
+ sossi_write_reg(reg, sossi_read_reg(reg) | bits);
+}
+
+static void sossi_clear_bits(int reg, u32 bits)
+{
+ sossi_write_reg(reg, sossi_read_reg(reg) & ~bits);
+}
+
+#if 1
+void sossi_dump(void)
+{
+ printk(" INIT1: 0x%08x\n", sossi_read_reg(SOSSI_INIT1_REG));
+ printk(" INIT2: 0x%08x\n", sossi_read_reg(SOSSI_INIT2_REG));
+ printk(" INIT3: 0x%08x\n", sossi_read_reg(SOSSI_INIT3_REG));
+ printk(" TEARING: 0x%08x\n", sossi_read_reg(SOSSI_TEARING_REG));
+ printk(" INIT1B: 0x%08x\n", sossi_read_reg(SOSSI_INIT1B_REG));
+}
+#endif
+
+static void sossi_dma_init(void)
+{
+ /* OMAP3.1 mapping disable */
+ omap_writel(omap_readl(DMA_GSCR) | (1 << 3), DMA_GSCR);
+ /* Logical channel type to b0100 */
+ omap_writew(omap_readw(DMA_LCD_LCH_CTRL) | (1 << 2), DMA_LCD_LCH_CTRL);
+ /* LCD_DMA dest port to 1 */
+ omap_writew(omap_readw(DMA_LCD_CTRL) | (1 << 8), DMA_LCD_CTRL);
+ /* LCD_CCR OMAP31 comp mode */
+ omap_writew(omap_readw(DMA_LCD_CCR) | (1 << 10), DMA_LCD_CCR);
+}
+
+#define MOD_CONF_CTRL_1 0xfffe1110
+#define CONF_SOSSI_RESET_R (1 << 23)
+#define CONF_MOD_SOSSI_CLK_EN_R (1 << 16)
+
+int sossi_init(void)
+{
+ u32 l, k;
+
+ /* Reset and enable the SoSSI module */
+ l = omap_readl(MOD_CONF_CTRL_1);
+ l |= CONF_SOSSI_RESET_R;
+ omap_writel(l, MOD_CONF_CTRL_1);
+ l &= ~CONF_SOSSI_RESET_R;
+ omap_writel(l, MOD_CONF_CTRL_1);
+
+ l |= CONF_MOD_SOSSI_CLK_EN_R;
+ /* FIXME: Hardcode divide ratio 3 */
+ l |= 2 << 17;
+ omap_writel(l, MOD_CONF_CTRL_1);
+
+ omap_writel(omap_readl(ARM_IDLECT2) | (1 << 11), ARM_IDLECT2);
+ omap_writel(omap_readl(ARM_IDLECT1) | (1 << 6), ARM_IDLECT1);
+
+ sossi_dma_init();
+
+ l = sossi_read_reg(SOSSI_INIT2_REG);
+ /* Enable and reset the SoSSI block */
+ l |= (1 << 0) | (1 << 1);
+ sossi_write_reg(SOSSI_INIT2_REG, l);
+ /* Take SoSSI out of reset */
+ l &= ~(1 << 1);
+ sossi_write_reg(SOSSI_INIT2_REG, l);
+
+ sossi_write_reg(SOSSI_ID_REG, 0);
+ l = sossi_read_reg(SOSSI_ID_REG);
+ k = sossi_read_reg(SOSSI_ID_REG);
+
+ if (l != 0x55555555 || k != 0xaaaaaaaa) {
+ printk(KERN_ERR "Invalid SoSSI sync pattern: %08x, %08x\n", l, k);
+ return -ENODEV;
+ }
+ l = sossi_read_reg(SOSSI_ID_REG); /* Component code */
+ l = sossi_read_reg(SOSSI_ID_REG);
+ printk(KERN_INFO "SoSSI rev. %d.%d initialized\n", l >> 16, l & 0xffff);
+
+ l = sossi_read_reg(SOSSI_INIT1_REG);
+ l |= (1 << 19); /* DMA_MODE */
+ l &= ~(1 << 31); /* REORDERING */
+ sossi_write_reg(SOSSI_INIT1_REG, l);
+
+ return 0;
+}
+
+static void set_timings(int tw0, int tw1)
+{
+ u32 l;
+
+ l = sossi_read_reg(SOSSI_INIT1_REG);
+ l &= ~((0x0f << 20) | (0x3f << 24));
+ l |= ((tw0 & 0x0f) << 20) | ((tw1 & 0x3f) << 24);
+ sossi_write_reg(SOSSI_INIT1_REG, l);
+}
+
+static struct sossi {
+ int bus_pick_width;
+} sossi;
+
+void sossi_set_xfer_params(int tw0, int tw1, int bus_pick_count, int bus_pick_width)
+{
+ u32 l;
+
+ set_timings(tw0, tw1);
+ sossi.bus_pick_width = bus_pick_width;
+ l = ((bus_pick_count - 1) << 5) | ((bus_pick_width - 1) & 0x1f);
+ sossi_write_reg(SOSSI_INIT3_REG, l);
+}
+
+void sossi_start_transfer(void)
+{
+ /* WE */
+ sossi_clear_bits(SOSSI_INIT2_REG, 1 << 4);
+ /* CS active low */
+ sossi_clear_bits(SOSSI_INIT1_REG, 1 << 30);
+ /* FIXME: locking? */
+}
+
+void sossi_stop_transfer(void)
+{
+ /* WE */
+ sossi_set_bits(SOSSI_INIT2_REG, 1 << 4);
+ /* CS active low */
+ sossi_set_bits(SOSSI_INIT1_REG, 1 << 30);
+ /* FIXME: locking? */
+}
+
+static void send_data(const void *data, unsigned int len)
+{
+ while (len >= 4) {
+ sossi_write_reg(SOSSI_FIFO_REG, *(const u32 *) data);
+ len -= 4;
+ data += 4;
+ }
+ while (len >= 2) {
+ sossi_write_reg16(SOSSI_FIFO_REG, *(const u16 *) data);
+ len -= 2;
+ data += 2;
+ }
+ while (len) {
+ sossi_write_reg8(SOSSI_FIFO_REG, *(const u8 *) data);
+ len--;
+ data++;
+ }
+}
+
+static void set_cycles(unsigned int len)
+{
+ int nr_cycles = len / (sossi.bus_pick_width / 8);
+
+ sossi_clear_bits(SOSSI_INIT1_REG, 0x3ffff);
+ sossi_set_bits(SOSSI_INIT1_REG, (nr_cycles - 1) & 0x3ffff);
+}
+
+void sossi_send_cmd(const void *data, unsigned int len)
+{
+ sossi_clear_bits(SOSSI_INIT1_REG, 1 << 18);
+ set_cycles(len);
+ send_data(data, len);
+}
+
+void sossi_send_data(const void *data, unsigned int len)
+{
+ sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
+ set_cycles(len);
+ send_data(data, len);
+}
+
+void sossi_prepare_dma_transfer(unsigned int count)
+{
+ sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
+ set_cycles(count);
+}
+
+void sossi_send_data_const32(u32 data, unsigned int count)
+{
+ sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
+ set_cycles(count * 4);
+ while (count > 0) {
+ sossi_write_reg(SOSSI_FIFO_REG, data);
+ count--;
+ }
+}
+
+void sossi_set_tearing(int mode, int hs_counter, int detect_limit,
+ int vs_counter, int vs_detect_limit, int flags)
+{
+ u32 l = 0;
+
+ l |= vs_counter << 30;
+ if (flags & SOSSI_FLAG_HS_INVERTED)
+ l |= 1 << 29;
+ if (flags & SOSSI_FLAG_VS_INVERTED)
+ l |= 1 << 28;
+ l |= mode << 26;
+ l |= hs_counter << 15;
+ l |= vs_detect_limit << 3;
+ l |= detect_limit;
+ sossi_write_reg(SOSSI_TEARING_REG, l);
+}
+
+void sossi_read_data(void *data, unsigned int len)
+{
+ /* Before reading we must check if some writings are going on */
+ while (!(sossi_read_reg(SOSSI_INIT2_REG) & (1 << 3)));
+ sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
+ set_cycles(len);
+ while (len >= 4) {
+ *(u32 *) data = sossi_read_reg(SOSSI_FIFO_REG);
+ len -= 4;
+ data += 4;
+ }
+ while (len >= 2) {
+ *(u16 *) data = sossi_read_reg16(SOSSI_FIFO_REG);
+ len -= 2;
+ data += 2;
+ }
+ while (len) {
+ *(u8 *) data = sossi_read_reg8(SOSSI_FIFO_REG);
+ len--;
+ data++;
+ }
+}
--- /dev/null
+#ifndef DRIVERS_VIDEO_OMAP_SOSSI_H
+#define DRIVERS_VIDEO_OMAP_SOSSI_H
+
+#define SOSSI_FLAG_HS_INVERTED 0x01
+#define SOSSI_FLAG_VS_INVERTED 0x02
+
+extern int sossi_init(void);
+extern void sossi_set_xfer_params(int tw0, int tw1, int bus_pick_count, int bus_pick_width);
+extern void sossi_start_transfer(void);
+extern void sossi_stop_transfer(void);
+extern void sossi_send_cmd(const void *data, unsigned int len);
+extern void sossi_send_data(const void *data, unsigned int len);
+extern void sossi_send_data_const32(u32 data, unsigned int count);
+extern void sossi_prepare_dma_transfer(unsigned int count);
+extern void sossi_read_data(void *data, unsigned int len);
+extern void sossi_set_tearing(int mode, int hs_counter, int detect_limit,
+ int vs_counter, int vs_detect_limit, int flags);
+
+#endif
#define FB_ACCEL_NV_20 44 /* nVidia Arch 20 */
#define FB_ACCEL_NV_30 45 /* nVidia Arch 30 */
#define FB_ACCEL_NV_40 46 /* nVidia Arch 40 */
+#define FB_ACCEL_OMAP1610 47 /* TI OMAP16xx */
#define FB_ACCEL_NEOMAGIC_NM2070 90 /* NeoMagic NM2070 */
#define FB_ACCEL_NEOMAGIC_NM2090 91 /* NeoMagic NM2090 */
#define FB_ACCEL_NEOMAGIC_NM2093 92 /* NeoMagic NM2093 */