Commit 56023db1 authored by Imre Deak's avatar Imre Deak Committed by Tony Lindgren

FB: OMAP: Add support for framebuffer

Adds support for OMAP framebuffer.
Signed-off-by: default avatarImre Deak <imre.deak@nokia.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent 49067b23
......@@ -1468,6 +1468,48 @@ config FB_S1D13XXX
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
......
......@@ -96,6 +96,7 @@ obj-$(CONFIG_FB_IMX) += imxfb.o
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
#
# 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)
/*
* 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 */
/*
* 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,
};
/*
* 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,
};
/*
* 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,
};
/*
* 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,
};
/*
* 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,
};
/*
* 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,
};
/*
* 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");
/*
* 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 */
/*
* 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");
/*
* File: drivers/video/omap_new/omapfb_main.c
*
* Special optimiSed Screen Interface driver for TI OMAP boards
*
* Copyright (C) 2004 Nokia Corporation
* Author: Juha Yrjl <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++;
}
}
#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
......@@ -107,6 +107,7 @@
#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 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment