Commit 420e73d5 authored by Imre Deak's avatar Imre Deak Committed by Juha Yrjola

ARM: OMAP: A whopping FB driver update

- Support for the Philips LPH8923 LCD panel
- Support for the Epson HWA742 LCD controller

- Support for frame buffer located in SRAM and/or SDRAM
- Support for boot loader initialized LCD controller / frame buffer
  content.

- LCD panels will now register a device in the relevant board-* files
  instead of specifying the LCD type as an OMAP_TAG_LCD. The controller
  type is still specified in OMAP_TAG_LCD.
- A new ATAG OMAP_TAG_FBMEM is used to describe the frame buffer memory
  configuration (SRAM and SDRAM regions)

- Changed the OMAP1 LCD controller driver to export its functions
  through the lcd_ctrl object.
- OMAP1 pixel clock divider will now round up in lcdc.c
- Changed the OMAP1 SoSSI driver to export its functions through the
  lcd_ctrl_extif object.
- OMAP1 SoSSI clock calculation goes through all possible clock
  dividers. Rounding of clock tick values now takes places at each
  timing parameter.

- OMAP2 RFBI clock calculation goes through all possible clock
  dividers. Rounding of clock tick values now takes places at each
  timing parameter.
- OMAP2 pixel clock divider will now round up in dispc.c
Signed-off-by: default avatarImre Deak <imre.deak@nokia.com>
Signed-off-by: default avatarJuha Yrjl <juha.yrjola@nokia.com>
parent ab7ad9fe
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.16-rc2-omap1
# Wed Feb 8 18:52:38 2006
# Wed Feb 8 19:07:23 2006
#
CONFIG_ARM=y
CONFIG_MMU=y
......@@ -814,10 +814,12 @@ CONFIG_FB=y
# CONFIG_FB_TILEBLITTING is not set
# CONFIG_FB_S1D13XXX is not set
CONFIG_FB_OMAP=y
# CONFIG_FB_OMAP_LCDC_INTERNAL is not set
CONFIG_FB_OMAP_LCDC_EXTERNAL=y
CONFIG_FB_OMAP_LCDC_HWA742=y
CONFIG_FB_OMAP_MANUAL_UPDATE=y
CONFIG_FB_OMAP_DMA_TUNE=y
CONFIG_FB_OMAP_LCD_LPH8923=y
# CONFIG_FB_OMAP_BOOTLOADER_INIT is not set
# CONFIG_FB_OMAP_DMA_TUNE is not set
# CONFIG_FB_VIRTUAL is not set
#
......
......@@ -271,12 +271,18 @@ static struct platform_device h2_irda_device = {
.resource = h2_irda_resources,
};
static struct platform_device h2_lcd_device = {
.name = "lcd_h2",
.id = -1,
};
static struct platform_device *h2_devices[] __initdata = {
&h2_nor_device,
&h2_nand_device,
&h2_smc91x_device,
&h2_irda_device,
&h2_kp_device,
&h2_lcd_device,
};
static void __init h2_init_smc91x(void)
......@@ -325,7 +331,6 @@ static struct omap_uart_config h2_uart_config __initdata = {
};
static struct omap_lcd_config h2_lcd_config __initdata = {
.panel_name = "h2",
.ctrl_name = "internal",
};
......
......@@ -349,6 +349,11 @@ static struct platform_device h3_irda_device = {
.resource = h3_irda_resources,
};
static struct platform_device h3_lcd_device = {
.name = "lcd_h3",
.id = -1,
};
static struct platform_device *devices[] __initdata = {
&nor_device,
&nand_device,
......@@ -356,6 +361,7 @@ static struct platform_device *devices[] __initdata = {
&intlat_device,
&h3_irda_device,
&h3_kp_device,
&h3_lcd_device,
};
static struct omap_usb_config h3_usb_config __initdata = {
......@@ -385,7 +391,6 @@ static struct omap_uart_config h3_uart_config __initdata = {
};
static struct omap_lcd_config h3_lcd_config __initdata = {
.panel_name = "h3",
.ctrl_name = "internal",
};
......
......@@ -169,10 +169,16 @@ static struct platform_device innovator1510_smc91x_device = {
.resource = innovator1510_smc91x_resources,
};
static struct platform_device innovator1510_lcd_device = {
.name = "lcd_inn1510",
.id = -1,
};
static struct platform_device *innovator1510_devices[] __initdata = {
&innovator_flash_device,
&innovator1510_smc91x_device,
&innovator_kp_device,
&innovator1510_lcd_device,
};
#endif /* CONFIG_ARCH_OMAP15XX */
......@@ -199,10 +205,16 @@ static struct platform_device innovator1610_smc91x_device = {
.resource = innovator1610_smc91x_resources,
};
static struct platform_device innovator1610_lcd_device = {
.name = "inn1610_lcd",
.id = -1,
};
static struct platform_device *innovator1610_devices[] __initdata = {
&innovator_flash_device,
&innovator1610_smc91x_device,
&innovator_kp_device,
&innovator1610_lcd_device,
};
#endif /* CONFIG_ARCH_OMAP16XX */
......@@ -248,7 +260,6 @@ static struct omap_usb_config innovator1510_usb_config __initdata = {
};
static struct omap_lcd_config innovator1510_lcd_config __initdata = {
.panel_name = "inn1510",
.ctrl_name = "internal",
};
#endif
......@@ -270,7 +281,6 @@ static struct omap_usb_config h2_usb_config __initdata = {
};
static struct omap_lcd_config innovator1610_lcd_config __initdata = {
.panel_name = "inn1610",
.ctrl_name = "internal",
};
#endif
......
......@@ -30,6 +30,15 @@ static void __init omap_nokia770_init_irq(void)
omap_init_irq();
}
static struct spi_board_info nokia770_spi_board_info[] __initdata = {
[0] = {
.modalias = "lcd_lph8923",
.bus_num = 2,
.chip_select = 3,
.max_speed_hz = 12000000,
},
};
static struct platform_device *nokia770_devices[] __initdata = {
};
......@@ -70,6 +79,8 @@ static void __init omap_nokia770_init(void)
nokia770_config[0].data = &nokia770_usb_config;
platform_add_devices(nokia770_devices, ARRAY_SIZE(nokia770_devices));
spi_register_board_info(nokia770_spi_board_info,
ARRAY_SIZE(nokia770_spi_board_info));
omap_board_config = nokia770_config;
omap_board_config_size = ARRAY_SIZE(nokia770_config);
omap_serial_init();
......
......@@ -178,12 +178,18 @@ static struct platform_device osk5912_kp_device = {
.resource = osk5912_kp_resources,
};
static struct platform_device osk5912_lcd_device = {
.name = "lcd_osk",
.id = -1,
};
static struct platform_device *osk5912_devices[] __initdata = {
&osk5912_flash_device,
&osk5912_smc91x_device,
&osk5912_cf_device,
&osk5912_mcbsp1_device,
&osk5912_kp_device,
&osk5912_lcd_device,
};
static void __init osk_init_smc91x(void)
......@@ -238,7 +244,6 @@ static struct omap_uart_config osk_uart_config __initdata = {
};
static struct omap_lcd_config osk_lcd_config __initdata = {
.panel_name = "osk",
.ctrl_name = "internal",
};
......
......@@ -38,6 +38,15 @@ static void __init omap_generic_init_irq(void)
omap_init_irq();
}
static struct platform_device palmte_lcd_device = {
.name = "lcd_palmte",
.id = -1,
};
static struct platform_device *devices[] __initdata = {
&palmte_lcd_device,
};
static struct omap_usb_config palmte_usb_config __initdata = {
.register_dev = 1,
.hmc_mode = 0,
......@@ -55,7 +64,6 @@ static struct omap_mmc_config palmte_mmc_config __initdata = {
};
static struct omap_lcd_config palmte_lcd_config __initdata = {
.panel_name = "palmte",
.ctrl_name = "internal",
};
......@@ -69,6 +77,8 @@ static void __init omap_generic_init(void)
{
omap_board_config = palmte_config;
omap_board_config_size = ARRAY_SIZE(palmte_config);
platform_add_devices(devices, ARRAY_SIZE(devices));
}
static void __init omap_generic_map_io(void)
......
......@@ -186,11 +186,17 @@ static struct platform_device kp_device = {
.resource = kp_resources,
};
static struct platform_device lcd_device = {
.name = "lcd_p2",
.id = -1,
};
static struct platform_device *devices[] __initdata = {
&nor_device,
&nand_device,
&smc91x_device,
&kp_device,
&lcd_device,
};
#define P2_NAND_RB_GPIO_PIN 62
......@@ -205,7 +211,6 @@ static struct omap_uart_config perseus2_uart_config __initdata = {
};
static struct omap_lcd_config perseus2_lcd_config __initdata = {
.panel_name = "p2",
.ctrl_name = "internal",
};
......
......@@ -18,6 +18,7 @@
#include <asm/io.h>
#include <asm/arch/mux.h>
#include <asm/arch/tc.h>
#include <asm/arch/omapfb.h>
extern int omap1_clk_init(void);
extern void omap_check_revision(void);
......@@ -121,6 +122,7 @@ void __init omap1_map_common_io(void)
#endif
omap_sram_init();
omapfb_reserve_mem();
}
/*
......
......@@ -120,9 +120,15 @@ static struct platform_device apollon_smc91x_device = {
.resource = apollon_smc91x_resources,
};
static struct platform_device apollon_lcd_device = {
.name = "apollon_lcd",
.id = -1,
};
static struct platform_device *apollon_devices[] __initdata = {
&apollon_onenand_device,
&apollon_smc91x_device,
&apollon_lcd_device,
};
static inline void __init apollon_init_smc91x(void)
......@@ -169,7 +175,6 @@ static struct omap_mmc_config apollon_mmc_config __initdata = {
};
static struct omap_lcd_config apollon_lcd_config __initdata = {
.panel_name = "apollon",
.ctrl_name = "internal",
};
......
......@@ -250,11 +250,17 @@ static struct platform_device h4_kp_device = {
},
};
static struct platform_device h4_lcd_device = {
.name = "lcd_h4",
.id = -1,
};
static struct platform_device *h4_devices[] __initdata = {
&h4_smc91x_device,
&h4_flash_device,
&h4_irda_device,
&h4_kp_device,
&h4_lcd_device,
};
static inline void __init h4_init_smc91x(void)
......@@ -301,7 +307,6 @@ static struct omap_mmc_config h4_mmc_config __initdata = {
};
static struct omap_lcd_config h4_lcd_config __initdata = {
.panel_name = "h4",
.ctrl_name = "internal",
};
......
......@@ -22,6 +22,7 @@
#include <asm/mach/map.h>
#include <asm/arch/mux.h>
#include <asm/arch/omapfb.h>
extern void omap_sram_init(void);
extern int omap2_clk_init(void);
......@@ -59,6 +60,7 @@ void __init omap2_map_common_io(void)
omap2_check_revision();
omap_sram_init();
omapfb_reserve_mem();
}
void __init omap2_init_common_hw(void)
......
......@@ -3,7 +3,7 @@
#
# Common support
obj-y := common.o sram.o sram-fn.o clock.o devices.o dma.o mux.o gpio.o mcbsp.o usb.o
obj-y := common.o sram.o sram-fn.o clock.o devices.o dma.o mux.o gpio.o mcbsp.o usb.o fb.o
obj-m :=
obj-n :=
obj- :=
......
......@@ -416,40 +416,6 @@ static void omap_init_rng(void)
static inline void omap_init_rng(void) {}
#endif
#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE)
static struct omap_lcd_config omap_fb_conf;
static u64 omap_fb_dma_mask = ~(u32)0;
static struct platform_device omap_fb_device = {
.name = "omapfb",
.id = -1,
.dev = {
.release = omap_nop_release,
.dma_mask = &omap_fb_dma_mask,
.coherent_dma_mask = ~(u32)0,
.platform_data = &omap_fb_conf,
},
.num_resources = 0,
};
static inline void omap_init_fb(void)
{
const struct omap_lcd_config *conf;
conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
if (conf != NULL)
omap_fb_conf = *conf;
platform_device_register(&omap_fb_device);
}
#else
static inline void omap_init_fb(void) {}
#endif
/*
* This gets called after board-specific INIT_MACHINE, and initializes most
* on-chip peripherals accessible on this board (except for few like USB):
......@@ -475,7 +441,6 @@ static int __init omap_init_devices(void)
/* please keep these calls, and their implementations above,
* in alphabetical order so they're easier to sort through.
*/
omap_init_fb();
omap_init_i2c();
omap_init_kp();
omap_init_mmc();
......
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/bootmem.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#include <asm/mach/map.h>
#include <asm/arch/board.h>
#include <asm/arch/sram.h>
#include <asm/arch/omapfb.h>
#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE)
static struct omapfb_platform_data omapfb_config;
static u64 omap_fb_dma_mask = ~(u32)0;
/* in devices.c */
extern void omap_nop_release(struct device *dev);
static struct platform_device omap_fb_device = {
.name = "omapfb",
.id = -1,
.dev = {
.release = omap_nop_release,
.dma_mask = &omap_fb_dma_mask,
.coherent_dma_mask = ~(u32)0,
.platform_data = &omapfb_config,
},
.num_resources = 0,
};
/* called from map_io */
void omapfb_reserve_mem(void)
{
const struct omap_fbmem_config *fbmem_conf;
omapfb_config.fbmem.fb_sram_start = omap_fb_sram_start;
omapfb_config.fbmem.fb_sram_size = omap_fb_sram_size;
fbmem_conf = omap_get_config(OMAP_TAG_FBMEM, struct omap_fbmem_config);
if (fbmem_conf != NULL) {
/* indicate that the bootloader already initialized the
* fb device, so we'll skip that part in the fb driver
*/
omapfb_config.fbmem.fb_sdram_start = fbmem_conf->fb_sdram_start;
omapfb_config.fbmem.fb_sdram_size = fbmem_conf->fb_sdram_size;
if (fbmem_conf->fb_sdram_size) {
pr_info("Reserving %u bytes SDRAM for frame buffer\n",
fbmem_conf->fb_sdram_size);
reserve_bootmem(fbmem_conf->fb_sdram_start,
fbmem_conf->fb_sdram_size);
}
}
}
static inline int omap_init_fb(void)
{
const struct omap_lcd_config *conf;
conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
if (conf == NULL)
return 0;
omapfb_config.lcd = *conf;
return platform_device_register(&omap_fb_device);
}
arch_initcall(omap_init_fb);
#else
void omapfb_reserve_mem(void) {}
#endif
......@@ -23,6 +23,7 @@
#include <asm/mach/map.h>
#include <asm/arch/sram.h>
#include <asm/arch/board.h>
#define OMAP1_SRAM_PA 0x20000000
#define OMAP1_SRAM_VA 0xd0000000
......@@ -50,6 +51,9 @@ static unsigned long omap_sram_base;
static unsigned long omap_sram_size;
static unsigned long omap_sram_ceil;
unsigned long omap_fb_sram_start;
unsigned long omap_fb_sram_size;
/* Depending on the target RAMFS firewall setup, the public usable amount of
* SRAM varies. The default accessable size for all device types is 2k. A GP
* device allows ARM11 but not other initators for full size. This
......@@ -74,6 +78,32 @@ static int is_sram_locked(void)
return 1; /* assume locked with no PPA or security driver */
}
void get_fb_sram_conf(unsigned long start_avail, unsigned size_avail,
unsigned long *start, unsigned long *size)
{
const struct omap_fbmem_config *fbmem_conf;
fbmem_conf = omap_get_config(OMAP_TAG_FBMEM, struct omap_fbmem_config);
if (fbmem_conf != NULL) {
*start = fbmem_conf->fb_sram_start;
*size = fbmem_conf->fb_sram_size;
} else {
*size = 0;
*start = 0;
}
if (*size && (
*start < start_avail ||
*start + *size > start_avail + size_avail)) {
printk(KERN_ERR "invalid FB SRAM configuration\n");
*start = start_avail;
*size = size_avail;
}
if (*size)
pr_info("Reserving %lu bytes SRAM for frame buffer\n", *size);
}
/*
* The amount of SRAM depends on the core type.
* Note that we cannot try to test for SRAM here because writes
......@@ -82,12 +112,16 @@ static int is_sram_locked(void)
*/
void __init omap_detect_sram(void)
{
unsigned long sram_start;
if (cpu_is_omap24xx()) {
if (is_sram_locked()) {
omap_sram_base = OMAP2_SRAM_PUB_VA;
sram_start = OMAP2_SRAM_PUB_PA;
omap_sram_size = 0x800; /* 2K */
} else {
omap_sram_base = OMAP2_SRAM_VA;
sram_start = OMAP2_SRAM_PA;
if (cpu_is_omap242x())
omap_sram_size = 0xa0000; /* 640K */
else if (cpu_is_omap243x())
......@@ -95,6 +129,7 @@ void __init omap_detect_sram(void)
}
} else {
omap_sram_base = OMAP1_SRAM_VA;
sram_start = OMAP1_SRAM_PA;
if (cpu_is_omap730())
omap_sram_size = 0x32000; /* 200K */
......@@ -110,6 +145,12 @@ void __init omap_detect_sram(void)
omap_sram_size = 0x4000;
}
}
get_fb_sram_conf(sram_start + SRAM_BOOTLOADER_SZ,
omap_sram_size - SRAM_BOOTLOADER_SZ,
&omap_fb_sram_start, &omap_fb_sram_size);
if (omap_fb_sram_size)
omap_sram_size -= sram_start + omap_sram_size -
omap_fb_sram_start;
omap_sram_ceil = omap_sram_base + omap_sram_size;
}
......
......@@ -4,13 +4,6 @@ config FB_OMAP
help
Frame buffer driver for OMAP based boards.
config FB_OMAP_LCDC_INTERNAL
bool "Internal LCD controller support"
depends on FB_OMAP
help
Say Y here, if you want to have support for the internal OMAP
LCD controller. If unsure, say Y.
config FB_OMAP_LCDC_EXTERNAL
bool "External LCD controller support"
depends on FB_OMAP
......@@ -18,6 +11,13 @@ config FB_OMAP_LCDC_EXTERNAL
Say Y here, if you want to have support for boards with an
external LCD controller connected to the SoSSI/RFBI interface.
config FB_OMAP_LCDC_HWA742
bool "Epson HWA742 LCD controller support"
depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL
help
Say Y here if you want to have support for the external
Epson HWA742 LCD controller.
config FB_OMAP_MANUAL_UPDATE
bool "Default to manual update mode"
depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL
......@@ -27,6 +27,21 @@ config FB_OMAP_MANUAL_UPDATE
the frame buffer content and thus a reload of the image data to
the external frame buffer is required. If unsure, say N.
config FB_OMAP_LCD_LPH8923
bool "Philips LPH8923 LCD support"
depends on FB_OMAP
help
Say Y here if you want to have support for the Philips
LPH8923 LCD.
config FB_OMAP_BOOTLOADER_INIT
bool "Check bootloader initializaion"
depends on FB_OMAP
help
Say Y here if you want to enable checking if the bootloader has
already initialized the display controller. In this case the
driver will skip the initialization.
config FB_OMAP_DMA_TUNE
bool "Set DMA SDRAM access priority high"
depends on FB_OMAP && ARCH_OMAP1
......@@ -37,3 +52,4 @@ config FB_OMAP_DMA_TUNE
answer yes. Answer no if you have a dedicated video
memory, or don't use any of the accelerated features.
......@@ -6,13 +6,14 @@ obj-$(CONFIG_FB_OMAP) += omapfb.o
objs-yy := omapfb_main.o
objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o
objs-y$(CONFIG_ARCH_OMAP2) += dispc.o
objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_INTERNAL) += lcdc.o
objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o
objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o
objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o
objs-y$(CONFIG_MACH_OMAP_H4) += lcd_h4.o
objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o
objs-y$(CONFIG_MACH_OMAP_H2) += lcd_h2.o
......@@ -23,5 +24,7 @@ objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o
objs-y$(CONFIG_MACH_OMAP_PERSEUS2) += lcd_p2.o
objs-y$(CONFIG_MACH_OMAP_APOLLON) += lcd_apollon.o
objs-y$(CONFIG_FB_OMAP_LCD_LPH8923) += lcd_lph8923.o
omapfb-objs := $(objs-yy)
......@@ -24,8 +24,6 @@
#ifndef __OMAPFB_DEBUG_H
#define __OMAPFB_DEBUG_H
#include <asm/io.h>
#ifdef OMAPFB_DBG
#define DBGPRINT(level, fmt, ...) if (level <= OMAPFB_DBG) do { \
......@@ -35,37 +33,11 @@
#define DBGENTER(level) DBGPRINT(level, "Enter\n")
#define DBGLEAVE(level) DBGPRINT(level, "Leave\n")
static inline void dump_dma_regs(int lch)
{
#ifdef CONFIG_ARCH_OMAP1
#define _R(x) __REG16(OMAP_DMA_##x(lch))
DBGPRINT(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
#endif
}
#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 */
......
......@@ -23,15 +23,18 @@
#include <linux/kernel.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/arch/sram.h>
#include <asm/arch/omapfb.h>
#include <asm/arch/board.h>
#include "dispc.h"
/* #define OMAPFB_DBG 2 */
/* #define OMAPFB_DBG 1 */
#include "debug.h"
......@@ -127,6 +130,8 @@
DISPC_IRQ_VID2_FIFO_UNDERFLOW | \
DISPC_IRQ_SYNC_LOST)
#define RFBI_CONTROL 0x48050040
#define MAX_PALETTE_SIZE (256 * 16)
#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args)
......@@ -138,11 +143,25 @@
static struct {
u32 base;
void *vram_virt;
dma_addr_t vram_phys;
int vram_size;
dma_addr_t fb_sram_paddr;
u32 fb_sram_size;
int fb_sram_lines;
dma_addr_t fb_sdram_paddr;
void *fb_sdram_vaddr;
u32 fb_sdram_size;
int fb_sdram_lines;
dma_addr_t palette_paddr;
void *palette_vaddr;
void *fb_kern_vaddr;
int multiplane_head;
int ext_mode;
int fbmem_allocated;
unsigned long enabled_irqs;
void (*irq_callback)(void *);
......@@ -152,8 +171,6 @@ static struct {
struct clk *dss_ick, *dss1_fck;
struct clk *dss_54m_fck;
int active_plane_mask;
enum omapfb_update_mode update_mode;
struct omapfb_device *fbdev;
} dispc;
......@@ -182,6 +199,11 @@ static void enable_rfbi_mode(int enable)
l |= 1 << 15;
l |= enable ? 0 : (1 << 16);
dispc_write_reg(DISPC_CONTROL, l);
/* Set bypass mode in RFBI module */
l = __raw_readl(io_p2v(RFBI_CONTROL));
l |= enable ? 0 : (1 << 1);
__raw_writel(l, io_p2v(RFBI_CONTROL));
}
static void set_lcd_data_lines(int data_lines)
......@@ -267,8 +289,8 @@ void omap_dispc_enable_digit_out(int enable)
}
EXPORT_SYMBOL(omap_dispc_enable_digit_out);
static int omap_dispc_setup_plane(int plane, int channel_out,
unsigned long offset, int screen_width,
static inline int _setup_plane(int plane, int channel_out,
u32 paddr, int screen_width,
int pos_x, int pos_y, int width, int height,
int color_mode)
{
......@@ -291,7 +313,10 @@ static int omap_dispc_setup_plane(int plane, int channel_out,
int bpp;
u32 l;
DBGENTER(1);
DBGPRINT(2, "plane %d channel %d paddr %u scr_width %d pos_x %d pos_y %d "
"width %d height %d color_mode %d\n",
plane, channel_out, paddr, screen_width, pos_x, pos_y,
width, height, color_mode);
switch (plane) {
case OMAPFB_PLANE_GFX:
......@@ -352,10 +377,7 @@ static int omap_dispc_setup_plane(int plane, int channel_out,
dispc_write_reg(at_reg[plane], l);
dispc_write_reg(ba_reg[plane],
dispc.vram_phys + PAGE_ALIGN(MAX_PALETTE_SIZE) + offset);
dispc_write_reg(ba_reg[plane], paddr);
MOD_REG_FLD(ps_reg[plane],
FLD_MASK(16, 11) | FLD_MASK(0, 11), (pos_y << 16) | pos_x);
......@@ -364,6 +386,52 @@ static int omap_dispc_setup_plane(int plane, int channel_out,
dispc_write_reg(ri_reg[plane], (screen_width - width) * bpp / 8 + 1);
return height * screen_width * bpp / 8;
}
static int omap_dispc_setup_plane(int plane, int channel_out,
unsigned long offset, int screen_width,
int pos_x, int pos_y, int width, int height,
int color_mode)
{
int yspan;
u32 paddr;
int mp_head = -1;
int r;
if (offset > dispc.fb_sram_size + dispc.fb_sdram_size)
return -EINVAL;
if (offset < dispc.fb_sram_size) {
paddr = dispc.fb_sram_paddr + offset;
yspan = min_t(int, height, dispc.fb_sram_lines);
if (yspan) {
if ((r = _setup_plane(plane, channel_out, paddr,
screen_width, pos_x, pos_y,
width, height, color_mode)) < 0)
return r;
offset += r;
height -= yspan;
pos_y += yspan;
mp_head = plane;
if (++plane > 2)
plane = OMAPFB_PLANE_GFX;
}
}
if (height) {
offset -= dispc.fb_sram_size;
paddr = dispc.fb_sdram_paddr + offset;
yspan = min_t(int, height, dispc.fb_sdram_lines);
if (yspan) {
if ((r = _setup_plane(plane, channel_out, paddr,
screen_width, pos_x, pos_y,
width, height, color_mode)) < 0)
return r;
if (mp_head != -1)
dispc.multiplane_head = mp_head;
}
}
return 0;
}
......@@ -372,11 +440,16 @@ static int omap_dispc_enable_plane(int plane, int enable)
const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES,
DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES,
DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES };
DBGENTER(1);
DBGENTER(2);
if ((unsigned int)plane > 2)
return -EINVAL;
MOD_REG_FLD(at_reg[plane], 1, enable ? 1 : 0);
if (plane == dispc.multiplane_head) {
if (++plane > 2)
plane = OMAPFB_PLANE_GFX;
MOD_REG_FLD(at_reg[plane], 1, enable ? 1 : 0);
}
return 0;
}
......@@ -435,6 +508,7 @@ static int omap_dispc_set_update_mode(enum omapfb_update_mode mode)
if (mode != dispc.update_mode) {
switch (mode) {
case OMAPFB_AUTO_UPDATE:
case OMAPFB_MANUAL_UPDATE:
omap_dispc_enable_lcd_out(1);
dispc.update_mode = mode;
break;
......@@ -470,7 +544,7 @@ static void calc_ck_div(int is_tft, int pck, int *lck_div, int *pck_div)
pck = max(1, pck);
fck = clk_get_rate(dispc.dss1_fck);
lck = fck;
*pck_div = lck / pck;
*pck_div = (lck + pck - 1) / pck;
if (is_tft)
*pck_div = max(2, *pck_div);
else
......@@ -489,6 +563,14 @@ static void calc_ck_div(int is_tft, int pck, int *lck_div, int *pck_div)
}
}
static void set_lcd_tft_mode(int enable)
{
u32 mask;
mask = 1 << 3;
MOD_REG_FLD(DISPC_CONTROL, mask, enable ? mask : 0);
}
static void set_lcd_timings(void)
{
u32 l;
......@@ -499,10 +581,6 @@ static void set_lcd_timings(void)
DBGENTER(1);
/* TFT dither, TFT/STN */
l = (1 << 7) | (1 << 3);
MOD_REG_FLD(DISPC_CONTROL, l, is_tft ? l : 0);
l = dispc_read_reg(DISPC_TIMING_H);
l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8));
l |= ( max(1, (min(64, panel->hsw))) - 1 ) << 0;
......@@ -588,7 +666,7 @@ static irqreturn_t omap_dispc_irq_handler(int irq, void *dev, struct pt_regs *re
if (stat & DISPC_IRQ_MASK_ERROR) {
if (jabber++ < 5) {
pr_err("irq error status %04x\n", stat);
pr_err("irq error status %04x\n", stat & 0x7fff);
} else {
pr_err("disable irq\n");
dispc_write_reg(DISPC_IRQENABLE, 0);
......@@ -684,8 +762,191 @@ static void omap_dispc_resume(void)
DBGLEAVE(1);
}
/* Called when used in bypass mode */
static int alloc_vram(int req_size)
static int omap_dispc_update_window(struct omapfb_update_window *win,
void (*complete_callback)(void *arg),
void *complete_callback_data)
{
return dispc.update_mode == OMAPFB_UPDATE_DISABLED ? -ENODEV : 0;
}
/* Greatest common divisor */
static int calc_gcd(int a, int b)
{
int tmp;
if (a < b) {
tmp = a;
a = b;
b = tmp;
}
for (;;) {
tmp = a % b;
if (tmp != 0) {
a = b;
b = tmp;
} else
break;
}
return b;
}
/* Least common multiple */
static int calc_lcm(int a, int b)
{
return a * b / calc_gcd(a, b);
}
static void omap_dispc_get_vram_layout(unsigned long *size, void **virt,
dma_addr_t *phys)
{
*size = dispc.fb_sram_size + dispc.fb_sdram_size;
*virt = dispc.fb_kern_vaddr;
/* Physical vram might not be contiguous. No one except own mmap
* should use this addr.
*/
*phys = 0;
}
static int omap_dispc_mmap_user(struct vm_area_struct *vma)
{
unsigned long len;
unsigned long offset;
unsigned long vaddr;
int r;
DBGPRINT(1, "vm_pgoff 0x%08lx vm_start 0x%08lx vm_end 0x%08lx\n",
vma->vm_pgoff, vma->vm_start, vma->vm_end);
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
offset = vma->vm_pgoff << PAGE_SHIFT;
if (offset >= dispc.fb_sram_size + dispc.fb_sdram_size)
return -EINVAL;
len = vma->vm_end - vma->vm_start;
if (offset + len > dispc.fb_sram_size + dispc.fb_sdram_size)
return -EINVAL;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
vma->vm_flags |= VM_PFNMAP;
vaddr = vma->vm_start;
if (dispc.fb_sram_size) {
if (offset < dispc.fb_sram_size) {
int chunk = min_t(int, dispc.fb_sram_size - offset, len);
DBGPRINT(1, "map sram va %08lx pa %08lx size %d\n",
vaddr, dispc.fb_sram_paddr + offset, chunk);
r = io_remap_pfn_range(vma, vaddr,
(dispc.fb_sram_paddr +
offset) >> PAGE_SHIFT, chunk,
vma->vm_page_prot);
if (r == -EINVAL)
return r;
if (r < 0)
return -EAGAIN;
vaddr += chunk;
len -= chunk;
offset = 0;
} else
offset -= dispc.fb_sram_size;
}
if (len) {
DBGPRINT(1, "map sdram va %08lx pa %08lx size %ld\n",
vaddr, dispc.fb_sdram_paddr + offset, len);
BUG_ON(offset > dispc.fb_sdram_size ||
offset + len > dispc.fb_sdram_size ||
vma->vm_end - vaddr != len);
r = io_remap_pfn_range(vma, vaddr, (dispc.fb_sdram_paddr +
offset) >> PAGE_SHIFT, len,
vma->vm_page_prot);
/* no teardown of the previous mapping here.
* do_mmap_pgoff will take core of that. */
if (r == -EINVAL)
return r;
if (r < 0)
return -EAGAIN;
}
return 0;
}
static int mmap_kern(void)
{
struct vm_struct *kvma;
struct vm_area_struct vma;
pgprot_t pgprot;
unsigned long vaddr;
DBGENTER(1);
kvma = get_vm_area(dispc.fb_sram_size + dispc.fb_sdram_size, VM_IOREMAP);
if (kvma == NULL) {
pr_err("can't get kernel vm area\n");
return -ENOMEM;
}
vma.vm_mm = &init_mm;
dispc.fb_kern_vaddr = kvma->addr;
vaddr = (unsigned long)kvma->addr;
pgprot = pgprot_writecombine(pgprot_kernel);
if (dispc.fb_sram_size) {
vma.vm_start = vaddr;
vma.vm_end = vaddr + dispc.fb_sram_size;
if (io_remap_pfn_range(&vma, vaddr,
dispc.fb_sram_paddr >> PAGE_SHIFT,
dispc.fb_sram_size, pgprot) < 0) {
pr_err("kernel mmap for FBMEM failed\n");
return -EAGAIN;
}
vaddr += dispc.fb_sram_size;
}
if (dispc.fb_sdram_size) {
vma.vm_start = vaddr;
vma.vm_end = vaddr + dispc.fb_sdram_size;
if (io_remap_pfn_range(&vma, vaddr,
dispc.fb_sdram_paddr >> PAGE_SHIFT,
dispc.fb_sdram_size, pgprot) < 0) {
pr_err("kernel mmap for FBMEM failed\n");
return -EAGAIN;
}
}
DBGLEAVE(1);
return 0;
}
static void unmap_kern(void)
{
vunmap(dispc.fb_kern_vaddr);
}
static int alloc_palette_ram(void)
{
dispc.palette_vaddr = dma_alloc_writecombine(dispc.fbdev->dev,
MAX_PALETTE_SIZE, &dispc.palette_paddr, GFP_KERNEL);
if (dispc.palette_vaddr == NULL) {
pr_err("failed to alloc palette memory\n");
return -ENOMEM;
}
return 0;
}
static void free_palette_ram(void)
{
dma_free_writecombine(dispc.fbdev->dev, MAX_PALETTE_SIZE,
dispc.palette_vaddr, dispc.palette_paddr);
}
static int alloc_fbmem(int req_size)
{
int frame_size;
struct lcd_panel *panel = dispc.fbdev->panel;
......@@ -693,11 +954,11 @@ static int alloc_vram(int req_size)
frame_size = PAGE_ALIGN(panel->x_res * panel->bpp / 8 * panel->y_res);
if (req_size > frame_size)
frame_size = req_size;
dispc.vram_size = PAGE_ALIGN(MAX_PALETTE_SIZE) + frame_size;
dispc.vram_virt = dma_alloc_writecombine(dispc.fbdev->dev,
dispc.vram_size, &dispc.vram_phys, GFP_KERNEL);
dispc.fb_sdram_size = PAGE_ALIGN(MAX_PALETTE_SIZE) + frame_size;
dispc.fb_kern_vaddr = dma_alloc_writecombine(dispc.fbdev->dev,
dispc.fb_sdram_size, &dispc.fb_sdram_paddr, GFP_KERNEL);
if (dispc.vram_virt == 0) {
if (dispc.fb_kern_vaddr == 0) {
pr_err("unable to allocate fb DMA memory\n");
return -ENOMEM;
}
......@@ -705,18 +966,75 @@ static int alloc_vram(int req_size)
return 0;
}
static void free_vram(void)
static void free_fbmem(void)
{
dma_free_writecombine(dispc.fbdev->dev, dispc.vram_size,
dispc.vram_virt, dispc.vram_phys);
dma_free_writecombine(dispc.fbdev->dev, dispc.fb_sdram_size,
dispc.fb_kern_vaddr, dispc.fb_sdram_paddr);
}
static void omap_dispc_get_vram_layout(unsigned long *size, void **virt,
dma_addr_t *phys)
static int setup_fbmem(int req_size)
{
*size = dispc.vram_size - PAGE_ALIGN(MAX_PALETTE_SIZE);
*virt = (u8 *)dispc.vram_virt + PAGE_ALIGN(MAX_PALETTE_SIZE);
*phys = dispc.vram_phys + PAGE_ALIGN(MAX_PALETTE_SIZE);
struct lcd_panel *panel = dispc.fbdev->panel;
struct omapfb_platform_data *conf;
unsigned long size_align;
int line_size;
int frame_size;
int lines;
int r;
conf = dispc.fbdev->dev->platform_data;
if (conf->fbmem.fb_sram_size + conf->fbmem.fb_sdram_size == 0) {
if ((r = alloc_fbmem(req_size)) < 0)
return r;
dispc.fbmem_allocated = 1;
dispc.fb_sdram_lines = panel->y_res;
return 0;
}
dispc.fb_sram_paddr = conf->fbmem.fb_sram_start;
dispc.fb_sram_size = conf->fbmem.fb_sram_size;
dispc.fb_sdram_paddr = conf->fbmem.fb_sdram_start;
dispc.fb_sdram_size = conf->fbmem.fb_sdram_size;
line_size = panel->x_res * panel->bpp / 8;
frame_size = PAGE_ALIGN(line_size * panel->y_res);
size_align = calc_lcm(line_size, PAGE_SIZE);
if (dispc.fb_sram_size + dispc.fb_sdram_size < frame_size ||
(dispc.fb_sdram_size && (dispc.fb_sram_size % size_align))) {
pr_err("Invalid FB memory configuration\n");
return -EINVAL;
}
if (dispc.fb_sram_size + dispc.fb_sdram_size < req_size) {
pr_err("%d vram was requested, but only %u is available\n",
req_size, dispc.fb_sram_size + dispc.fb_sdram_size);
}
lines = dispc.fb_sram_size / line_size;
lines = min_t(int, panel->y_res, lines);
dispc.fb_sram_lines = lines;
lines = panel->y_res - lines;
dispc.fb_sdram_lines = lines;
if ((r = mmap_kern()) < 0)
return r;
DBGPRINT(1, "fb_sram %08x size %08x fb_sdram %08x size %08x\n",
dispc.fb_sram_paddr, dispc.fb_sram_size,
dispc.fb_sdram_paddr, dispc.fb_sdram_size);
return 0;
}
static void cleanup_fbmem(void)
{
if (dispc.fbmem_allocated)
free_fbmem();
else
unmap_kern();
}
static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
......@@ -726,6 +1044,7 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
u32 l;
struct lcd_panel *panel = fbdev->panel;
int tmo = 10000;
int skip_init = 0;
DBGENTER(1);
......@@ -735,39 +1054,50 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
dispc.fbdev = fbdev;
dispc.ext_mode = ext_mode;
dispc.multiplane_head = -1;
if (fbdev->dev->platform_data == NULL) {
pr_err("missing FB configuration\n");
return -ENOENT;
}
init_completion(&dispc.frame_done);
if ((r = get_dss_clocks()) < 0)
goto fail0;
return r;
enable_lcd_clocks(1);
/* Reset monitoring works only w/ the 54M clk */
enable_digit_clocks(1);
l = dispc_read_reg(DISPC_REVISION);
pr_info(MODULE_NAME ": version %d.%d\n", l >> 4 & 0x0f, l & 0x0f);
/* Soft reset */
MOD_REG_FLD(DISPC_SYSCONFIG, 1 << 1, 1 << 1);
while (!(dispc_read_reg(DISPC_SYSSTATUS) & 1)) {
if (!--tmo) {
pr_err("soft reset failed\n");
r = -ENODEV;
enable_digit_clocks(0);
goto fail1;
}
#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
l = dispc_read_reg(DISPC_CONTROL);
/* LCD enabled ? */
if (l & 1) {
pr_info(MODULE_NAME ": skipping hardware initialization\n");
skip_init = 1;
}
#endif
enable_digit_clocks(0);
if (!skip_init) {
/* Reset monitoring works only w/ the 54M clk */
enable_digit_clocks(1);
if (!ext_mode && (r = alloc_vram(req_vram_size)) < 0)
goto fail1;
/* Soft reset */
MOD_REG_FLD(DISPC_SYSCONFIG, 1 << 1, 1 << 1);
/* Set logic clock to the fck for now */
MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(16, 8), 1);
while (!(dispc_read_reg(DISPC_SYSSTATUS) & 1)) {
if (!--tmo) {
pr_err("soft reset failed\n");
r = -ENODEV;
enable_digit_clocks(0);
goto fail1;
}
}
setup_plane_fifo(0);
setup_plane_fifo(1);
setup_plane_fifo(2);
enable_digit_clocks(0);
}
l = dispc_read_reg(DISPC_IRQSTATUS);
dispc_write_reg(l, DISPC_IRQSTATUS);
......@@ -778,38 +1108,60 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
if ((r = request_irq(INT_24XX_DSS_IRQ, omap_dispc_irq_handler,
0, MODULE_NAME, NULL)) < 0) {
pr_err("can't get DSS IRQ\n");
goto fail2;
goto fail1;
}
set_lcd_data_lines(panel->data_lines);
set_load_mode(DISPC_LOAD_FRAME_ONLY);
/* L3 firewall setting: enable access to OCM RAM */
__raw_writel(0x402000b0, io_p2v(0x680050a0));
if (!ext_mode) {
omap_dispc_set_lcd_size(panel->x_res, panel->y_res);
set_lcd_timings();
if ((r = alloc_palette_ram()) < 0)
goto fail2;
if ((r = setup_fbmem(req_vram_size)) < 0)
goto fail3;
if (!skip_init) {
memset(dispc.fb_kern_vaddr, 0,
dispc.fb_sram_size + dispc.fb_sdram_size);
/* Set logic clock to fck, pixel clock to fck/2 for now */
MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(16, 8), 1 << 16);
MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(0, 8), 2 << 0);
setup_plane_fifo(0);
setup_plane_fifo(1);
setup_plane_fifo(2);
set_lcd_tft_mode(panel->config & OMAP_LCDC_PANEL_TFT);
set_lcd_data_lines(panel->data_lines);
set_load_mode(DISPC_LOAD_FRAME_ONLY);
if (!ext_mode) {
omap_dispc_set_lcd_size(panel->x_res, panel->y_res);
set_lcd_timings();
}
enable_rfbi_mode(ext_mode);
}
enable_rfbi_mode(ext_mode);
DBGLEAVE(1);
return 0;
fail3:
free_palette_ram();
fail2:
if (ext_mode)
free_vram();
free_irq(INT_24XX_DSS_IRQ, NULL);
fail1:
enable_lcd_clocks(0);
put_dss_clocks();
fail0:
DBGLEAVE(1);
return r;
return r;
}
static void omap_dispc_cleanup(void)
{
cleanup_fbmem();
free_palette_ram();
free_irq(INT_24XX_DSS_IRQ, NULL);
enable_lcd_clocks(0);
put_dss_clocks();
if (dispc.ext_mode)
free_vram();
}
static unsigned long omap_dispc_get_caps(void)
......@@ -822,10 +1174,11 @@ struct lcd_ctrl omap2_int_ctrl = {
.init = omap_dispc_init,
.cleanup = omap_dispc_cleanup,
.get_vram_layout = omap_dispc_get_vram_layout,
.mmap = omap_dispc_mmap_user,
.get_caps = omap_dispc_get_caps,
.set_update_mode = omap_dispc_set_update_mode,
.get_update_mode = omap_dispc_get_update_mode,
.update_window = NULL,
.update_window = omap_dispc_update_window,
.suspend = omap_dispc_suspend,
.resume = omap_dispc_resume,
.setup_plane = omap_dispc_setup_plane,
......
/*
* File: drivers/video/omap/hwa742.c
*
* Epson HWA742 LCD controller driver
*
* Copyright (C) 2004-2005 Nokia Corporation
* Authors: Juha Yrjölä <juha.yrjola@nokia.com>
* Imre Deak <imre.deak@nokia.com>
* YUV support: Jussi Laako <jussi.laako@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 <linux/fb.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <asm/arch/dma.h>
#include <asm/arch/omapfb.h>
/* #define OMAPFB_DBG 1 */
#include "hwa742.h"
#include "debug.h"
#define MODULE_NAME "omapfb-hwa742"
#define HWA742_REV_CODE_REG 0x0
#define HWA742_CONFIG_REG 0x2
#define HWA742_PLL_DIV_REG 0x4
#define HWA742_PLL_0_REG 0x6
#define HWA742_PLL_1_REG 0x8
#define HWA742_PLL_2_REG 0xa
#define HWA742_PLL_3_REG 0xc
#define HWA742_PLL_4_REG 0xe
#define HWA742_CLK_SRC_REG 0x12
#define HWA742_PANEL_TYPE_REG 0x14
#define HWA742_H_DISP_REG 0x16
#define HWA742_H_NDP_REG 0x18
#define HWA742_V_DISP_1_REG 0x1a
#define HWA742_V_DISP_2_REG 0x1c
#define HWA742_V_NDP_REG 0x1e
#define HWA742_HS_W_REG 0x20
#define HWA742_HP_S_REG 0x22
#define HWA742_VS_W_REG 0x24
#define HWA742_VP_S_REG 0x26
#define HWA742_PCLK_POL_REG 0x28
#define HWA742_INPUT_MODE_REG 0x2a
#define HWA742_TRANSL_MODE_REG1 0x2e
#define HWA742_DISP_MODE_REG 0x34
#define HWA742_WINDOW_TYPE 0x36
#define HWA742_WINDOW_X_START_0 0x38
#define HWA742_WINDOW_X_START_1 0x3a
#define HWA742_WINDOW_Y_START_0 0x3c
#define HWA742_WINDOW_Y_START_1 0x3e
#define HWA742_WINDOW_X_END_0 0x40
#define HWA742_WINDOW_X_END_1 0x42
#define HWA742_WINDOW_Y_END_0 0x44
#define HWA742_WINDOW_Y_END_1 0x46
#define HWA742_MEMORY_WRITE_LSB 0x48
#define HWA742_MEMORY_WRITE_MSB 0x49
#define HWA742_MEMORY_READ_0 0x4a
#define HWA742_MEMORY_READ_1 0x4c
#define HWA742_MEMORY_READ_2 0x4e
#define HWA742_POWER_SAVE 0x56
#define HWA742_NDP_CTRL 0x58
#define HWA742_AUTO_UPDATE_TIME (HZ / 20)
#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args)
/* Reserve 4 request slots for requests in irq context */
#define REQ_POOL_SIZE 24
#define IRQ_REQ_POOL_SIZE 4
struct update_param {
int x, y, width, height;
int color_mode;
int flags;
};
#define REQ_FROM_IRQ_POOL 0x01
#define REQ_COMPLETE 0
#define REQ_PENDING 1
struct hwa742_request {
struct list_head entry;
unsigned int flags;
int (*handler)(struct hwa742_request *req);
void (*complete)(void *data);
void *complete_data;
union {
struct update_param update;
struct completion *sync;
} par;
};
struct hwa742_struct {
enum omapfb_update_mode update_mode;
enum omapfb_update_mode update_mode_before_suspend;
struct timer_list auto_update_timer;
int stop_auto_update;
struct omapfb_update_window auto_update_window;
struct hwa742_request req_pool[REQ_POOL_SIZE];
struct list_head pending_req_list;
struct list_head free_req_list;
struct semaphore req_sema;
spinlock_t req_lock;
struct clk *sys_ck;
struct extif_timings reg_timings, lut_timings;
int prev_color_mode;
int prev_flags;
int window_type;
u32 max_transmit_size;
u32 extif_clk_period;
struct omapfb_device *fbdev;
struct lcd_ctrl_extif *extif;
struct lcd_ctrl *int_ctrl;
} hwa742;
static u8 hwa742_read_reg(u8 reg)
{
u8 data;
hwa742.extif->set_bits_per_cycle(8);
hwa742.extif->write_command(&reg, 1);
hwa742.extif->read_data(&data, 1);
return data;
}
static void hwa742_write_reg(u8 reg, u8 data)
{
hwa742.extif->set_bits_per_cycle(8);
hwa742.extif->write_command(&reg, 1);
hwa742.extif->write_data(&data, 1);
}
void hwa742_read_id(int *rev_code, int *config)
{
*rev_code = hwa742_read_reg(HWA742_REV_CODE_REG);
*config = hwa742_read_reg(HWA742_CONFIG_REG);
}
EXPORT_SYMBOL(hwa742_read_id);
static void set_window_regs(int x_start, int y_start, int x_end, int y_end)
{
u8 tmp[8];
u8 cmd;
x_end--;
y_end--;
tmp[0] = x_start;
tmp[1] = x_start >> 8;
tmp[2] = y_start;
tmp[3] = y_start >> 8;
tmp[4] = x_end;
tmp[5] = x_end >> 8;
tmp[6] = y_end;
tmp[7] = y_end >> 8;
hwa742.extif->set_bits_per_cycle(8);
cmd = HWA742_WINDOW_X_START_0;
hwa742.extif->write_command(&cmd, 1);
hwa742.extif->write_data(tmp, 8);
}
static void set_format_regs(int conv, int transl, int flags)
{
if (flags & OMAPFB_FORMAT_FLAG_DOUBLE) {
hwa742.window_type = ((hwa742.window_type & 0xfc) | 0x01);
DBGPRINT(2, "hwa742: enabled pixel doubling\n");
} else {
hwa742.window_type = (hwa742.window_type & 0xfc);
DBGPRINT(2, "hwa742: disabled pixel doubling\n");
}
hwa742_write_reg(HWA742_INPUT_MODE_REG, conv);
hwa742_write_reg(HWA742_TRANSL_MODE_REG1, transl);
hwa742_write_reg(HWA742_WINDOW_TYPE, hwa742.window_type);
}
static inline struct hwa742_request *alloc_req(void)
{
unsigned long flags;
struct hwa742_request *req;
int req_flags = 0;
if (!in_interrupt())
down(&hwa742.req_sema);
else
req_flags = REQ_FROM_IRQ_POOL;
spin_lock_irqsave(&hwa742.req_lock, flags);
BUG_ON(list_empty(&hwa742.free_req_list));
req = list_entry(hwa742.free_req_list.next,
struct hwa742_request, entry);
list_del(&req->entry);
spin_unlock_irqrestore(&hwa742.req_lock, flags);
INIT_LIST_HEAD(&req->entry);
req->flags = req_flags;
return req;
}
static inline void free_req(struct hwa742_request *req)
{
unsigned long flags;
spin_lock_irqsave(&hwa742.req_lock, flags);
list_del(&req->entry);
list_add(&req->entry, &hwa742.free_req_list);
if (!(req->flags & REQ_FROM_IRQ_POOL))
up(&hwa742.req_sema);
spin_unlock_irqrestore(&hwa742.req_lock, flags);
}
static void process_pending_requests(void)
{
unsigned long flags;
DBGENTER(2);
spin_lock_irqsave(&hwa742.req_lock, flags);
while (!list_empty(&hwa742.pending_req_list)) {
struct hwa742_request *req;
void (*complete)(void *);
void *complete_data;
req = list_entry(hwa742.pending_req_list.next,
struct hwa742_request, entry);
spin_unlock_irqrestore(&hwa742.req_lock, flags);
if (req->handler(req) == REQ_PENDING)
return;
complete = req->complete;
complete_data = req->complete_data;
free_req(req);
if (complete)
complete(complete_data);
spin_lock_irqsave(&hwa742.req_lock, flags);
}
spin_unlock_irqrestore(&hwa742.req_lock, flags);
DBGLEAVE(2);
}
static void submit_req_list(struct list_head *head)
{
unsigned long flags;
int process = 1;
DBGENTER(2);
spin_lock_irqsave(&hwa742.req_lock, flags);
if (likely(!list_empty(&hwa742.pending_req_list)))
process = 0;
list_splice_init(head, hwa742.pending_req_list.prev);
spin_unlock_irqrestore(&hwa742.req_lock, flags);
if (process)
process_pending_requests();
DBGLEAVE(2);
}
static void request_complete(void *data)
{
struct hwa742_request *req = (struct hwa742_request *)data;
void (*complete)(void *);
void *complete_data;
complete = req->complete;
complete_data = req->complete_data;
free_req(req);
if (complete)
complete(complete_data);
process_pending_requests();
}
static int send_frame_handler(struct hwa742_request *req)
{
struct update_param *par = &req->par.update;
int x = par->x;
int y = par->y;
int w = par->width;
int h = par->height;
int bpp;
int conv, transl;
unsigned long offset;
int color_mode = par->color_mode;
int flags = par->flags;
int scr_width = 800;
DBGPRINT(2, "x %d y %d w %d h %d scr_width %d color_mode %d flags %d\n",
x, y, w, h, scr_width, color_mode, flags);
switch (color_mode) {
case OMAPFB_COLOR_YUV422:
bpp = 16;
conv = 0x08;
transl = 0x25;
break;
case OMAPFB_COLOR_YUV420:
bpp = 12;
conv = 0x09;
transl = 0x25;
break;
case OMAPFB_COLOR_RGB565:
bpp = 16;
conv = 0x01;
transl = 0x05;
break;
default:
return -EINVAL;
}
if (hwa742.prev_flags != flags ||
hwa742.prev_color_mode != color_mode) {
set_format_regs(conv, transl, flags);
hwa742.prev_color_mode = color_mode;
hwa742.prev_flags = flags;
}
set_window_regs(x, y, x + w, y + h);
offset = (scr_width * y + x) * bpp / 8;
hwa742.int_ctrl->setup_plane(OMAPFB_PLANE_GFX,
OMAPFB_CHANNEL_OUT_LCD, offset, scr_width, 0, 0, w, h,
color_mode);
hwa742.extif->set_bits_per_cycle(16);
hwa742.int_ctrl->enable_plane(OMAPFB_PLANE_GFX, 1);
hwa742.extif->transfer_area(w, h, request_complete, req);
return REQ_PENDING;
}
static void send_frame_complete(void *data)
{
hwa742.int_ctrl->enable_plane(OMAPFB_PLANE_GFX, 0);
}
#define ADD_PREQ(_x, _y, _w, _h) do { \
req = alloc_req(); \
req->handler = send_frame_handler; \
req->complete = send_frame_complete; \
req->par.update.x = _x; \
req->par.update.y = _y; \
req->par.update.width = _w; \
req->par.update.height = _h; \
req->par.update.color_mode = color_mode;\
req->par.update.flags = flags; \
list_add_tail(&req->entry, req_head); \
} while(0)
static void create_req_list(struct omapfb_update_window *win,
struct list_head *req_head)
{
struct hwa742_request *req;
int x = win->x;
int y = win->y;
int width = win->width;
int height = win->height;
int color_mode;
int flags;
flags = win->format & OMAPFB_FORMAT_FLAG_DOUBLE;
color_mode = win->format & OMAPFB_FORMAT_MASK;
if (x & 1) {
ADD_PREQ(x, y, 1, height);
width--;
x++;
}
if (width & ~1) {
unsigned int xspan = width & ~1;
unsigned int ystart = y;
unsigned int yspan = height;
if (xspan * height * 2 > hwa742.max_transmit_size) {
yspan = hwa742.max_transmit_size / (xspan * 2);
ADD_PREQ(x, ystart, xspan, yspan);
ystart += yspan;
yspan = height - yspan;
}
ADD_PREQ(x, ystart, xspan, yspan);
x += xspan;
width -= xspan;
}
if (width)
ADD_PREQ(x, y, 1, height);
}
static void auto_update_complete(void *data)
{
DBGENTER(2);
if (!hwa742.stop_auto_update)
mod_timer(&hwa742.auto_update_timer,
jiffies + HWA742_AUTO_UPDATE_TIME);
DBGLEAVE(2);
}
static void hwa742_update_window_auto(unsigned long arg)
{
LIST_HEAD(req_list);
struct hwa742_request *last;
DBGENTER(2);
create_req_list(&hwa742.auto_update_window, &req_list);
last = list_entry(req_list.prev, struct hwa742_request, entry);
last->complete = auto_update_complete;
last->complete_data = NULL;
submit_req_list(&req_list);
DBGLEAVE(2);
}
int hwa742_update_window_async(struct omapfb_update_window *win,
void (*complete_callback)(void *arg),
void *complete_callback_data)
{
LIST_HEAD(req_list);
struct hwa742_request *last;
int r = 0;
DBGENTER(2);
if (hwa742.update_mode != OMAPFB_MANUAL_UPDATE) {
r = -EINVAL;
goto out;
}
if (unlikely(win->format & ~(0x03 | OMAPFB_FORMAT_FLAG_DOUBLE))) {
r = -EINVAL;
goto out;
}
create_req_list(win, &req_list);
last = list_entry(req_list.prev, struct hwa742_request, entry);
last->complete = complete_callback;
last->complete_data = (void *)complete_callback_data;
submit_req_list(&req_list);
out:
DBGLEAVE(2);
return r;
}
EXPORT_SYMBOL(hwa742_update_window_async);
static int hwa742_setup_plane(int plane, int channel_out,
unsigned long offset, int screen_width,
int pos_x, int pos_y, int width, int height,
int color_mode)
{
if (plane != OMAPFB_PLANE_GFX ||
channel_out != OMAPFB_CHANNEL_OUT_LCD)
return -EINVAL;
return 0;
}
static int hwa742_enable_plane(int plane, int enable)
{
if (plane != 0)
return -EINVAL;
hwa742.int_ctrl->enable_plane(plane, enable);
return 0;
}
static int sync_handler(struct hwa742_request *req)
{
complete(req->par.sync);
return REQ_COMPLETE;
}
static void hwa742_sync(void)
{
LIST_HEAD(req_list);
struct hwa742_request *req;
struct completion comp;
DBGENTER(2);
req = alloc_req();
req->handler = sync_handler;
req->complete = NULL;
init_completion(&comp);
req->par.sync = &comp;
list_add(&req->entry, &req_list);
submit_req_list(&req_list);
wait_for_completion(&comp);
DBGLEAVE(2);
}
static struct notifier_block *hwa742_client_list;
int hwa742_register_client(struct hwa742_notifier_block *hwa742_nb,
hwa742_notifier_callback_t callback,
void *callback_data)
{
int r;
DBGPRINT(1, "update_mode %d\n", hwa742.update_mode);
hwa742_nb->nb.notifier_call = (int (*)(struct notifier_block *,
unsigned long, void *))callback;
hwa742_nb->data = callback_data;
r = notifier_chain_register(&hwa742_client_list, &hwa742_nb->nb);
if (r)
return r;
if (hwa742.update_mode == OMAPFB_MANUAL_UPDATE) {
DBGPRINT(1, "calling client list\n");
notifier_call_chain(&hwa742_client_list,
HWA742_EVENT_READY,
hwa742.fbdev);
}
return 0;
}
EXPORT_SYMBOL(hwa742_register_client);
int hwa742_unregister_client(struct hwa742_notifier_block *hwa742_nb)
{
return notifier_chain_unregister(&hwa742_client_list,
&hwa742_nb->nb);
}
EXPORT_SYMBOL(hwa742_unregister_client);
static int hwa742_set_update_mode(enum omapfb_update_mode mode)
{
int r = 0;
DBGENTER(1);
if (mode != OMAPFB_MANUAL_UPDATE && mode != OMAPFB_AUTO_UPDATE &&
mode != OMAPFB_UPDATE_DISABLED) {
r = -EINVAL;
goto out;
}
if (mode == hwa742.update_mode)
goto out;
printk(KERN_INFO "hwa742: setting update mode to %s\n",
mode == OMAPFB_UPDATE_DISABLED ? "disabled" :
(mode == OMAPFB_AUTO_UPDATE ? "auto" : "manual"));
switch (hwa742.update_mode) {
case OMAPFB_MANUAL_UPDATE:
notifier_call_chain(&hwa742_client_list,
HWA742_EVENT_DISABLED,
hwa742.fbdev);
break;
case OMAPFB_AUTO_UPDATE:
hwa742.stop_auto_update = 1;
del_timer_sync(&hwa742.auto_update_timer);
break;
case OMAPFB_UPDATE_DISABLED:
break;
}
hwa742.update_mode = mode;
hwa742_sync();
hwa742.stop_auto_update = 0;
switch (mode) {
case OMAPFB_MANUAL_UPDATE:
notifier_call_chain(&hwa742_client_list,
HWA742_EVENT_READY,
hwa742.fbdev);
break;
case OMAPFB_AUTO_UPDATE:
hwa742_update_window_auto(0);
break;
case OMAPFB_UPDATE_DISABLED:
break;
}
out:
DBGLEAVE(1);
return r;
}
static enum omapfb_update_mode hwa742_get_update_mode(void)
{
return hwa742.update_mode;
}
static unsigned long round_to_extif_ticks(unsigned long ps, int div)
{
int bus_tick = hwa742.extif_clk_period * div;
return (ps + bus_tick - 1) / bus_tick * bus_tick;
}
static int calc_reg_timing(unsigned long sysclk, int div)
{
struct extif_timings *t;
unsigned long systim;
/* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns,
* AccessTime 2 ns + 12.2 ns (regs),
* WEOffTime = WEOnTime + 1 ns,
* REOffTime = REOnTime + 16 ns (regs),
* CSOffTime = REOffTime + 1 ns
* ReadCycle = 2ns + 2*SYSCLK (regs),
* WriteCycle = 2*SYSCLK + 2 ns,
* CSPulseWidth = 10 ns */
systim = 1000000000 / (sysclk / 1000);
DBGPRINT(1, "HWA742 systim %lu ps extif_clk_period %u ps"
"extif_clk_div %d\n", systim, hwa742.extif_clk_period, div);
t = &hwa742.reg_timings;
memset(t, 0, sizeof(*t));
t->clk_div = div;
t->cs_on_time = 0;
t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
t->access_time = round_to_extif_ticks(t->re_on_time + 12200, div);
t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div);
t->re_off_time = round_to_extif_ticks(t->re_on_time + 16000, div);
t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div);
t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div);
if (t->we_cycle_time < t->we_off_time)
t->we_cycle_time = t->we_off_time;
t->re_cycle_time = round_to_extif_ticks(2 * systim + 2000, div);
if (t->re_cycle_time < t->re_off_time)
t->re_cycle_time = t->re_off_time;
t->cs_pulse_width = 0;
DBGPRINT(1, "[reg]cson %d csoff %d reon %d reoff %d\n",
t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time);
DBGPRINT(1, "[reg]weon %d weoff %d recyc %d wecyc %d\n",
t->we_on_time, t->we_off_time, t->re_cycle_time,
t->we_cycle_time);
DBGPRINT(1, "[reg]rdaccess %d cspulse %d\n",
t->access_time, t->cs_pulse_width);
return hwa742.extif->convert_timings(t);
}
static int calc_lut_timing(unsigned long sysclk, int div)
{
struct extif_timings *t;
unsigned long systim;
/* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns,
* AccessTime 2 ns + 4 * SYSCLK + 26 (lut),
* WEOffTime = WEOnTime + 1 ns,
* REOffTime = REOnTime + 4*SYSCLK + 26 ns (lut),
* CSOffTime = REOffTime + 1 ns
* ReadCycle = 2ns + 4*SYSCLK + 26 ns (lut),
* WriteCycle = 2*SYSCLK + 2 ns,
* CSPulseWidth = 10 ns
*/
systim = 1000000000 / (sysclk / 1000);
DBGPRINT(1, "HWA742 systim %lu ps extif_clk_period %u ps"
"extif_clk_div %d\n", systim, hwa742.extif_clk_period, div);
t = &hwa742.lut_timings;
memset(t, 0, sizeof(*t));
t->clk_div = div;
t->cs_on_time = 0;
t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
t->access_time = round_to_extif_ticks(t->re_on_time + 4 * systim +
26000, div);
t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div);
t->re_off_time = round_to_extif_ticks(t->re_on_time + 4 * systim +
26000, div);
t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div);
t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div);
if (t->we_cycle_time < t->we_off_time)
t->we_cycle_time = t->we_off_time;
t->re_cycle_time = round_to_extif_ticks(2000 + 4 * systim + 26000, div);
if (t->re_cycle_time < t->re_off_time)
t->re_cycle_time = t->re_off_time;
t->cs_pulse_width = 0;
DBGPRINT(1, "[lut]cson %d csoff %d reon %d reoff %d\n",
t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time);
DBGPRINT(1, "[lut]weon %d weoff %d recyc %d wecyc %d\n",
t->we_on_time, t->we_off_time, t->re_cycle_time,
t->we_cycle_time);
DBGPRINT(1, "[lut]rdaccess %d cspulse %d\n",
t->access_time, t->cs_pulse_width);
return hwa742.extif->convert_timings(t);
}
static int calc_extif_timings(unsigned long sysclk)
{
int max_clk_div;
int div;
hwa742.extif->get_clk_info(&hwa742.extif_clk_period, &max_clk_div);
for (div = 1; div < max_clk_div; div++) {
if (calc_reg_timing(sysclk, div) == 0)
break;
}
if (div == max_clk_div)
goto err;
for (div = 1; div < max_clk_div; div++) {
if (calc_lut_timing(sysclk, div) == 0)
break;
}
if (div < max_clk_div)
return 0;
err:
pr_err("can't setup timings\n");
return -1;
}
static unsigned long hwa742_get_caps(void)
{
return OMAPFB_CAPS_MANUAL_UPDATE;
}
static void hwa742_suspend(void)
{
hwa742.update_mode_before_suspend = hwa742.update_mode;
hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED);
/* Enable sleep mode */
hwa742_write_reg(HWA742_POWER_SAVE, 1 << 1);
clk_disable(hwa742.sys_ck);
}
static void hwa742_resume(void)
{
if (clk_enable(hwa742.sys_ck) != 0)
pr_err("failed to enable SYS clock\n");
/* Disable sleep mode */
hwa742_write_reg(HWA742_POWER_SAVE, 0);
while (1) {
/* Loop until PLL output is stabilized */
if (hwa742_read_reg(HWA742_PLL_DIV_REG) & (1 << 7))
break;
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(msecs_to_jiffies(5));
}
hwa742_set_update_mode(hwa742.update_mode_before_suspend);
}
struct lcd_ctrl hwa742_ctrl;
static int hwa742_init(struct omapfb_device *fbdev, int ext_mode, int req_vram_size)
{
int r = 0, i;
u8 rev, conf;
unsigned long sysfreq;
int div, nd;
DBGENTER(1);
hwa742.sys_ck = clk_get(0, "bclk");
if (IS_ERR(hwa742.sys_ck)) {
pr_err("can't get SYS clock\n");
return PTR_ERR(hwa742.sys_ck);
}
if ((r = clk_enable(hwa742.sys_ck)) != 0) {
pr_err("can't enable SYS clock\n");
clk_put(hwa742.sys_ck);
return r;
}
BUG_ON(!fbdev->ext_if || !fbdev->int_ctrl);
hwa742.fbdev = fbdev;
hwa742.extif = fbdev->ext_if;
hwa742.int_ctrl = fbdev->int_ctrl;
spin_lock_init(&hwa742.req_lock);
if ((r = hwa742.int_ctrl->init(fbdev, 1, req_vram_size)) < 0)
goto err1;
if ((r = hwa742.extif->init()) < 0)
goto err2;
hwa742_ctrl.get_vram_layout = hwa742.int_ctrl->get_vram_layout;
hwa742_ctrl.mmap = hwa742.int_ctrl->mmap;
sysfreq = clk_get_rate(hwa742.sys_ck);
if ((r = calc_extif_timings(sysfreq)) < 0)
goto err3;
hwa742.extif->set_timings(&hwa742.reg_timings);
div = (hwa742_read_reg(HWA742_PLL_DIV_REG) & 0x3f) + 1;
nd = (hwa742_read_reg(HWA742_PLL_4_REG) & 0x7f) + 1;
if ((r = calc_extif_timings(sysfreq / div * nd)) < 0)
goto err3;
hwa742.extif->set_timings(&hwa742.reg_timings);
rev = hwa742_read_reg(HWA742_REV_CODE_REG);
if ((rev & 0xfc) != 0x80) {
pr_err("invalid revision %02x\n", rev);
r = -ENODEV;
goto err3;
}
conf = hwa742_read_reg(HWA742_CONFIG_REG);
pr_info(MODULE_NAME ": Epson HWA742 LCD controller rev. %d "
"initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07);
if (!(hwa742_read_reg(HWA742_PLL_DIV_REG) & 0x80)) {
pr_err("controller not initialized by the bootloader\n");
r = -ENODEV;
goto err2;
}
hwa742.max_transmit_size = hwa742.extif->max_transmit_size;
hwa742.update_mode = OMAPFB_UPDATE_DISABLED;
hwa742.auto_update_window.x = 0;
hwa742.auto_update_window.y = 0;
hwa742.auto_update_window.width = fbdev->panel->x_res;
hwa742.auto_update_window.height = fbdev->panel->y_res;
hwa742.auto_update_window.format = 0;
init_timer(&hwa742.auto_update_timer);
hwa742.auto_update_timer.function = hwa742_update_window_auto;
hwa742.auto_update_timer.data = 0;
hwa742.prev_color_mode = -1;
hwa742.prev_flags = 0;
hwa742.fbdev = fbdev;
INIT_LIST_HEAD(&hwa742.free_req_list);
INIT_LIST_HEAD(&hwa742.pending_req_list);
for (i = 0; i < ARRAY_SIZE(hwa742.req_pool); i++)
list_add(&hwa742.req_pool[i].entry, &hwa742.free_req_list);
BUG_ON(i <= IRQ_REQ_POOL_SIZE);
sema_init(&hwa742.req_sema, i - IRQ_REQ_POOL_SIZE);
return 0;
err3:
hwa742.extif->cleanup();
err2:
hwa742.int_ctrl->cleanup();
err1:
clk_disable(hwa742.sys_ck);
clk_put(hwa742.sys_ck);
return r;
}
static void hwa742_cleanup(void)
{
hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED);
hwa742.extif->cleanup();
hwa742.int_ctrl->cleanup();
clk_disable(hwa742.sys_ck);
clk_put(hwa742.sys_ck);
}
struct lcd_ctrl hwa742_ctrl = {
.name = "hwa742",
.init = hwa742_init,
.cleanup = hwa742_cleanup,
.get_caps = hwa742_get_caps,
.set_update_mode = hwa742_set_update_mode,
.get_update_mode = hwa742_get_update_mode,
.setup_plane = hwa742_setup_plane,
.enable_plane = hwa742_enable_plane,
.update_window = hwa742_update_window_async,
.sync = hwa742_sync,
.suspend = hwa742_suspend,
.resume = hwa742_resume,
};
#ifndef __HWA742_H
#define __HWA742_H
#include <linux/fb.h>
#include <asm/arch/omapfb.h>
#define HWA742_EVENT_READY 1
#define HWA742_EVENT_DISABLED 2
struct hwa742_notifier_block {
struct notifier_block nb;
void *data;
};
typedef int (*hwa742_notifier_callback_t)(struct hwa742_notifier_block *,
unsigned long event,
struct omapfb_device *fbdev);
extern void hwa742_read_id(int *rev_code, int *config);
extern int hwa742_register_client(struct hwa742_notifier_block *hwa742_nb,
hwa742_notifier_callback_t callback,
void *callback_data);
extern int hwa742_unregister_client(struct hwa742_notifier_block *hwa742_nb);
extern int hwa742_update_window_async(struct omapfb_update_window *win,
void (*complete_callback)(void *arg),
void *complete_callback_data);
#endif
......@@ -22,6 +22,7 @@
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/arch/mux.h>
#include <asm/arch/omapfb.h>
......@@ -131,3 +132,52 @@ struct lcd_panel h2_panel = {
.get_caps = h2_panel_get_caps,
};
static int h2_panel_probe(struct platform_device *pdev)
{
DBGENTER(1);
omapfb_register_panel(&h2_panel);
return 0;
}
static int h2_panel_remove(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
static int h2_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
{
DBGENTER(1);
return 0;
}
static int h2_panel_resume(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
struct platform_driver h2_panel_driver = {
.probe = h2_panel_probe,
.remove = h2_panel_remove,
.suspend = h2_panel_suspend,
.resume = h2_panel_resume,
.driver = {
.name = "lcd_h2",
.owner = THIS_MODULE,
},
};
static int h2_panel_drv_init(void)
{
return platform_driver_register(&h2_panel_driver);
}
static void h2_panel_drv_cleanup(void)
{
platform_driver_unregister(&h2_panel_driver);
}
module_init(h2_panel_drv_init);
module_exit(h2_panel_drv_cleanup);
......@@ -22,6 +22,7 @@
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/arch/gpio.h>
#include <asm/arch/tps65010.h>
......@@ -110,3 +111,52 @@ struct lcd_panel h3_panel = {
.get_caps = h3_panel_get_caps,
};
static int h3_panel_probe(struct platform_device *pdev)
{
DBGENTER(1);
omapfb_register_panel(&h3_panel);
return 0;
}
static int h3_panel_remove(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
static int h3_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
{
DBGENTER(1);
return 0;
}
static int h3_panel_resume(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
struct platform_driver h3_panel_driver = {
.probe = h3_panel_probe,
.remove = h3_panel_remove,
.suspend = h3_panel_suspend,
.resume = h3_panel_resume,
.driver = {
.name = "lcd_h3",
.owner = THIS_MODULE,
},
};
static int h3_panel_drv_init(void)
{
return platform_driver_register(&h3_panel_driver);
}
static void h3_panel_drv_cleanup(void)
{
platform_driver_unregister(&h3_panel_driver);
}
module_init(h3_panel_drv_init);
module_exit(h3_panel_drv_cleanup);
......@@ -22,6 +22,7 @@
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/arch/omapfb.h>
......@@ -84,3 +85,52 @@ struct lcd_panel h4_panel = {
.get_caps = h4_panel_get_caps,
};
static int h4_panel_probe(struct platform_device *pdev)
{
DBGENTER(1);
omapfb_register_panel(&h4_panel);
return 0;
}
static int h4_panel_remove(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
static int h4_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
{
DBGENTER(1);
return 0;
}
static int h4_panel_resume(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
struct platform_driver h4_panel_driver = {
.probe = h4_panel_probe,
.remove = h4_panel_remove,
.suspend = h4_panel_suspend,
.resume = h4_panel_resume,
.driver = {
.name = "lcd_h4",
.owner = THIS_MODULE,
},
};
static int h4_panel_drv_init(void)
{
return platform_driver_register(&h4_panel_driver);
}
static void h4_panel_drv_cleanup(void)
{
platform_driver_unregister(&h4_panel_driver);
}
module_init(h4_panel_drv_init);
module_exit(h4_panel_drv_cleanup);
......@@ -22,6 +22,7 @@
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/io.h>
......@@ -93,3 +94,52 @@ struct lcd_panel innovator1510_panel = {
.get_caps = innovator1510_panel_get_caps,
};
static int innovator1510_panel_probe(struct platform_device *pdev)
{
DBGENTER(1);
omapfb_register_panel(&innovator1510_panel);
return 0;
}
static int innovator1510_panel_remove(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
static int innovator1510_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
{
DBGENTER(1);
return 0;
}
static int innovator1510_panel_resume(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
struct platform_driver innovator1510_panel_driver = {
.probe = innovator1510_panel_probe,
.remove = innovator1510_panel_remove,
.suspend = innovator1510_panel_suspend,
.resume = innovator1510_panel_resume,
.driver = {
.name = "lcd_inn1510",
.owner = THIS_MODULE,
},
};
static int innovator1510_panel_drv_init(void)
{
return platform_driver_register(&innovator1510_panel_driver);
}
static void innovator1510_panel_drv_cleanup(void)
{
platform_driver_unregister(&innovator1510_panel_driver);
}
module_init(innovator1510_panel_drv_init);
module_exit(innovator1510_panel_drv_cleanup);
......@@ -121,3 +121,52 @@ struct lcd_panel innovator1610_panel = {
.get_caps = innovator1610_panel_get_caps,
};
static int innovator1610_panel_probe(struct platform_device *pdev)
{
DBGENTER(1);
omapfb_register_panel(&innovator1610_panel);
return 0;
}
static int innovator1610_panel_remove(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
static int innovator1610_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
{
DBGENTER(1);
return 0;
}
static int innovator1610_panel_resume(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
struct platform_driver innovator1610_panel_driver = {
.probe = innovator1610_panel_probe,
.remove = innovator1610_panel_remove,
.suspend = innovator1610_panel_suspend,
.resume = innovator1610_panel_resume,
.driver = {
.name = "lcd_inn1610",
.owner = THIS_MODULE,
},
};
static int innovator1610_panel_drv_init(void)
{
return platform_driver_register(&innovator1610_panel_driver);
}
static void innovator1610_panel_drv_cleanup(void)
{
platform_driver_unregister(&innovator1610_panel_driver);
}
module_init(innovator1610_panel_drv_init);
module_exit(innovator1610_panel_drv_cleanup);
#include <linux/module.h>
#include <linux/delay.h>
#include <asm/arch/hardware.h>
#include <asm/arch/gpio.h>
#include <asm/arch/board.h>
#include <asm/arch/omapfb.h>
#include <asm/arch/lcd_lph8923.h>
#include <linux/spi/spi.h>
#include <asm/arch/mcspi.h>
#include "../../cbus/tahvo.h"
/* #define OMAPFB_DBG 1 */
#include "debug.h"
#define LPH8923_MODULE_NAME "lcd_lph8923"
#define LPH8923_VER_BUGGY 1
#define LPH8923_VER_NON_BUGGY 3
struct {
int enabled;
int version;
u8 display_id[3];
unsigned int saved_bklight_level;
unsigned long hw_guard_end; /* next value of jiffies
when we can issue the
next sleep in/out command */
unsigned long hw_guard_wait; /* max guard time in jiffies */
struct omapfb_device *fbdev;
struct spi_device *spi;
} lph8923;
#define LPH8923_CMD_READ_DISP_ID 0x04
#define LPH8923_CMD_READ_RED 0x06
#define LPH8923_CMD_READ_GREEN 0x07
#define LPH8923_CMD_READ_BLUE 0x08
#define LPH8923_CMD_READ_DISP_STATUS 0x09
#define LPH8923_CMD_SLEEP_IN 0x10
#define LPH8923_CMD_SLEEP_OUT 0x11
#define LPH8923_CMD_DISP_OFF 0x28
#define LPH8923_CMD_DISP_ON 0x29
static struct lcd_panel lph8923_panel;
#define LPH8923_SPEED_HZ 12000000
static int lph8923_spi_probe(struct spi_device *spi)
{
DBGENTER(1);
spi->dev.power.power_state = PMSG_ON;
spi->mode = SPI_MODE_0;
spi->bits_per_word = 9;
spi_setup(spi);
DBGPRINT(1, "spi %p\n", spi);
lph8923.spi = spi;
omapfb_register_panel(&lph8923_panel);
return 0;
}
static int lph8923_spi_remove(struct spi_device *spi)
{
DBGENTER(1);
lph8923.spi = NULL;
return 0;
}
static struct spi_driver lph8923_spi_driver = {
.driver = {
.name = LPH8923_MODULE_NAME,
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = lph8923_spi_probe,
.remove = __devexit_p(lph8923_spi_remove),
};
static int lph8923_drv_init(void)
{
spi_register_driver(&lph8923_spi_driver);
return 0;
}
module_init(lph8923_drv_init);
static void lph8923_drv_cleanup(void)
{
spi_unregister_driver(&lph8923_spi_driver);
}
module_exit(lph8923_drv_cleanup);
static void set_spi_data_width(int width)
{
if (lph8923.spi->bits_per_word != width) {
lph8923.spi->bits_per_word = width;
spi_setup(lph8923.spi);
}
}
static void lph8923_read(int cmd, u8 *buf, int len)
{
struct spi_message m;
struct spi_transfer t;
u16 w;
BUG_ON(lph8923.spi == NULL);
spi_message_init(&m);
m.spi = lph8923.spi;
if (len > 1) {
cmd <<= 1;
set_spi_data_width(10);
} else
set_spi_data_width(9);
w = cmd;
t.cs_change = len ? 1 : 0;
t.tx_buf = &w;
t.rx_buf = NULL;
t.len = 2;
spi_message_add_tail(&t, &m);
spi_sync(m.spi, &m);
if (!len)
return;
spi_message_init(&m);
m.spi = lph8923.spi;
t.cs_change = 0;
t.tx_buf = NULL;
t.rx_buf = buf;
t.len = len;
set_spi_data_width(8);
spi_message_add_tail(&t, &m);
spi_sync(m.spi, &m);
}
static void lph8923_write(int cmd, const u8 *buf, int len)
{
struct spi_message m;
struct spi_transfer t;
u16 w;
int i;
BUG_ON(lph8923.spi == NULL);
spi_message_init(&m);
m.spi = lph8923.spi;
if (len > 1) {
cmd <<= 1;
set_spi_data_width(10);
} else {
set_spi_data_width(9);
}
t.cs_change = 0;
w = cmd;
t.tx_buf = &w;
t.rx_buf = NULL;
t.len = 2;
spi_message_add_tail(&t, &m);
spi_sync(m.spi, &m);
if (!len)
return;
set_spi_data_width(9);
t.tx_buf = &w;
for (i = 0; i < len; i++) {
u16 w;
spi_message_init(&m);
m.spi = lph8923.spi;
spi_message_add_tail(&t, &m);
w = buf[i] | (1 << 8);
spi_sync(m.spi, &m);
}
}
static inline void lph8923_cmd(int cmd)
{
lph8923_write(cmd, NULL, 0);
}
struct cmd_data {
u8 cmd;
u8 len;
const u8 *data;
} __attribute__ ((packed));;
static const struct cmd_data init_cmds_buggy_lph8923[] = {
{ 0xb0, 1, "\x08" },
{ 0xb1, 2, "\x0b\x1c" },
{ 0xb2, 4, "\x00\x00\x00\x00" },
{ 0xb3, 4, "\x00\x00\x00\x00" },
{ 0xb4, 1, "\x87" },
{ 0xb5, 4, "\x37\x07\x37\x07" },
{ 0xb6, 2, "\x64\x24" },
{ 0xb7, 1, "\x90" },
{ 0xb8, 3, "\x10\x11\x20" },
{ 0xb9, 2, "\x31\x02" },
{ 0xba, 3, "\x04\xa3\x9d" },
{ 0xbb, 4, "\x15\xb2\x8c\x00" },
{ 0xc2, 3, "\x02\x00\x00" },
};
static const struct cmd_data init_cmds_non_buggy_lph8923[] = {
{ 0xc2, 3, "\x02\x00\x00" },
};
static inline void lph8923_set_16bit_mode(void)
{
lph8923_write(0x3a, "\x50", 1);
}
static void lph8923_send_init_string(void)
{
int c;
const struct cmd_data *cd;
switch (lph8923.version) {
case LPH8923_VER_BUGGY:
c = sizeof(init_cmds_buggy_lph8923)/sizeof(init_cmds_buggy_lph8923[0]);
cd = init_cmds_buggy_lph8923;
break;
case LPH8923_VER_NON_BUGGY:
default:
c = sizeof(init_cmds_non_buggy_lph8923)/sizeof(init_cmds_non_buggy_lph8923[0]);
cd = init_cmds_non_buggy_lph8923;
break;
}
while (c--) {
lph8923_write(cd->cmd, cd->data, cd->len);
cd++;
}
lph8923_set_16bit_mode();
}
static void hw_guard_start(int guard_msec)
{
lph8923.hw_guard_wait = msecs_to_jiffies(guard_msec);
lph8923.hw_guard_end = jiffies + lph8923.hw_guard_wait;
}
static void hw_guard_wait(void)
{
unsigned long wait = lph8923.hw_guard_end - jiffies;
if ((long)wait > 0 && wait <= lph8923.hw_guard_wait) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(wait);
}
}
static void lph8923_set_sleep_mode(int on)
{
int cmd, sleep_time = 5;
if (on)
cmd = LPH8923_CMD_SLEEP_IN;
else
cmd = LPH8923_CMD_SLEEP_OUT;
hw_guard_wait();
lph8923_cmd(cmd);
hw_guard_start(120);
/* When we enable the panel, it seems we _have_ to sleep
* 120 ms before sending the init string */
if (!on)
sleep_time = 120;
msleep(sleep_time);
}
static void lph8923_set_display_state(int enabled)
{
int cmd = enabled ? LPH8923_CMD_DISP_ON : LPH8923_CMD_DISP_OFF;
lph8923_cmd(cmd);
}
static void lph8923_detect(void)
{
lph8923_read(LPH8923_CMD_READ_DISP_ID, lph8923.display_id, 3);
printk(KERN_INFO "Moscow display id: %02x%02x%02x\n",
lph8923.display_id[0], lph8923.display_id[1],
lph8923.display_id[2]);
if (lph8923.display_id[0] == 0x45) {
lph8923.version = LPH8923_VER_NON_BUGGY;
printk(KERN_INFO "Non-buggy Moscow detected\n");
return;
} else {
lph8923.version = LPH8923_VER_BUGGY;
printk(KERN_INFO "Buggy Moscow detected\n");
}
}
static int lph8923_enabled(void)
{
u32 disp_status;
int enabled;
lph8923_read(LPH8923_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4);
disp_status = __be32_to_cpu(disp_status);
enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10));
DBGPRINT(1, ": panel %senabled by bootloader (status 0x%04x)\n",
enabled ? "" : "not ", disp_status);
return enabled;
}
static int lph8923_panel_init(struct omapfb_device *fbdev)
{
lph8923.fbdev = fbdev;
lph8923.enabled = 1;
lph8923_detect();
if (lph8923.version == LPH8923_VER_NON_BUGGY)
lph8923.enabled = lph8923_enabled();
else
/* We can't be sure, but assume the bootloader
* enabled it already */
lph8923.enabled = 1;
return 0;
}
static void lph8923_panel_cleanup(void)
{
}
static int lph8923_panel_set_bklight_level(unsigned int level)
{
if (level > 0xf)
return -EINVAL;
if (!lph8923.enabled) {
lph8923.saved_bklight_level = level;
return 0;
}
level = level * tahvo_get_max_backlight_level() / 0x0f;
tahvo_set_backlight_level(level);
return 0;
}
static unsigned int lph8923_panel_get_bklight_level(void)
{
return tahvo_get_backlight_level() * 0x0f /
tahvo_get_max_backlight_level();
}
static unsigned int lph8923_panel_get_bklight_max(void)
{
return 0x0f;
}
static int lph8923_panel_enable(void)
{
if (lph8923.enabled)
return 0;
lph8923_set_sleep_mode(0);
lph8923.enabled = 1;
lph8923_send_init_string();
lph8923_set_display_state(1);
lph8923_panel_set_bklight_level(lph8923.saved_bklight_level);
return 0;
}
static void lph8923_panel_disable(void)
{
if (!lph8923.enabled)
return;
lph8923.saved_bklight_level = lph8923_panel_get_bklight_level();
lph8923_panel_set_bklight_level(0);
lph8923_set_display_state(0);
lph8923_set_sleep_mode(1);
lph8923.enabled = 0;
}
static unsigned long lph8923_panel_get_caps(void)
{
return OMAPFB_CAPS_SET_BACKLIGHT;
}
static u16 read_first_pixel(void)
{
u8 b;
u16 pixel;
lph8923_read(LPH8923_CMD_READ_RED, &b, 1);
pixel = (b >> 1) << 11;
lph8923_read(LPH8923_CMD_READ_GREEN, &b, 1);
pixel |= b << 5;
lph8923_read(LPH8923_CMD_READ_BLUE, &b, 1);
pixel |= (b >> 1);
return pixel;
}
static int lph8923_panel_test(int test_num)
{
static const u16 test_values[4] = {
0x0000, 0xffff, 0xaaaa, 0x5555,
};
int i;
if (test_num != LCD_LPH8923_TEST_RGB_LINES)
return LCD_LPH8923_TEST_INVALID;
for (i = 0; i < ARRAY_SIZE(test_values); i++) {
int delay;
unsigned long tmo;
omapfb_write_first_pixel(lph8923.fbdev, test_values[i]);
tmo = jiffies + msecs_to_jiffies(100);
delay = msecs_to_jiffies(25);
while (1) {
u16 pixel;
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(delay);
pixel = read_first_pixel();
if (pixel == test_values[i])
break;
if (time_after(jiffies, tmo)) {
printk(KERN_ERR "Moscow RGB I/F test failed: "
"expecting %04x, got %04x\n",
test_values[i], pixel);
return LCD_LPH8923_TEST_FAILED;
}
delay = msecs_to_jiffies(10);
}
}
return 0;
}
static struct lcd_panel lph8923_panel = {
.name = "lph8923",
.config = OMAP_LCDC_PANEL_TFT,
.bpp = 16,
.data_lines = 16,
.x_res = 800,
.y_res = 480,
.pixel_clock = 21940,
.hsw = 50,
.hfp = 20,
.hbp = 15,
.vsw = 2,
.vfp = 1,
.vbp = 3,
.init = lph8923_panel_init,
.cleanup = lph8923_panel_cleanup,
.enable = lph8923_panel_enable,
.disable = lph8923_panel_disable,
.get_caps = lph8923_panel_get_caps,
.set_bklight_level= lph8923_panel_set_bklight_level,
.get_bklight_level= lph8923_panel_get_bklight_level,
.get_bklight_max= lph8923_panel_get_bklight_max,
.run_test = lph8923_panel_test,
};
......@@ -23,6 +23,7 @@
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/arch/gpio.h>
#include <asm/arch/mux.h>
......@@ -113,3 +114,52 @@ struct lcd_panel osk_panel = {
.get_caps = osk_panel_get_caps,
};
static int osk_panel_probe(struct platform_device *pdev)
{
DBGENTER(1);
omapfb_register_panel(&osk_panel);
return 0;
}
static int osk_panel_remove(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
static int osk_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
{
DBGENTER(1);
return 0;
}
static int osk_panel_resume(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
struct platform_driver osk_panel_driver = {
.probe = osk_panel_probe,
.remove = osk_panel_remove,
.suspend = osk_panel_suspend,
.resume = osk_panel_resume,
.driver = {
.name = "lcd_osk",
.owner = THIS_MODULE,
},
};
static int osk_panel_drv_init(void)
{
return platform_driver_register(&osk_panel_driver);
}
static void osk_panel_drv_cleanup(void)
{
platform_driver_unregister(&osk_panel_driver);
}
module_init(osk_panel_drv_init);
module_exit(osk_panel_drv_cleanup);
......@@ -25,6 +25,7 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <asm/arch/mux.h>
#include <asm/arch/gpio.h>
......@@ -305,3 +306,52 @@ struct lcd_panel p2_panel = {
.get_caps = p2_panel_get_caps,
};
static int p2_panel_probe(struct platform_device *pdev)
{
DBGENTER(1);
omapfb_register_panel(&p2_panel);
return 0;
}
static int p2_panel_remove(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
static int p2_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
{
DBGENTER(1);
return 0;
}
static int p2_panel_resume(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
struct platform_driver p2_panel_driver = {
.probe = p2_panel_probe,
.remove = p2_panel_remove,
.suspend = p2_panel_suspend,
.resume = p2_panel_resume,
.driver = {
.name = "lcd_p2",
.owner = THIS_MODULE,
},
};
static int p2_panel_drv_init(void)
{
return platform_driver_register(&p2_panel_driver);
}
static void p2_panel_drv_cleanup(void)
{
platform_driver_unregister(&p2_panel_driver);
}
module_init(p2_panel_drv_init);
module_exit(p2_panel_drv_cleanup);
......@@ -22,6 +22,7 @@
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/io.h>
......@@ -89,3 +90,52 @@ struct lcd_panel palmte_panel = {
.get_caps = palmte_panel_get_caps,
};
static int palmte_panel_probe(struct platform_device *pdev)
{
DBGENTER(1);
omapfb_register_panel(&palmte_panel);
return 0;
}
static int palmte_panel_remove(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
static int palmte_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
{
DBGENTER(1);
return 0;
}
static int palmte_panel_resume(struct platform_device *pdev)
{
DBGENTER(1);
return 0;
}
struct platform_driver palmte_panel_driver = {
.probe = palmte_panel_probe,
.remove = palmte_panel_remove,
.suspend = palmte_panel_suspend,
.resume = palmte_panel_resume,
.driver = {
.name = "lcd_palmte",
.owner = THIS_MODULE,
},
};
static int palmte_panel_drv_init(void)
{
return platform_driver_register(&palmte_panel_driver);
}
static void palmte_panel_drv_cleanup(void)
{
platform_driver_unregister(&palmte_panel_driver);
}
module_init(palmte_panel_drv_init);
module_exit(palmte_panel_drv_cleanup);
......@@ -30,6 +30,7 @@
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
#include <linux/clk.h>
#include <asm/arch/dma.h>
......@@ -37,7 +38,7 @@
#include <asm/mach-types.h>
/* #define OMAPFB_DBG 2 */
/* #define OMAPFB_DBG 1 */
#include "debug.h"
......@@ -87,13 +88,17 @@ enum lcdc_load_mode {
static struct omap_lcd_controller {
enum omapfb_update_mode update_mode;
int ext_mode;
unsigned long frame_offset;
int screen_width;
int xres;
int yres;
enum omapfb_color_format color_mode;
int bpp;
int palette_org;
void *palette_virt;
dma_addr_t palette_phys;
int palette_code;
int palette_size;
......@@ -103,6 +108,10 @@ static struct omap_lcd_controller {
struct clk *lcd_ck;
struct omapfb_device *fbdev;
void (*dma_callback)(void *data);
void *dma_callback_data;
int fbmem_allocated;
dma_addr_t vram_phys;
void *vram_virt;
unsigned long vram_size;
......@@ -207,18 +216,21 @@ static void setup_lcd_dma(void)
OMAP_DMA_DATA_TYPE_S32,
};
struct fb_var_screeninfo *var = &omap_lcdc.fbdev->fb_info->var;
struct lcd_panel *panel = omap_lcdc.fbdev->panel;
unsigned long src;
int esize, xelem, yelem;
src = omap_lcdc.vram_phys + PAGE_ALIGN(MAX_PALETTE_SIZE) +
omap_lcdc.frame_offset;
src = omap_lcdc.vram_phys + omap_lcdc.frame_offset;
switch (var->rotate) {
case 0:
esize = omap_lcdc.fbdev->mirror || (src & 3) ? 2 : 4;
xelem = panel->x_res * omap_lcdc.bpp / 8 / esize;
yelem = panel->y_res;
if (omap_lcdc.fbdev->mirror || (src & 3) ||
omap_lcdc.color_mode == OMAPFB_COLOR_YUV420 ||
(omap_lcdc.xres & 1))
esize = 2;
else
esize = 4;
xelem = omap_lcdc.xres * omap_lcdc.bpp / 8 / esize;
yelem = omap_lcdc.yres;
break;
case 90:
case 180:
......@@ -227,21 +239,27 @@ static void setup_lcd_dma(void)
BUG();
}
esize = 2;
xelem = panel->y_res * omap_lcdc.bpp / 16;
yelem = panel->x_res;
xelem = omap_lcdc.yres * omap_lcdc.bpp / 16;
yelem = omap_lcdc.xres;
break;
default:
BUG();
return;
}
DBGPRINT(1, "setup_dma: src %#010lx esize %d xelem %d yelem %d\n",
DBGPRINT(2, "setup_dma: src %#010lx 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_omap15xx()) {
int bpp = omap_lcdc.bpp;
/* YUV support is only for external mode when we have the
* YUV window embedded in a 16bpp frame buffer.
*/
if (omap_lcdc.color_mode == OMAPFB_COLOR_YUV420)
bpp = 16;
/* Set virtual xres elem size */
omap_set_lcd_dma_b1_vxres(
omap_lcdc.screen_width * omap_lcdc.bpp / 8 / esize);
omap_lcdc.screen_width * bpp / 8 / esize);
/* Setup transformations */
omap_set_lcd_dma_b1_rotation(var->rotate);
omap_set_lcd_dma_b1_mirror(omap_lcdc.fbdev->mirror);
......@@ -305,7 +323,7 @@ static int omap_lcdc_setup_plane(int plane, int channel_out,
struct lcd_panel *panel = omap_lcdc.fbdev->panel;
int rot_x, rot_y;
DBGENTER(1);
DBGENTER(2);
if (var->rotate == 0) {
rot_x = panel->x_res;
......@@ -315,7 +333,7 @@ static int omap_lcdc_setup_plane(int plane, int channel_out,
rot_y = panel->x_res;
}
if (plane != 0 || channel_out != 0 || pos_x != 0 || pos_y != 0 ||
width != rot_x || height != rot_y) {
width > rot_x || height > rot_y) {
DBGPRINT(1, "invalid plane params plane %d pos_x %d "
"pos_y %d w %d h %d\n", plane, pos_x, pos_y,
width, height);
......@@ -323,6 +341,8 @@ static int omap_lcdc_setup_plane(int plane, int channel_out,
}
omap_lcdc.frame_offset = offset;
omap_lcdc.xres = width;
omap_lcdc.yres = height;
omap_lcdc.screen_width = screen_width;
omap_lcdc.color_mode = color_mode;
......@@ -337,6 +357,18 @@ static int omap_lcdc_setup_plane(int plane, int channel_out,
omap_lcdc.palette_code = 0x4000;
omap_lcdc.palette_size = 32;
break;
case OMAPFB_COLOR_YUV420:
if (omap_lcdc.ext_mode) {
omap_lcdc.bpp = 12;
break;
}
/* fallthrough */
case OMAPFB_COLOR_YUV422:
if (omap_lcdc.ext_mode) {
omap_lcdc.bpp = 16;
break;
}
/* fallthrough */
default:
/* FIXME: other BPPs.
* bpp1: code 0, size 256
......@@ -348,8 +380,10 @@ static int omap_lcdc_setup_plane(int plane, int channel_out,
return -1;
}
omap_lcdc.palette_org = PAGE_ALIGN(MAX_PALETTE_SIZE) -
omap_lcdc.palette_size;
if (omap_lcdc.ext_mode) {
setup_lcd_dma();
return 0;
}
if (omap_lcdc.update_mode == OMAPFB_AUTO_UPDATE) {
disable_controller();
......@@ -358,14 +392,17 @@ static int omap_lcdc_setup_plane(int plane, int channel_out,
enable_controller();
}
DBGLEAVE(1);
DBGLEAVE(2);
return 0;
}
static int omap_lcdc_enable_plane(int plane, int enable)
{
if (plane != 0 || enable != 1)
DBGPRINT(2, "plane %d enable %d update_mode %d ext_mode %d\n",
plane, enable, omap_lcdc.update_mode,
omap_lcdc.ext_mode);
if (plane != OMAPFB_PLANE_GFX)
return -EINVAL;
return 0;
......@@ -381,12 +418,12 @@ static void load_palette(void)
DBGENTER(1);
palette = (u16 *)((u8 *)omap_lcdc.vram_virt + omap_lcdc.palette_org);
palette = (u16 *)omap_lcdc.palette_virt;
*(u16 *)palette &= 0x0fff;
*(u16 *)palette |= omap_lcdc.palette_code;
omap_set_lcd_dma_b1(omap_lcdc.vram_phys + omap_lcdc.palette_org,
omap_set_lcd_dma_b1(omap_lcdc.palette_phys,
omap_lcdc.palette_size / 4 + 1, 1, OMAP_DMA_DATA_TYPE_S32);
omap_set_lcd_dma_single_transfer(1);
......@@ -403,16 +440,45 @@ static void load_palette(void)
disable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE);
omap_stop_lcd_dma();
omap_set_lcd_dma_single_transfer(omap_lcdc.ext_mode);
DBGLEAVE(1);
}
/* Used only in internal controller mode */
static int omap_lcdc_setcolreg(u_int regno, u16 red, u16 green, u16 blue,
u16 transp, int update_hw_pal)
{
u16 *palette;
if (omap_lcdc.color_mode != OMAPFB_COLOR_CLUT_8BPP || regno > 255)
return -EINVAL;
palette = (u16 *)omap_lcdc.palette_virt;
palette[regno] &= ~0x0fff;
palette[regno] |= ((red >> 12) << 8) | ((green >> 12) << 4 ) |
(blue >> 12);
if (update_hw_pal) {
disable_controller();
omap_stop_lcd_dma();
load_palette();
setup_lcd_dma();
set_load_mode(OMAP_LCDC_LOAD_FRAME);
enable_controller();
}
return 0;
}
static void calc_ck_div(int is_tft, int pck, int *pck_div)
{
unsigned long lck;
pck = max(1, pck);
lck = clk_get_rate(omap_lcdc.lcd_ck);
*pck_div = lck / pck;
*pck_div = (lck + pck - 1) / pck;
if (is_tft)
*pck_div = max(2, *pck_div);
else
......@@ -486,7 +552,9 @@ static void inline setup_regs(void)
}
/* Configure the LCD controller, download the color palette and start a looped
* DMA transfer of the frame image data. */
* DMA transfer of the frame image data. Called only in internal
* controller mode.
*/
static int omap_lcdc_set_update_mode(enum omapfb_update_mode mode)
{
int r = 0;
......@@ -527,6 +595,7 @@ static enum omapfb_update_mode omap_lcdc_get_update_mode(void)
return omap_lcdc.update_mode;
}
/* PM code called only in internal controller mode */
static void omap_lcdc_suspend(void)
{
if (omap_lcdc.update_mode == OMAPFB_AUTO_UPDATE) {
......@@ -547,12 +616,184 @@ static void omap_lcdc_resume(void)
}
}
static unsigned long omap_lcdc_get_caps(void)
{
return 0;
}
static void omap_lcdc_get_vram_layout(unsigned long *size, void **virt,
dma_addr_t *phys)
{
*size = omap_lcdc.vram_size - PAGE_ALIGN(MAX_PALETTE_SIZE);
*virt = (u8 *)omap_lcdc.vram_virt + PAGE_ALIGN(MAX_PALETTE_SIZE);
*phys = omap_lcdc.vram_phys + PAGE_ALIGN(MAX_PALETTE_SIZE);
*size = omap_lcdc.vram_size;
*virt = (u8 *)omap_lcdc.vram_virt;
*phys = omap_lcdc.vram_phys;
}
int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data)
{
BUG_ON(callback == NULL);
if (omap_lcdc.dma_callback)
return -EBUSY;
else {
omap_lcdc.dma_callback = callback;
omap_lcdc.dma_callback_data = data;
}
return 0;
}
EXPORT_SYMBOL(omap_lcdc_set_dma_callback);
void omap_lcdc_free_dma_callback(void)
{
omap_lcdc.dma_callback = NULL;
}
EXPORT_SYMBOL(omap_lcdc_free_dma_callback);
static void lcdc_dma_handler(u16 status, void *data)
{
DBGENTER(2);
if (omap_lcdc.dma_callback)
omap_lcdc.dma_callback(omap_lcdc.dma_callback_data);
}
static int mmap_kern(void)
{
struct vm_struct *kvma;
struct vm_area_struct vma;
pgprot_t pgprot;
unsigned long vaddr;
DBGENTER(1);
kvma = get_vm_area(omap_lcdc.vram_size, VM_IOREMAP);
if (kvma == NULL) {
pr_err("can't get kernel vm area\n");
return -ENOMEM;
}
vma.vm_mm = &init_mm;
vaddr = (unsigned long)kvma->addr;
vma.vm_start = vaddr;
vma.vm_end = vaddr + omap_lcdc.vram_size;
pgprot = pgprot_writecombine(pgprot_kernel);
if (io_remap_pfn_range(&vma, vaddr,
omap_lcdc.vram_phys >> PAGE_SHIFT,
omap_lcdc.vram_size, pgprot) < 0) {
pr_err("kernel mmap for FB memory failed\n");
return -EAGAIN;
}
omap_lcdc.vram_virt = (void *)vaddr;
DBGLEAVE(1);
return 0;
}
static void unmap_kern(void)
{
vunmap(omap_lcdc.vram_virt);
}
static int alloc_palette_ram(void)
{
omap_lcdc.palette_virt = dma_alloc_writecombine(omap_lcdc.fbdev->dev,
MAX_PALETTE_SIZE, &omap_lcdc.palette_phys, GFP_KERNEL);
if (omap_lcdc.palette_virt == NULL) {
pr_err("failed to alloc palette memory\n");
return -ENOMEM;
}
memset(omap_lcdc.palette_virt, 0, MAX_PALETTE_SIZE);
return 0;
}
static void free_palette_ram(void)
{
dma_free_writecombine(omap_lcdc.fbdev->dev, MAX_PALETTE_SIZE,
omap_lcdc.palette_virt, omap_lcdc.palette_phys);
}
static int alloc_fbmem(int req_size)
{
int frame_size;
struct lcd_panel *panel = omap_lcdc.fbdev->panel;
frame_size = PAGE_ALIGN(panel->x_res * panel->bpp / 8 * panel->y_res);
if (req_size > frame_size)
frame_size = req_size;
omap_lcdc.vram_size = frame_size;
omap_lcdc.vram_virt = dma_alloc_writecombine(omap_lcdc.fbdev->dev,
omap_lcdc.vram_size, &omap_lcdc.vram_phys, GFP_KERNEL);
if (omap_lcdc.vram_virt == NULL) {
pr_err("unable to allocate FB DMA memory\n");
return -ENOMEM;
}
memset(omap_lcdc.vram_virt, 0, omap_lcdc.vram_size);
return 0;
}
static void free_fbmem(void)
{
dma_free_writecombine(omap_lcdc.fbdev->dev, omap_lcdc.vram_size,
omap_lcdc.vram_virt, omap_lcdc.vram_phys);
}
static int setup_fbmem(int req_size)
{
struct lcd_panel *panel = omap_lcdc.fbdev->panel;
struct omapfb_platform_data *conf;
int frame_size;
int r;
conf = omap_lcdc.fbdev->dev->platform_data;
if (conf->fbmem.fb_sram_size) {
pr_err("can't use FB SRAM in OMAP1\n");
return -EINVAL;
}
if (conf->fbmem.fb_sdram_size == 0) {
omap_lcdc.fbmem_allocated = 1;
if ((r = alloc_fbmem(req_size)) < 0)
return r;
return 0;
}
frame_size = PAGE_ALIGN(panel->x_res * panel->bpp / 8 * panel->y_res);
if (conf->fbmem.fb_sdram_size < frame_size) {
pr_err("invalid FB memory configuration\n");
return -EINVAL;
}
if (conf->fbmem.fb_sdram_size < req_size) {
pr_err("%d vram was requested, but only %u is available\n",
req_size, conf->fbmem.fb_sdram_size);
}
omap_lcdc.vram_phys = conf->fbmem.fb_sdram_start;
omap_lcdc.vram_size = conf->fbmem.fb_sdram_size;
if ((r = mmap_kern()) < 0)
return r;
DBGPRINT(1, "vram at %08x size %08lx mapped to 0x%p\n",
omap_lcdc.vram_phys, omap_lcdc.vram_size, omap_lcdc.vram_virt);
return 0;
}
static void cleanup_fbmem(void)
{
if (omap_lcdc.fbmem_allocated)
free_fbmem();
else
unmap_kern();
}
static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode,
......@@ -562,14 +803,13 @@ static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode,
u32 l;
int rate;
struct clk *tc_ck;
struct lcd_panel *panel = fbdev->panel;
int frame_size;
DBGENTER(1);
omap_lcdc.irq_mask = 0;
omap_lcdc.fbdev = fbdev;
omap_lcdc.ext_mode = ext_mode;
pr_info(MODULE_NAME ": init\n");
......@@ -612,27 +852,29 @@ static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode,
goto fail2;
}
r = omap_request_lcd_dma(NULL, NULL);
r = omap_request_lcd_dma(lcdc_dma_handler, NULL);
if (r) {
pr_err("unable to get LCD DMA\n");
goto fail3;
}
frame_size = panel->x_res * panel->bpp * panel->y_res / 8;
if (req_vram_size > frame_size)
frame_size = req_vram_size;
omap_lcdc.vram_size = PAGE_ALIGN(MAX_PALETTE_SIZE) + frame_size;
omap_lcdc.vram_virt = dma_alloc_writecombine(fbdev->dev,
omap_lcdc.vram_size, &omap_lcdc.vram_phys, GFP_KERNEL);
omap_set_lcd_dma_single_transfer(ext_mode);
omap_set_lcd_dma_ext_controller(ext_mode);
if (!ext_mode)
if ((r = alloc_palette_ram()) < 0)
goto fail4;
req_vram_size = 1024 * 1024;
if ((r = setup_fbmem(req_vram_size)) < 0)
goto fail5;
if (omap_lcdc.vram_virt == NULL) {
pr_err("unable to allocate fb DMA memory\n");
r = -ENOMEM;
goto fail4;
}
DBGLEAVE(1);
return 0;
fail5:
if (!ext_mode)
free_palette_ram();
fail4:
omap_free_lcd_dma();
fail3:
......@@ -648,46 +890,15 @@ fail0:
static void omap_lcdc_cleanup(void)
{
dma_free_writecombine(omap_lcdc.fbdev->dev, omap_lcdc.vram_size,
omap_lcdc.vram_virt, omap_lcdc.vram_phys);
if (!omap_lcdc.ext_mode)
free_palette_ram();
cleanup_fbmem();
omap_free_lcd_dma();
free_irq(OMAP_LCDC_IRQ, omap_lcdc.fbdev);
clk_disable(omap_lcdc.lcd_ck);
clk_put(omap_lcdc.lcd_ck);
}
static unsigned long omap_lcdc_get_caps(void)
{
return 0;
}
static int omap_lcdc_setcolreg(u_int regno, u16 red, u16 green, u16 blue,
u16 transp, int update_hw_pal)
{
u16 *palette;
if (omap_lcdc.color_mode != OMAPFB_COLOR_CLUT_8BPP || regno > 255)
return -EINVAL;
palette = (u16 *)((u8*)omap_lcdc.vram_virt +
PAGE_ALIGN(MAX_PALETTE_SIZE) - omap_lcdc.palette_size);
palette[regno] &= ~0x0fff;
palette[regno] |= ((red >> 12) << 8) | ((green >> 12) << 4 ) |
(blue >> 12);
if (update_hw_pal) {
disable_controller();
omap_stop_lcd_dma();
load_palette();
setup_lcd_dma();
set_load_mode(OMAP_LCDC_LOAD_FRAME);
enable_controller();
}
return 0;
}
struct lcd_ctrl omap1_int_ctrl = {
.name = "internal",
.init = omap_lcdc_init,
......
#ifndef LCDC_H
#define LCDC_H
int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data);
void omap_lcdc_free_dma_callback(void);
#endif
......@@ -80,64 +80,20 @@ static struct caps_table_struct {
* LCD panel
* ---------------------------------------------------------------------------
*/
extern struct lcd_panel h4_panel;
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 palmte_panel;
extern struct lcd_panel innovator1610_panel;
extern struct lcd_panel innovator1510_panel;
extern struct lcd_panel lph8923_panel;
extern struct lcd_panel apollon_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_H4
&h4_panel,
#endif
#ifdef CONFIG_MACH_OMAP_PERSEUS2
&p2_panel,
#endif
#ifdef CONFIG_MACH_OMAP_OSK
&osk_panel,
#endif
#ifdef CONFIG_MACH_OMAP_PALMTE
&palmte_panel,
#endif
#ifdef CONFIG_MACH_OMAP_INNOVATOR
#ifdef CONFIG_ARCH_OMAP15XX
&innovator1510_panel,
#endif
#ifdef CONFIG_ARCH_OMAP16XX
&innovator1610_panel,
#endif
#endif
#ifdef CONFIG_MACH_OMAP_APOLLON
&apollon_panel,
#endif
};
extern struct lcd_ctrl omap1_int_ctrl;
extern struct lcd_ctrl omap2_int_ctrl;
extern struct lcd_ctrl hwa742_ctrl;
extern struct lcd_ctrl blizzard_ctrl;
static struct lcd_ctrl *ctrls[] = {
#ifdef CONFIG_FB_OMAP_LCDC_INTERNAL
#ifdef CONFIG_ARCH_OMAP1
&omap1_int_ctrl,
#else
&omap2_int_ctrl,
#endif
#ifdef CONFIG_FB_OMAP_LCDC_HWA742
&hwa742_ctrl,
#endif
};
......@@ -192,7 +148,6 @@ static int ctrl_init(struct omapfb_device *fbdev)
fbdev->ctrl->get_vram_layout(&fbdev->vram_size, &fbdev->vram_virt_base,
&fbdev->vram_phys_base);
memset((void *)fbdev->vram_virt_base, 0, fbdev->vram_size);
DBGPRINT(1, "vram_phys %08x vram_virt %p vram_size=%lu\n",
fbdev->vram_phys_base, fbdev->vram_virt_base,
......@@ -339,12 +294,24 @@ static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
return 0;
}
static int omapfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
struct omapfb_device *fbdev = info->par;
int r;
omapfb_rqueue_lock(fbdev);
r = fbdev->ctrl->mmap(vma);
omapfb_rqueue_unlock(fbdev);
return r;
}
static void omapfb_update_full_screen(struct omapfb_device *fbdev);
static int omapfb_blank(int blank, struct fb_info *fbi)
{
struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par;
int do_update = 0;
int r = 0;
DBGENTER(1);
......@@ -359,7 +326,7 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
fbdev->state = OMAPFB_ACTIVE;
if (fbdev->ctrl->get_update_mode() ==
OMAPFB_MANUAL_UPDATE)
omapfb_update_full_screen(fbdev);
do_update = 1;
}
break;
case VESA_POWERDOWN:
......@@ -375,6 +342,9 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
}
omapfb_rqueue_unlock(fbdev);
if (do_update)
omapfb_update_full_screen(fbdev);
DBGLEAVE(1);
return r;
}
......@@ -763,8 +733,6 @@ static int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd,
} p;
int r = 0;
DBGENTER(2);
BUG_ON(!ops);
DBGPRINT(2, "cmd=%010x\n", cmd);
switch (cmd)
......@@ -792,6 +760,15 @@ static int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd,
(enum omapfb_update_mode __user *)arg))
r = -EFAULT;
break;
case OMAPFB_UPDATE_WINDOW_OLD:
if (copy_from_user(&p.update_window, (void __user *)arg,
sizeof(struct omapfb_update_window_old)))
r = -EFAULT;
else {
p.update_window.format = 0;
r = omapfb_update_win(fbdev, &p.update_window);
}
break;
case OMAPFB_UPDATE_WINDOW:
if (copy_from_user(&p.update_window, (void __user *)arg,
sizeof(p.update_window)))
......@@ -1134,45 +1111,21 @@ static void omapfb_free_resources(struct omapfb_device *fbdev, int state)
}
}
static int omapfb_find_panel(struct omapfb_device *fbdev)
{
const struct omap_lcd_config *conf;
char name[17];
int i;
conf = (struct omap_lcd_config *)fbdev->dev->platform_data;
fbdev->panel = NULL;
if (conf == NULL)
return -1;
strncpy(name, conf->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)
{
struct omap_lcd_config *conf;
struct omapfb_platform_data *conf;
char name[17];
int i;
conf = (struct omap_lcd_config *)fbdev->dev->platform_data;
conf = (struct omapfb_platform_data *)fbdev->dev->platform_data;
fbdev->ctrl = NULL;
if (conf == NULL)
if (conf == NULL) {
DBGPRINT(1, "omap_lcd_config not found\n");
return -1;
}
strncpy(name, conf->ctrl_name, sizeof(name) - 1);
strncpy(name, conf->lcd.ctrl_name, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';
if (strcmp(name, "internal") == 0) {
......@@ -1181,14 +1134,17 @@ static int omapfb_find_ctrl(struct omapfb_device *fbdev)
}
for (i = 0; i < ARRAY_SIZE(ctrls); i++) {
DBGPRINT(1, "ctrl %s\n", ctrls[i]->name);
if (strcmp(ctrls[i]->name, name) == 0) {
fbdev->ctrl = ctrls[i];
break;
}
}
if (fbdev->ctrl == NULL)
if (fbdev->ctrl == NULL) {
DBGPRINT(1, "ctrl %s not supported\n", name);
return -1;
}
return 0;
}
......@@ -1218,13 +1174,12 @@ static void check_required_callbacks(struct omapfb_device *fbdev)
* start LCD frame transfer
* 7. register system fb_info structure
*/
static int omapfb_probe(struct platform_device *pdev)
static int omapfb_do_probe(struct platform_device *pdev, struct lcd_panel *panel)
{
struct omapfb_device *fbdev = NULL;
struct fb_info *fbi;
int init_state;
unsigned long phz, hhz, vhz;
struct lcd_panel *panel;
int r = 0;
DBGENTER(1);
......@@ -1248,14 +1203,13 @@ static int omapfb_probe(struct platform_device *pdev)
fbdev = (struct omapfb_device *)fbi->par;
fbdev->fb_info = fbi;
fbdev->dev = &pdev->dev;
fbdev->panel = panel;
platform_set_drvdata(pdev, fbdev);
init_MUTEX(&fbdev->rqueue_sema);
#ifdef CONFIG_ARCH_OMAP1
#ifdef CONFIG_FB_OMAP_LCDC_INTERNAL
fbdev->int_ctrl = &omap1_int_ctrl;
#endif
#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
fbdev->ext_if = &sossi_extif;
#endif
......@@ -1271,15 +1225,6 @@ static int omapfb_probe(struct platform_device *pdev)
goto cleanup;
}
if (omapfb_find_panel(fbdev) < 0) {
pr_err("LCD panel not found, board not supported\n");
r = -ENODEV;
goto cleanup;
}
check_required_callbacks(fbdev);
pr_info(MODULE_NAME ": configured for panel %s\n", fbdev->panel->name);
r = fbdev->panel->init(fbdev);
......@@ -1292,6 +1237,14 @@ static int omapfb_probe(struct platform_device *pdev)
goto cleanup;
init_state++;
/* We depend on doing this after ctrl_init, since it can redefine
* member functions.
*/
if (fbdev->ctrl->mmap)
omapfb_ops.fb_mmap = omapfb_mmap;
check_required_callbacks(fbdev);
r = fbinfo_init(fbdev);
if (r)
goto cleanup;
......@@ -1308,7 +1261,8 @@ static int omapfb_probe(struct platform_device *pdev)
goto cleanup;
}
omapfb_enable_plane(fbdev, 0, 1);
if (!manual_update)
omapfb_enable_plane(fbdev, OMAPFB_PLANE_GFX, 1);
omapfb_set_update_mode(fbdev, manual_update ?
OMAPFB_MANUAL_UPDATE : OMAPFB_AUTO_UPDATE);
......@@ -1352,6 +1306,30 @@ cleanup:
return r;
}
static struct platform_device *fbdev_pdev;
static struct lcd_panel *fbdev_panel;
static int omapfb_probe(struct platform_device *pdev)
{
BUG_ON(fbdev_pdev != NULL);
DBGENTER(1);
fbdev_pdev = pdev;
if (fbdev_panel != NULL)
omapfb_do_probe(fbdev_pdev, fbdev_panel);
return 0;
}
void omapfb_register_panel(struct lcd_panel *panel)
{
BUG_ON(fbdev_panel != NULL);
DBGENTER(1);
fbdev_panel = panel;
if (fbdev_pdev != NULL)
omapfb_do_probe(fbdev_pdev, fbdev_panel);
}
/* Called when the device is being detached from the driver */
static int omapfb_remove(struct platform_device *pdev)
{
......
......@@ -35,6 +35,10 @@
#include "dispc.h"
/* #define OMAPFB_DBG 1 */
#include "debug.h"
#define MODULE_NAME "omapfb-rfbi"
#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args)
......@@ -68,8 +72,11 @@ static struct {
void (*lcdc_callback)(void *data);
void *lcdc_callback_data;
unsigned long l4_khz;
int bits_per_cycle;
} rfbi;
struct lcd_ctrl_extif rfbi_extif;
static inline void rfbi_write_reg(int idx, u32 val)
{
__raw_writel(val, rfbi.base + idx);
......@@ -80,25 +87,18 @@ static inline u32 rfbi_read_reg(int idx)
return __raw_readl(rfbi.base + idx);
}
static int ns_to_l4_ticks(int time)
{
unsigned long tick_ps;
int ret;
/* Calculate in picosecs to yield more exact results */
tick_ps = 1000000000 / (rfbi.l4_khz);
ret = (time * 1000 + tick_ps - 1) / tick_ps;
return ret * 2;
}
#ifdef OMAPFB_DBG
static void print_timings(void)
static void rfbi_print_timings(void)
{
u32 l;
u32 time;
DBGPRINT(1, "Tick time %lu ps\n", 1000000000 / rfbi.l4_khz);
l = rfbi_read_reg(RFBI_CONFIG0);
time = 1000000000 / rfbi.l4_khz;
if (l & (1 << 4))
time *= 2;
DBGPRINT(1, "Tick time %u ps\n", time);
l = rfbi_read_reg(RFBI_ONOFF_TIME0);
DBGPRINT(1, "CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, "
"REONTIME %d, REOFFTIME %d\n",
......@@ -109,55 +109,181 @@ static void print_timings(void)
"ACCESSTIME %d\n",
(l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, (l >> 22) & 0x3f);
}
#else
static void rfbi_print_timings(void) {}
#endif
static void rfbi_set_timings(const struct extif_timings *t)
{
u32 l;
int on, off;
on = ns_to_l4_ticks(t->cs_on_time) & 0x0f;
l = on;
off = ns_to_l4_ticks(t->cs_off_time) & 0x3f;
if (off <= on)
off = on + 2;
l |= off << 4;
on = ns_to_l4_ticks(t->we_on_time) & 0x0f;
l |= on << 10;
off = ns_to_l4_ticks(t->we_off_time) & 0x3f;
if (off <= on)
off = on + 2;
l |= off << 14;
l |= (ns_to_l4_ticks(t->re_on_time) & 0x0f) << 20;
l |= (ns_to_l4_ticks(t->re_off_time) & 0x3f) << 24;
rfbi_write_reg(RFBI_ONOFF_TIME0, l);
l = ns_to_l4_ticks(t->we_cycle_time) & 0x3f;
l |= (ns_to_l4_ticks(t->re_cycle_time) & 0x3f) << 6;
l |= (ns_to_l4_ticks(t->cs_pulse_width) & 0x3f) << 12;
l |= (ns_to_l4_ticks(t->access_time) & 0x3f) << 22;
rfbi_write_reg(RFBI_CYCLE_TIME0, l);
BUG_ON(!t->converted);
rfbi_write_reg(RFBI_ONOFF_TIME0, t->tim[0]);
rfbi_write_reg(RFBI_CYCLE_TIME0, t->tim[1]);
l = rfbi_read_reg(RFBI_CONFIG0);
l &= ~(1 << 4);
l |= (t->tim[2] ? 1 : 0) << 4;
rfbi_write_reg(RFBI_CONFIG0, l);
rfbi_print_timings();
}
static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div)
{
*clk_period = 1000000000 / rfbi.l4_khz;
*max_clk_div = 2;
}
static int ps_to_rfbi_ticks(int time, int div)
{
unsigned long tick_ps;
int ret;
/* Calculate in picosecs to yield more exact results */
tick_ps = 1000000000 / (rfbi.l4_khz) * div;
ret = (time + tick_ps - 1) / tick_ps;
return ret;
}
static void rfbi_write_command(u32 cmd)
static int rfbi_convert_timings(struct extif_timings *t)
{
rfbi_write_reg(RFBI_CMD, cmd);
u32 l;
int reon, reoff, weon, weoff, cson, csoff, cs_pulse;
int actim, recyc, wecyc;
int div = t->clk_div;
if (div <= 0 || div > 2)
return -1;
/* Make sure that after conversion it still holds that:
* weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff,
* csoff > cson, csoff >= max(weoff, reoff), actim > reon
*/
weon = ps_to_rfbi_ticks(t->we_on_time, div);
weoff = ps_to_rfbi_ticks(t->we_off_time, div);
if (weoff <= weon)
weoff = weon + 1;
if (weon > 0x0f)
return -1;
if (weoff > 0x3f)
return -1;
reon = ps_to_rfbi_ticks(t->re_on_time, div);
reoff = ps_to_rfbi_ticks(t->re_off_time, div);
if (reoff <= reon)
reoff = reon + 1;
if (reon > 0x0f)
return -1;
if (reoff > 0x3f)
return -1;
cson = ps_to_rfbi_ticks(t->cs_on_time, div);
csoff = ps_to_rfbi_ticks(t->cs_off_time, div);
if (csoff <= cson)
csoff = cson + 1;
if (csoff < max(weoff, reoff))
csoff = max(weoff, reoff);
if (cson > 0x0f)
return -1;
if (csoff > 0x3f)
return -1;
l = cson;
l |= csoff << 4;
l |= weon << 10;
l |= weoff << 14;
l |= reon << 20;
l |= reoff << 24;
t->tim[0] = l;
actim = ps_to_rfbi_ticks(t->access_time, div);
if (actim <= reon)
actim = reon + 1;
if (actim > 0x3f)
return -1;
wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div);
if (wecyc < weoff)
wecyc = weoff;
if (wecyc > 0x3f)
return -1;
recyc = ps_to_rfbi_ticks(t->re_cycle_time, div);
if (recyc < reoff)
recyc = reoff;
if (recyc > 0x3f)
return -1;
cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div);
if (cs_pulse > 0x3f)
return -1;
l = wecyc;
l |= recyc << 6;
l |= cs_pulse << 12;
l |= actim << 22;
t->tim[1] = l;
t->tim[2] = div - 1;
t->converted = 1;
return 0;
}
static u32 rfbi_read_data(void)
static void rfbi_write_command(const void *buf, unsigned int len)
{
u32 val;
if (rfbi.bits_per_cycle == 16) {
const u16 *w = buf;
BUG_ON(len & 1);
for (; len; len -= 2)
rfbi_write_reg(RFBI_CMD, *w++);
} else {
const u8 *b = buf;
BUG_ON(rfbi.bits_per_cycle != 8);
for (; len; len--)
rfbi_write_reg(RFBI_CMD, *b++);
}
}
rfbi_write_reg(RFBI_READ, 0);
val = rfbi_read_reg(RFBI_READ);
return val;
static void rfbi_read_data(void *buf, unsigned int len)
{
if (rfbi.bits_per_cycle == 16) {
u16 *w = buf;
BUG_ON(len & ~1);
for (; len; len -= 2) {
rfbi_write_reg(RFBI_READ, 0);
*w++ = rfbi_read_reg(RFBI_READ);
}
} else {
u8 *b = buf;
BUG_ON(rfbi.bits_per_cycle != 8);
for (; len; len--) {
rfbi_write_reg(RFBI_READ, 0);
*b++ = rfbi_read_reg(RFBI_READ);
}
}
}
static void rfbi_write_data(u32 val)
static void rfbi_write_data(const void *buf, unsigned int len)
{
rfbi_write_reg(RFBI_PARAM, val);
if (rfbi.bits_per_cycle == 16) {
const u16 *w = buf;
BUG_ON(len & 1);
for (; len; len -= 2)
rfbi_write_reg(RFBI_PARAM, *w++);
} else {
const u8 *b = buf;
BUG_ON(rfbi.bits_per_cycle != 8);
for (; len; len--)
rfbi_write_reg(RFBI_PARAM, *b++);
}
}
static void rfbi_transfer_area(int width, int height,
......@@ -195,13 +321,32 @@ static void rfbi_dma_callback(void *data)
rfbi.lcdc_callback(rfbi.lcdc_callback_data);
}
static void rfbi_set_bits_per_cycle(int bpc)
{
u32 l;
l = rfbi_read_reg(RFBI_CONFIG0);
l &= ~(0x03 << 0);
switch (bpc)
{
case 8:
break;
case 16:
l |= 3;
break;
default:
BUG();
}
rfbi_write_reg(RFBI_CONFIG0, l);
rfbi.bits_per_cycle = bpc;
}
static int rfbi_init(void)
{
u32 l;
int r;
struct clk *dss_ick;
memset(&rfbi, 0, sizeof(rfbi));
rfbi.base = io_p2v(RFBI_BASE);
l = rfbi_read_reg(RFBI_REVISION);
......@@ -212,6 +357,7 @@ static int rfbi_init(void)
pr_err("can't get dss_ick\n");
return PTR_ERR(dss_ick);
}
rfbi.l4_khz = clk_get_rate(dss_ick) / 1000;
clk_put(dss_ick);
......@@ -229,13 +375,10 @@ static int rfbi_init(void)
l |= (0 << 9) | (1 << 20) | (1 << 21);
rfbi_write_reg(RFBI_CONFIG0, l);
l = 0x10;
rfbi_write_reg(RFBI_DATA_CYCLE1_0, l);
rfbi_write_reg(RFBI_DATA_CYCLE2_0, l);
rfbi_write_reg(RFBI_DATA_CYCLE3_0, l);
rfbi_write_reg(RFBI_DATA_CYCLE1_0, 0x00000010);
l = rfbi_read_reg(RFBI_CONTROL);
/* Select CS0 */
/* Select CS0, clear bypass mode */
l = (0x01 << 2);
rfbi_write_reg(RFBI_CONTROL, l);
......@@ -255,10 +398,14 @@ static void rfbi_cleanup(void)
struct lcd_ctrl_extif rfbi_extif = {
.init = rfbi_init,
.cleanup = rfbi_cleanup,
.get_clk_info = rfbi_get_clk_info,
.set_bits_per_cycle = rfbi_set_bits_per_cycle,
.convert_timings = rfbi_convert_timings,
.set_timings = rfbi_set_timings,
.write_command = rfbi_write_command,
.read_data = rfbi_read_data,
.write_data = rfbi_write_data,
.transfer_area = rfbi_transfer_area,
.max_transmit_size = (u32)~0,
};
......@@ -28,7 +28,14 @@
#include <asm/io.h>
#include "sossi.h"
#include <asm/arch/dma.h>
#include <asm/arch/omapfb.h>
#include "lcdc.h"
/* #define OMAPFB_DBG 1 */
#include "debug.h"
#define MODULE_NAME "omapfb-sossi"
......@@ -48,38 +55,59 @@
#define DMA_LCD_CTRL 0xfffee3c4
#define DMA_LCD_LCH_CTRL 0xfffee3ea
#define RD_ACCESS 0
#define WR_ACCESS 1
#define SOSSI_MAX_XMIT_BYTES (512 * 1024)
#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args)
static int sossi_base = IO_ADDRESS(OMAP_SOSSI_BASE);
static struct sossi {
int base;
unsigned long dpll_khz;
int bus_pick_width;
void (*lcdc_callback)(void *data);
void *lcdc_callback_data;
/* timing for read and write access */
int clk_div;
u8 clk_tw0[2];
u8 clk_tw1[2];
/* if last_access is the same as current we don't have to change
* the timings
*/
int last_access;
} sossi;
struct lcd_ctrl_extif sossi_extif;
static inline u32 sossi_read_reg(int reg)
{
return readl(sossi_base + reg);
return readl(sossi.base + reg);
}
static inline u16 sossi_read_reg16(int reg)
{
return readw(sossi_base + reg);
return readw(sossi.base + reg);
}
static inline u8 sossi_read_reg8(int reg)
{
return readb(sossi_base + reg);
return readb(sossi.base + reg);
}
static inline void sossi_write_reg(int reg, u32 value)
{
writel(value, sossi_base + reg);
writel(value, sossi.base + reg);
}
static inline void sossi_write_reg16(int reg, u16 value)
{
writew(value, sossi_base + reg);
writew(value, sossi.base + reg);
}
static inline void sossi_write_reg8(int reg, u8 value)
{
writeb(value, sossi_base + reg);
writeb(value, sossi.base + reg);
}
static void sossi_set_bits(int reg, u32 bits)
......@@ -96,14 +124,26 @@ static void sossi_clear_bits(int reg, u32 bits)
#define CONF_SOSSI_RESET_R (1 << 23)
#define CONF_MOD_SOSSI_CLK_EN_R (1 << 16)
static struct clk *dpll_clk;
static void sossi_dma_callback(void *data);
int sossi_init(void)
static int sossi_init(void)
{
u32 l, k;
struct clk *dpll_clk;
int r;
sossi.base = IO_ADDRESS(OMAP_SOSSI_BASE);
dpll_clk = clk_get(NULL, "ck_dpll1");
BUG_ON(dpll_clk == NULL);
if (IS_ERR(dpll_clk)) {
pr_err("can't get dpll1 clock\n");
return PTR_ERR(dpll_clk);
}
sossi.dpll_khz = clk_get_rate(dpll_clk) / 1000;
clk_put(dpll_clk);
sossi_extif.max_transmit_size = SOSSI_MAX_XMIT_BYTES;
/* Reset and enable the SoSSI module */
l = omap_readl(MOD_CONF_CTRL_1);
......@@ -134,6 +174,12 @@ int sossi_init(void)
pr_err("Invalid SoSSI sync pattern: %08x, %08x\n", l, k);
return -ENODEV;
}
if ((r = omap_lcdc_set_dma_callback(sossi_dma_callback, NULL)) < 0) {
pr_err("can't get LCDC IRQ\n");
return r;
}
l = sossi_read_reg(SOSSI_ID_REG); /* Component code */
l = sossi_read_reg(SOSSI_ID_REG);
pr_info(KERN_INFO MODULE_NAME ": version %d.%d initialized\n",
......@@ -147,89 +193,201 @@ int sossi_init(void)
return 0;
}
static unsigned long get_sossi_clk_rate(int div)
static void sossi_cleanup(void)
{
return (clk_get_rate(dpll_clk)) / div;
omap_lcdc_free_dma_callback();
}
static unsigned long get_sossi_clk_period(int div)
#define KHZ_TO_PS(x) (1000000000 / (x))
static void sossi_get_clk_info(u32 *clk_period, u32 *max_clk_div)
{
/* In picoseconds */
return 1000000000 / (get_sossi_clk_rate(div) / 1000);
*clk_period = KHZ_TO_PS(sossi.dpll_khz);
*max_clk_div = 8;
}
static int ns_to_sossi_ticks(int time, int div)
static u32 ps_to_sossi_ticks(u32 ps, int div)
{
unsigned long tick_ps;
u32 clk_period = KHZ_TO_PS(sossi.dpll_khz) * div;
return (clk_period + ps - 1) / clk_period;
}
static int calc_rd_timings(struct extif_timings *t)
{
u32 tw0, tw1;
int reon, reoff, recyc, actim;
int div = t->clk_div;
/* Make sure that after conversion it still holds that:
* reoff > reon, recyc >= reoff, actim > reon
*/
reon = ps_to_sossi_ticks(t->re_on_time, div);
/* reon will be exactly one sossi tick */
if (reon > 1)
return -1;
reoff = ps_to_sossi_ticks(t->re_off_time, div);
if (reoff <= reon)
reoff = reon + 1;
/* Calculate in picosecs to yield more exact results */
tick_ps = get_sossi_clk_period(div);
tw0 = reoff - reon;
if (tw0 > 0x10)
return -1;
recyc = ps_to_sossi_ticks(t->re_cycle_time, div);
if (recyc <= reoff)
recyc = reoff + 1;
tw1 = recyc - reoff;
if (tw1 > 0x40)
return -1;
actim = ps_to_sossi_ticks(t->access_time, div);
if (actim < reoff)
actim++;
/* access time (data hold time) will be exactly one sossi
* tick
*/
if (actim - reoff > 1)
return -1;
return (time * 1000 + tick_ps - 1) / tick_ps;
t->tim[0] = tw0 - 1;
t->tim[1] = tw1 - 1;
return 0;
}
static int set_timings(int div, int tw0, int tw1)
static int calc_wr_timings(struct extif_timings *t)
{
u32 l;
u32 tw0, tw1;
int weon, weoff, wecyc;
int div = t->clk_div;
/* Make sure that after conversion it still holds that:
* weoff > weon, wecyc >= weoff
*/
weon = ps_to_sossi_ticks(t->we_on_time, div);
/* weon will be exactly one sossi tick */
if (weon > 1)
return -1;
weoff = ps_to_sossi_ticks(t->we_off_time, div);
if (weoff <= weon)
weoff = weon + 1;
tw0 = weoff - weon;
if (tw0 > 0x10)
return -1;
wecyc = ps_to_sossi_ticks(t->we_cycle_time, div);
if (wecyc <= weoff)
wecyc = weoff + 1;
if (tw1 * 1000 > 64 * get_sossi_clk_period(div))
tw1 = wecyc - weoff;
if (tw1 > 0x40)
return -1;
if (tw0 * 1000 > 16 * get_sossi_clk_period(div))
t->tim[2] = tw0 - 1;
t->tim[3] = tw1 - 1;
return 0;
}
static int sossi_convert_timings(struct extif_timings *t)
{
int r = 0;
int div = t->clk_div;
t->converted = 0;
if (div <= 0 || div > 8)
return -1;
/* no CS on SOSSI, so ignore cson, csoff, cs_pulsewidth */
if ((r = calc_rd_timings(t)) < 0)
return r;
if ((r = calc_wr_timings(t)) < 0)
return r;
t->tim[4] = div - 1;
t->converted = 1;
return 0;
}
static void sossi_set_timings(const struct extif_timings *t)
{
BUG_ON(!t->converted);
sossi.clk_tw0[RD_ACCESS] = t->tim[0];
sossi.clk_tw1[RD_ACCESS] = t->tim[1];
sossi.clk_tw0[WR_ACCESS] = t->tim[2];
sossi.clk_tw1[WR_ACCESS] = t->tim[3];
sossi.clk_div = t->tim[4];
}
static void _set_timing(int div, int tw0, int tw1)
{
u32 l;
DBGPRINT(2, "Using TW0 = %d, TW1 = %d, div = %d\n",
tw0 + 1, tw1 + 1, div + 1);
l = omap_readl(MOD_CONF_CTRL_1);
l &= ~(7 << 17);
l |= (div - 1) << 17;
l |= div << 17;
omap_writel(l, MOD_CONF_CTRL_1);
tw0 = ns_to_sossi_ticks(tw0, div) - 1;
tw1 = ns_to_sossi_ticks(tw1, div) - 1;
if (tw0 < 0)
tw0 = 0;
if (tw1 < 0)
tw1 = 0;
#if 0
printk("Using TW0 = %d, TW1 = %d, div = %d, period = %d ps\n",
tw0, tw1, div, get_sossi_clk_period(div));
#endif
l = sossi_read_reg(SOSSI_INIT1_REG);
l &= ~((0x0f << 20) | (0x3f << 24));
l |= ((tw0 & 0x0f) << 20) | ((tw1 & 0x3f) << 24);
l |= (tw0 << 20) | (tw1 << 24);
sossi_write_reg(SOSSI_INIT1_REG, l);
return 0;
}
static struct sossi {
int bus_pick_width;
} sossi;
void sossi_set_timings(int min_time, int min_tw0, int min_tw1)
static inline void set_timing(int access)
{
int div;
for (div = 1; div <= 8; div++) {
if (min_time * 1000 > get_sossi_clk_period(div))
continue;
if (set_timings(div, min_tw0, min_tw1) == 0)
break;
}
if (div == 9) {
pr_err("DPLL frequency too high for SoSSI\n");
BUG();
if (access != sossi.last_access) {
sossi.last_access = access;
_set_timing(sossi.clk_div,
sossi.clk_tw0[access], sossi.clk_tw1[access]);
}
}
void sossi_set_xfer_params(int bus_pick_count, int bus_pick_width)
static void sossi_set_bits_per_cycle(int bpc)
{
u32 l;
int bus_pick_count, bus_pick_width;
DBGPRINT(2, "bits_per_cycle %d\n", bpc);
/* We set explicitly the the bus_pick_count as well, although
* with remapping/reordering disabled it will be calculated by HW
* as (32 / bus_pick_width).
*/
switch (bpc) {
case 8:
bus_pick_count = 4;
bus_pick_width = 8;
break;
case 16:
bus_pick_count = 2;
bus_pick_width = 16;
break;
default:
BUG();
return;
}
l = sossi_read_reg(SOSSI_INIT3_REG);
sossi.bus_pick_width = bus_pick_width;
l = ((bus_pick_count - 1) << 5) | ((bus_pick_width - 1) & 0x1f);
l &= ~0x3ff;
l |= ((bus_pick_count - 1) << 5) | ((bus_pick_width - 1) & 0x1f);
sossi_write_reg(SOSSI_INIT3_REG, l);
}
void sossi_start_transfer(void)
static void sossi_start_transfer(void)
{
/* WE */
sossi_clear_bits(SOSSI_INIT2_REG, 1 << 4);
......@@ -238,7 +396,7 @@ void sossi_start_transfer(void)
/* FIXME: locking? */
}
void sossi_stop_transfer(void)
static void sossi_stop_transfer(void)
{
/* WE */
sossi_set_bits(SOSSI_INIT2_REG, 1 << 4);
......@@ -247,6 +405,12 @@ void sossi_stop_transfer(void)
/* FIXME: locking? */
}
static void wait_end_of_write(void)
{
/* Before reading we must check if some writings are going on */
while (!(sossi_read_reg(SOSSI_INIT2_REG) & (1 << 3)));
}
static void send_data(const void *data, unsigned int len)
{
while (len >= 4) {
......@@ -268,65 +432,71 @@ static void send_data(const void *data, unsigned int len)
static void set_cycles(unsigned int len)
{
int nr_cycles = len / (sossi.bus_pick_width / 8);
unsigned long nr_cycles = len / (sossi.bus_pick_width / 8);
BUG_ON((nr_cycles - 1) & ~0x3ffff);
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)
static void sossi_write_command(const void *data, unsigned int len)
{
set_timing(WR_ACCESS);
/* CMD#/DATA */
sossi_clear_bits(SOSSI_INIT1_REG, 1 << 18);
set_cycles(len);
sossi_start_transfer();
send_data(data, len);
sossi_stop_transfer();
wait_end_of_write();
}
void sossi_send_data(const void *data, unsigned int len)
static void sossi_write_data(const void *data, unsigned int len)
{
set_timing(WR_ACCESS);
/* CMD#/DATA */
sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
set_cycles(len);
sossi_start_transfer();
send_data(data, len);
sossi_stop_transfer();
wait_end_of_write();
}
void sossi_prepare_dma_transfer(unsigned int count)
static void sossi_transfer_area(int width, int height,
void (callback)(void *data), void *data)
{
sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
set_cycles(count);
}
BUG_ON(callback == NULL);
void sossi_send_data_const32(u32 data, unsigned int count)
{
sossi.lcdc_callback = callback;
sossi.lcdc_callback_data = data;
set_timing(WR_ACCESS);
/* CMD#/DATA */
sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
set_cycles(count * 4);
while (count > 0) {
sossi_write_reg(SOSSI_FIFO_REG, data);
count--;
}
set_cycles(width * height * sossi.bus_pick_width / 8);
DBGPRINT(2, "SOSSI_INIT1_REG %08x\n", sossi_read_reg(SOSSI_INIT1_REG));
sossi_start_transfer();
omap_enable_lcd_dma();
}
void sossi_set_tearing(int mode, int hs_counter, int detect_limit,
int vs_counter, int vs_detect_limit, int flags)
static void sossi_dma_callback(void *data)
{
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);
omap_stop_lcd_dma();
sossi_stop_transfer();
sossi.lcdc_callback(sossi.lcdc_callback_data);
}
void sossi_read_data(void *data, unsigned int len)
static 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)));
set_timing(RD_ACCESS);
/* CMD#/DATA */
sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
set_cycles(len);
sossi_start_transfer();
while (len >= 4) {
*(u32 *) data = sossi_read_reg(SOSSI_FIFO_REG);
len -= 4;
......@@ -342,4 +512,18 @@ void sossi_read_data(void *data, unsigned int len)
len--;
data++;
}
sossi_stop_transfer();
}
struct lcd_ctrl_extif sossi_extif = {
.init = sossi_init,
.cleanup = sossi_cleanup,
.get_clk_info = sossi_get_clk_info,
.convert_timings = sossi_convert_timings,
.set_timings = sossi_set_timings,
.set_bits_per_cycle = sossi_set_bits_per_cycle,
.write_command = sossi_write_command,
.read_data = sossi_read_data,
.write_data = sossi_write_data,
.transfer_area = sossi_transfer_area,
};
#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 bus_pick_count, int bus_pick_width);
extern void sossi_set_timings(int tick_ns, int tw0_ns, int tw1_ns);
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
......@@ -21,6 +21,7 @@
#define OMAP_TAG_LCD 0x4f05
#define OMAP_TAG_GPIO_SWITCH 0x4f06
#define OMAP_TAG_UART 0x4f07
#define OMAP_TAG_FBMEM 0x4f08
#define OMAP_TAG_STI_CONSOLE 0x4f09
#define OMAP_TAG_BOOT_REASON 0x4f80
......@@ -94,6 +95,13 @@ struct omap_lcd_config {
char ctrl_name[16];
};
struct omap_fbmem_config {
u32 fb_sram_start;
u32 fb_sram_size;
u32 fb_sdram_start;
u32 fb_sdram_size;
};
/* Cover:
* high -> closed
* low -> open
......
#ifndef __LCD_LPH8923_H
#define __LCD_LPH8923_H
enum lcd_lph8923_test_num {
LCD_LPH8923_TEST_RGB_LINES,
};
enum lcd_lph8923_test_result {
LCD_LPH8923_TEST_SUCCESS,
LCD_LPH8923_TEST_INVALID,
LCD_LPH8923_TEST_FAILED,
};
#endif
......@@ -35,6 +35,7 @@
#define OMAPFB_SYNC_GFX OMAP_IO(37)
#define OMAPFB_VSYNC OMAP_IO(38)
#define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, enum omapfb_update_mode)
#define OMAPFB_UPDATE_WINDOW_OLD OMAP_IOW(41, struct omapfb_update_window_old)
#define OMAPFB_GET_CAPS OMAP_IOR(42, unsigned long)
#define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, enum omapfb_update_mode)
#define OMAPFB_LCD_TEST OMAP_IOW(45, int)
......@@ -71,6 +72,11 @@ struct omapfb_update_window {
u32 format;
};
struct omapfb_update_window_old {
u32 x, y;
u32 width, height;
};
enum omapfb_plane {
OMAPFB_PLANE_GFX = 0,
OMAPFB_PLANE_VID1,
......@@ -121,6 +127,8 @@ enum omapfb_update_mode {
#include <linux/interrupt.h>
#include <linux/fb.h>
#include <asm/arch/board.h>
#define OMAP_LCDC_INV_VSYNC 0x0001
#define OMAP_LCDC_INV_HSYNC 0x0002
#define OMAP_LCDC_INV_PIX_CLOCK 0x0004
......@@ -184,17 +192,27 @@ struct extif_timings {
int re_cycle_time;
int cs_pulse_width;
int access_time;
int clk_div;
u32 tim[5]; /* set by extif->convert_timings */
int converted;
};
struct lcd_ctrl_extif {
int (*init) (void);
void (*cleanup) (void);
void (*get_clk_info) (u32 *clk_period, u32 *max_clk_div);
int (*convert_timings) (struct extif_timings *timings);
void (*set_timings) (const struct extif_timings *timings);
void (*write_command) (u32 cmd);
u32 (*read_data) (void);
void (*write_data) (u32 data);
void (*set_bits_per_cycle)(int bpc);
void (*write_command) (const void *buf, unsigned int len);
void (*read_data) (void *buf, unsigned int len);
void (*write_data) (const void *buf, unsigned int len);
void (*transfer_area) (int width, int height,
void (callback)(void * data), void *data);
unsigned long max_transmit_size;
};
struct lcd_ctrl {
......@@ -207,6 +225,7 @@ struct lcd_ctrl {
void (*get_vram_layout)(unsigned long *size,
void **virt_base,
dma_addr_t *phys_base);
int (*mmap) (struct vm_area_struct *vma);
unsigned long (*get_caps) (void);
int (*set_update_mode)(enum omapfb_update_mode mode);
enum omapfb_update_mode (*get_update_mode)(void);
......@@ -261,12 +280,10 @@ struct omapfb_device {
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;
struct omapfb_platform_data {
struct omap_lcd_config lcd;
struct omap_fbmem_config fbmem;
};
#ifdef CONFIG_ARCH_OMAP1
extern struct lcd_ctrl omap1_lcd_ctrl;
......@@ -274,8 +291,12 @@ extern struct lcd_ctrl omap1_lcd_ctrl;
extern struct lcd_ctrl omap2_disp_ctrl;
#endif
extern void omapfb_register_panel(struct lcd_panel *panel);
extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval);
/* in arch/arm/plat-omap/devices.c */
extern void omapfb_reserve_mem(void);
#endif /* __KERNEL__ */
#endif /* __OMAPFB_H */
......@@ -20,6 +20,8 @@ extern void omap2_sram_reprogram_sdrc(u32 perf_level, u32 dll_val,
u32 mem_type);
extern u32 omap2_set_prcm(u32 dpll_ctrl_val, u32 sdrc_rfr_val, int bypass);
extern unsigned long omap_fb_sram_start;
extern unsigned long omap_fb_sram_size;
/* Do not use these */
extern void sram_reprogram_clock(u32 ckctl, u32 dpllctl);
......
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