#
# 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
# 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
#
.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)
};
static struct omap_lcd_config h2_lcd_config __initdata = {
- .panel_name = "h2",
.ctrl_name = "internal",
};
.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,
&intlat_device,
&h3_irda_device,
&h3_kp_device,
+ &h3_lcd_device,
};
static struct omap_usb_config h3_usb_config __initdata = {
};
static struct omap_lcd_config h3_lcd_config __initdata = {
- .panel_name = "h3",
.ctrl_name = "internal",
};
.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 */
.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 */
};
static struct omap_lcd_config innovator1510_lcd_config __initdata = {
- .panel_name = "inn1510",
.ctrl_name = "internal",
};
#endif
};
static struct omap_lcd_config innovator1610_lcd_config __initdata = {
- .panel_name = "inn1610",
.ctrl_name = "internal",
};
#endif
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 = {
};
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();
.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)
};
static struct omap_lcd_config osk_lcd_config __initdata = {
- .panel_name = "osk",
.ctrl_name = "internal",
};
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,
};
static struct omap_lcd_config palmte_lcd_config __initdata = {
- .panel_name = "palmte",
.ctrl_name = "internal",
};
{
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)
.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
};
static struct omap_lcd_config perseus2_lcd_config __initdata = {
- .panel_name = "p2",
.ctrl_name = "internal",
};
#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);
#endif
omap_sram_init();
+ omapfb_reserve_mem();
}
/*
.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)
};
static struct omap_lcd_config apollon_lcd_config __initdata = {
- .panel_name = "apollon",
.ctrl_name = "internal",
};
},
};
+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)
};
static struct omap_lcd_config h4_lcd_config __initdata = {
- .panel_name = "h4",
.ctrl_name = "internal",
};
#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);
omap2_check_revision();
omap_sram_init();
+ omapfb_reserve_mem();
}
void __init omap2_init_common_hw(void)
#
# 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- :=
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):
/* 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();
--- /dev/null
+#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
+
+
#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
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
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
*/
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())
}
} else {
omap_sram_base = OMAP1_SRAM_VA;
+ sram_start = OMAP1_SRAM_PA;
if (cpu_is_omap730())
omap_sram_size = 0x32000; /* 200K */
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;
}
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
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
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
answer yes. Answer no if you have a dedicated video
memory, or don't use any of the accelerated features.
+
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
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)
#ifndef __OMAPFB_DEBUG_H
#define __OMAPFB_DEBUG_H
-#include <asm/io.h>
-
#ifdef OMAPFB_DBG
#define DBGPRINT(level, fmt, ...) if (level <= OMAPFB_DBG) do { \
#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 */
#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"
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)
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 *);
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;
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)
}
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)
{
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:
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);
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;
}
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;
}
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;
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
}
}
+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;
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;
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);
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;
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;
}
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,
u32 l;
struct lcd_panel *panel = fbdev->panel;
int tmo = 10000;
+ int skip_init = 0;
DBGENTER(1);
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);
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)
.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,
--- /dev/null
+/*
+ * 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(®, 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(®, 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 = ∁
+
+ 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,
+};
+
--- /dev/null
+#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
*/
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <asm/arch/mux.h>
#include <asm/arch/omapfb.h>
.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);
+
*/
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <asm/arch/gpio.h>
#include <asm/arch/tps65010.h>
.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);
+
*/
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <asm/arch/omapfb.h>
.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);
+
*/
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <asm/io.h>
.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);
+
.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);
+
--- /dev/null
+#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,
+};
*/
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <asm/arch/gpio.h>
#include <asm/arch/mux.h>
.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);
+
#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/platform_device.h>
#include <asm/arch/mux.h>
#include <asm/arch/gpio.h>
.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);
+
*/
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <asm/io.h>
.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);
+
#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>
#include <asm/mach-types.h>
-/* #define OMAPFB_DBG 2 */
+/* #define OMAPFB_DBG 1 */
#include "debug.h"
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;
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;
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:
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);
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;
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);
}
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;
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
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();
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;
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);
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
}
/* 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;
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) {
}
}
+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,
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");
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:
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,
--- /dev/null
+#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
* 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
};
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,
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);
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:
}
omapfb_rqueue_unlock(fbdev);
+ if (do_update)
+ omapfb_update_full_screen(fbdev);
+
DBGLEAVE(1);
return r;
}
} p;
int r = 0;
- DBGENTER(2);
-
BUG_ON(!ops);
DBGPRINT(2, "cmd=%010x\n", cmd);
switch (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)))
}
}
-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) {
}
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;
}
* 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);
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
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);
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;
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);
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)
{
#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)
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);
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",
"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,
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);
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);
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);
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,
};
#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"
#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)
#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);
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",
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);
/* FIXME: locking? */
}
-void sossi_stop_transfer(void)
+static void sossi_stop_transfer(void)
{
/* WE */
sossi_set_bits(SOSSI_INIT2_REG, 1 << 4);
/* 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) {
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;
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,
+};
+++ /dev/null
-#ifndef DRIVERS_VIDEO_OMAP_SOSSI_H
-#define DRIVERS_VIDEO_OMAP_SOSSI_H
-
-#define SOSSI_FLAG_HS_INVERTED 0x01
-#define SOSSI_FLAG_VS_INVERTED 0x02
-
-extern int sossi_init(void);
-extern void sossi_set_xfer_params(int 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
#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
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
--- /dev/null
+#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
#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)
u32 format;
};
+struct omapfb_update_window_old {
+ u32 x, y;
+ u32 width, height;
+};
+
enum omapfb_plane {
OMAPFB_PLANE_GFX = 0,
OMAPFB_PLANE_VID1,
#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
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 {
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);
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;
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 */
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);