[PATCH 06/15] OMAP: DSS2: Display Subsystem Driver core
From: Tomi Valkeinen <hidden>
Date: 2009-08-05 14:16:57
Also in:
linux-omap
Subsystem:
arm port, framebuffer layer, the rest · Maintainers:
Russell King, Helge Deller, Linus Torvalds
Signed-off-by: Tomi Valkeinen <redacted> --- arch/arm/plat-omap/include/mach/display.h | 542 +++++ drivers/video/Kconfig | 1 + drivers/video/Makefile | 1 + drivers/video/omap2/Kconfig | 2 + drivers/video/omap2/Makefile | 2 + drivers/video/omap2/displays/Kconfig | 4 + drivers/video/omap2/dss/Kconfig | 89 + drivers/video/omap2/dss/Makefile | 6 + drivers/video/omap2/dss/core.c | 917 +++++++++ drivers/video/omap2/dss/dispc.c | 3177 +++++++++++++++++++++++++++++ drivers/video/omap2/dss/display.c | 658 ++++++ drivers/video/omap2/dss/dpi.c | 388 ++++ drivers/video/omap2/dss/dss.c | 347 ++++ drivers/video/omap2/dss/dss.h | 356 ++++ drivers/video/omap2/dss/manager.c | 1487 ++++++++++++++ drivers/video/omap2/dss/overlay.c | 673 ++++++ 16 files changed, 8650 insertions(+), 0 deletions(-) create mode 100644 arch/arm/plat-omap/include/mach/display.h create mode 100644 drivers/video/omap2/Kconfig create mode 100644 drivers/video/omap2/Makefile create mode 100644 drivers/video/omap2/displays/Kconfig create mode 100644 drivers/video/omap2/displays/Makefile create mode 100644 drivers/video/omap2/dss/Kconfig create mode 100644 drivers/video/omap2/dss/Makefile create mode 100644 drivers/video/omap2/dss/core.c create mode 100644 drivers/video/omap2/dss/dispc.c create mode 100644 drivers/video/omap2/dss/display.c create mode 100644 drivers/video/omap2/dss/dpi.c create mode 100644 drivers/video/omap2/dss/dss.c create mode 100644 drivers/video/omap2/dss/dss.h create mode 100644 drivers/video/omap2/dss/manager.c create mode 100644 drivers/video/omap2/dss/overlay.c
diff --git a/arch/arm/plat-omap/include/mach/display.h b/arch/arm/plat-omap/include/mach/display.h
new file mode 100644
index 0000000..a84425a
--- /dev/null
+++ b/arch/arm/plat-omap/include/mach/display.h@@ -0,0 +1,542 @@ +/* + * linux/include/asm-arm/arch-omap/display.h + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __ASM_ARCH_OMAP_DISPLAY_H +#define __ASM_ARCH_OMAP_DISPLAY_H + +#include <linux/list.h> +#include <linux/kobject.h> +#include <linux/device.h> +#include <asm/atomic.h> + +#define DISPC_IRQ_FRAMEDONE (1 << 0) +#define DISPC_IRQ_VSYNC (1 << 1) +#define DISPC_IRQ_EVSYNC_EVEN (1 << 2) +#define DISPC_IRQ_EVSYNC_ODD (1 << 3) +#define DISPC_IRQ_ACBIAS_COUNT_STAT (1 << 4) +#define DISPC_IRQ_PROG_LINE_NUM (1 << 5) +#define DISPC_IRQ_GFX_FIFO_UNDERFLOW (1 << 6) +#define DISPC_IRQ_GFX_END_WIN (1 << 7) +#define DISPC_IRQ_PAL_GAMMA_MASK (1 << 8) +#define DISPC_IRQ_OCP_ERR (1 << 9) +#define DISPC_IRQ_VID1_FIFO_UNDERFLOW (1 << 10) +#define DISPC_IRQ_VID1_END_WIN (1 << 11) +#define DISPC_IRQ_VID2_FIFO_UNDERFLOW (1 << 12) +#define DISPC_IRQ_VID2_END_WIN (1 << 13) +#define DISPC_IRQ_SYNC_LOST (1 << 14) +#define DISPC_IRQ_SYNC_LOST_DIGIT (1 << 15) +#define DISPC_IRQ_WAKEUP (1 << 16) + +struct omap_dss_device; +struct omap_overlay_manager; + +enum omap_display_type { + OMAP_DISPLAY_TYPE_NONE = 0, + OMAP_DISPLAY_TYPE_DPI = 1 << 0, + OMAP_DISPLAY_TYPE_DBI = 1 << 1, + OMAP_DISPLAY_TYPE_SDI = 1 << 2, + OMAP_DISPLAY_TYPE_DSI = 1 << 3, + OMAP_DISPLAY_TYPE_VENC = 1 << 4, +}; + +enum omap_plane { + OMAP_DSS_GFX = 0, + OMAP_DSS_VIDEO1 = 1, + OMAP_DSS_VIDEO2 = 2 +}; + +enum omap_channel { + OMAP_DSS_CHANNEL_LCD = 0, + OMAP_DSS_CHANNEL_DIGIT = 1, +}; + +enum omap_color_mode { + OMAP_DSS_COLOR_CLUT1 = 1 << 0, /* BITMAP 1 */ + OMAP_DSS_COLOR_CLUT2 = 1 << 1, /* BITMAP 2 */ + OMAP_DSS_COLOR_CLUT4 = 1 << 2, /* BITMAP 4 */ + OMAP_DSS_COLOR_CLUT8 = 1 << 3, /* BITMAP 8 */ + OMAP_DSS_COLOR_RGB12U = 1 << 4, /* RGB12, 16-bit container */ + OMAP_DSS_COLOR_ARGB16 = 1 << 5, /* ARGB16 */ + OMAP_DSS_COLOR_RGB16 = 1 << 6, /* RGB16 */ + OMAP_DSS_COLOR_RGB24U = 1 << 7, /* RGB24, 32-bit container */ + OMAP_DSS_COLOR_RGB24P = 1 << 8, /* RGB24, 24-bit container */ + OMAP_DSS_COLOR_YUV2 = 1 << 9, /* YUV2 4:2:2 co-sited */ + OMAP_DSS_COLOR_UYVY = 1 << 10, /* UYVY 4:2:2 co-sited */ + OMAP_DSS_COLOR_ARGB32 = 1 << 11, /* ARGB32 */ + OMAP_DSS_COLOR_RGBA32 = 1 << 12, /* RGBA32 */ + OMAP_DSS_COLOR_RGBX32 = 1 << 13, /* RGBx32 */ + + OMAP_DSS_COLOR_GFX_OMAP3 = + OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 | + OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 | + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32, + + OMAP_DSS_COLOR_VID_OMAP3 = + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32 | + OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY, +}; + +enum omap_lcd_display_type { + OMAP_DSS_LCD_DISPLAY_STN, + OMAP_DSS_LCD_DISPLAY_TFT, +}; + +enum omap_dss_load_mode { + OMAP_DSS_LOAD_CLUT_AND_FRAME = 0, + OMAP_DSS_LOAD_CLUT_ONLY = 1, + OMAP_DSS_LOAD_FRAME_ONLY = 2, + OMAP_DSS_LOAD_CLUT_ONCE_FRAME = 3, +}; + +enum omap_dss_trans_key_type { + OMAP_DSS_COLOR_KEY_GFX_DST = 0, + OMAP_DSS_COLOR_KEY_VID_SRC = 1, +}; + +enum omap_rfbi_te_mode { + OMAP_DSS_RFBI_TE_MODE_1 = 1, + OMAP_DSS_RFBI_TE_MODE_2 = 2, +}; + +enum omap_panel_config { + OMAP_DSS_LCD_IVS = 1<<0, + OMAP_DSS_LCD_IHS = 1<<1, + OMAP_DSS_LCD_IPC = 1<<2, + OMAP_DSS_LCD_IEO = 1<<3, + OMAP_DSS_LCD_RF = 1<<4, + OMAP_DSS_LCD_ONOFF = 1<<5, + + OMAP_DSS_LCD_TFT = 1<<20, +}; + +enum omap_dss_venc_type { + OMAP_DSS_VENC_TYPE_COMPOSITE, + OMAP_DSS_VENC_TYPE_SVIDEO, +}; + +enum omap_display_caps { + OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE = 1 << 0, +}; + +enum omap_dss_update_mode { + OMAP_DSS_UPDATE_DISABLED = 0, + OMAP_DSS_UPDATE_AUTO, + OMAP_DSS_UPDATE_MANUAL, +}; + +enum omap_dss_display_state { + OMAP_DSS_DISPLAY_DISABLED = 0, + OMAP_DSS_DISPLAY_ACTIVE, + OMAP_DSS_DISPLAY_SUSPENDED, +}; + +/* XXX perhaps this should be removed */ +enum omap_dss_overlay_managers { + OMAP_DSS_OVL_MGR_LCD, + OMAP_DSS_OVL_MGR_TV, +}; + +enum omap_dss_rotation_type { + OMAP_DSS_ROT_DMA = 0, + OMAP_DSS_ROT_VRFB = 1, +}; + +enum omap_overlay_caps { + OMAP_DSS_OVL_CAP_SCALE = 1 << 0, + OMAP_DSS_OVL_CAP_DISPC = 1 << 1, +}; + +enum omap_overlay_manager_caps { + OMAP_DSS_OVL_MGR_CAP_DISPC = 1 << 0, +}; + +/* RFBI */ + +struct rfbi_timings { + int cs_on_time; + int cs_off_time; + int we_on_time; + int we_off_time; + int re_on_time; + int re_off_time; + int we_cycle_time; + int re_cycle_time; + int cs_pulse_width; + int access_time; + + int clk_div; + + u32 tim[5]; /* set by rfbi_convert_timings() */ + + int converted; +}; + +void omap_rfbi_write_command(const void *buf, u32 len); +void omap_rfbi_read_data(void *buf, u32 len); +void omap_rfbi_write_data(const void *buf, u32 len); +void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width, + u16 x, u16 y, + u16 w, u16 h); +int omap_rfbi_enable_te(bool enable, unsigned line); +int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode, + unsigned hs_pulse_time, unsigned vs_pulse_time, + int hs_pol_inv, int vs_pol_inv, int extif_div); + +/* DSI */ +void dsi_bus_lock(void); +void dsi_bus_unlock(void); +int dsi_vc_dcs_write(int channel, u8 *data, int len); +int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len); +int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen); +int dsi_vc_set_max_rx_packet_size(int channel, u16 len); +int dsi_vc_send_null(int channel); +int dsi_vc_send_bta_sync(int channel); + +/* Board specific data */ +struct omap_dss_board_info { + int (*get_last_off_on_transaction_id)(struct device *dev); + int num_devices; + struct omap_dss_device **devices; + struct omap_dss_device *default_device; +}; + +struct omap_video_timings { + /* Unit: pixels */ + u16 x_res; + /* Unit: pixels */ + u16 y_res; + /* Unit: KHz */ + u32 pixel_clock; + /* Unit: pixel clocks */ + u16 hsw; /* Horizontal synchronization pulse width */ + /* Unit: pixel clocks */ + u16 hfp; /* Horizontal front porch */ + /* Unit: pixel clocks */ + u16 hbp; /* Horizontal back porch */ + /* Unit: line clocks */ + u16 vsw; /* Vertical synchronization pulse width */ + /* Unit: line clocks */ + u16 vfp; /* Vertical front porch */ + /* Unit: line clocks */ + u16 vbp; /* Vertical back porch */ +}; + +#ifdef CONFIG_OMAP2_DSS_VENC +/* Hardcoded timings for tv modes. Venc only uses these to + * identify the mode, and does not actually use the configs + * itself. However, the configs should be something that + * a normal monitor can also show */ +const extern struct omap_video_timings omap_dss_pal_timings; +const extern struct omap_video_timings omap_dss_ntsc_timings; +#endif + +struct omap_overlay_info { + bool enabled; + + u32 paddr; + void __iomem *vaddr; + u16 screen_width; + u16 width; + u16 height; + enum omap_color_mode color_mode; + u8 rotation; + enum omap_dss_rotation_type rotation_type; + bool mirror; + + u16 pos_x; + u16 pos_y; + u16 out_width; /* if 0, out_width == width */ + u16 out_height; /* if 0, out_height == height */ + u8 global_alpha; +}; + +struct omap_overlay { + struct kobject kobj; + struct list_head list; + + /* static fields */ + const char *name; + int id; + enum omap_color_mode supported_modes; + enum omap_overlay_caps caps; + + /* dynamic fields */ + struct omap_overlay_manager *manager; + struct omap_overlay_info info; + + /* if true, info has been changed, but not applied() yet */ + bool info_dirty; + + int (*set_manager)(struct omap_overlay *ovl, + struct omap_overlay_manager *mgr); + int (*unset_manager)(struct omap_overlay *ovl); + + int (*set_overlay_info)(struct omap_overlay *ovl, + struct omap_overlay_info *info); + void (*get_overlay_info)(struct omap_overlay *ovl, + struct omap_overlay_info *info); + + int (*wait_for_go)(struct omap_overlay *ovl); +}; + +struct omap_overlay_manager_info { + u32 default_color; + + enum omap_dss_trans_key_type trans_key_type; + u32 trans_key; + bool trans_enabled; + + bool alpha_enabled; +}; + +struct omap_overlay_manager { + struct kobject kobj; + struct list_head list; + + /* static fields */ + const char *name; + int id; + enum omap_overlay_manager_caps caps; + int num_overlays; + struct omap_overlay **overlays; + enum omap_display_type supported_displays; + + /* dynamic fields */ + struct omap_dss_device *device; + struct omap_overlay_manager_info info; + + bool device_changed; + /* if true, info has been changed but not applied() yet */ + bool info_dirty; + + int (*set_device)(struct omap_overlay_manager *mgr, + struct omap_dss_device *dssdev); + int (*unset_device)(struct omap_overlay_manager *mgr); + + int (*set_manager_info)(struct omap_overlay_manager *mgr, + struct omap_overlay_manager_info *info); + void (*get_manager_info)(struct omap_overlay_manager *mgr, + struct omap_overlay_manager_info *info); + + int (*apply)(struct omap_overlay_manager *mgr); + int (*wait_for_go)(struct omap_overlay_manager *mgr); +}; + +struct omap_dss_device { + struct device dev; + + enum omap_display_type type; + + union { + struct { + u8 data_lines; + } dpi; + + struct { + u8 channel; + u8 data_lines; + } rfbi; + + struct { + u8 datapairs; + unsigned pad_off_pe:1; /* pull pads if disabled */ + unsigned pad_off_pu:1; /* pull up */ + } sdi; + + struct { + u8 clk_lane; + u8 clk_pol; + u8 data1_lane; + u8 data1_pol; + u8 data2_lane; + u8 data2_pol; + unsigned long lp_clk_hz; + unsigned long ddr_clk_hz; + + bool ext_te; + u8 ext_te_gpio; + } dsi; + + struct { + enum omap_dss_venc_type type; + bool invert_polarity; + } venc; + } phy; + + struct { + struct omap_video_timings timings; + + int acbi; /* ac-bias pin transitions per interrupt */ + /* Unit: line clocks */ + int acb; /* ac-bias pin frequency */ + + enum omap_panel_config config; + + u8 recommended_bpp; + + struct omap_dss_device *ctrl; + } panel; + + struct { + u8 pixel_size; + struct rfbi_timings rfbi_timings; + struct omap_dss_device *panel; + } ctrl; + + int reset_gpio; + + int max_backlight_level; + + const char *name; + + /* used to match device to driver */ + const char *driver_name; + + void *data; + + struct omap_dss_driver *driver; + + /* helper variable for driver suspend/resume */ + bool activate_after_resume; + + enum omap_display_caps caps; + + struct omap_overlay_manager *manager; + + enum omap_dss_display_state state; + + int (*enable)(struct omap_dss_device *dssdev); + void (*disable)(struct omap_dss_device *dssdev); + + int (*suspend)(struct omap_dss_device *dssdev); + int (*resume)(struct omap_dss_device *dssdev); + + void (*get_resolution)(struct omap_dss_device *dssdev, + u16 *xres, u16 *yres); + int (*get_recommended_bpp)(struct omap_dss_device *dssdev); + + int (*check_timings)(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); + void (*set_timings)(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); + void (*get_timings)(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); + int (*update)(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h); + int (*sync)(struct omap_dss_device *dssdev); + int (*wait_vsync)(struct omap_dss_device *dssdev); + + int (*set_update_mode)(struct omap_dss_device *dssdev, + enum omap_dss_update_mode); + enum omap_dss_update_mode (*get_update_mode) + (struct omap_dss_device *dssdev); + + int (*enable_te)(struct omap_dss_device *dssdev, bool enable); + int (*get_te)(struct omap_dss_device *dssdev); + + u8 (*get_rotate)(struct omap_dss_device *dssdev); + int (*set_rotate)(struct omap_dss_device *dssdev, u8 rotate); + + bool (*get_mirror)(struct omap_dss_device *dssdev); + int (*set_mirror)(struct omap_dss_device *dssdev, bool enable); + + int (*run_test)(struct omap_dss_device *dssdev, int test); + int (*memory_read)(struct omap_dss_device *dssdev, + void *buf, size_t size, + u16 x, u16 y, u16 w, u16 h); + + int (*set_wss)(struct omap_dss_device *dssdev, u32 wss); + u32 (*get_wss)(struct omap_dss_device *dssdev); + + /* platform specific */ + int (*platform_enable)(struct omap_dss_device *dssdev); + void (*platform_disable)(struct omap_dss_device *dssdev); + int (*set_backlight)(struct omap_dss_device *dssdev, int level); + int (*get_backlight)(struct omap_dss_device *dssdev); +}; + +struct omap_dss_driver { + struct device_driver driver; + + int (*probe)(struct omap_dss_device *); + void (*remove)(struct omap_dss_device *); + + int (*enable)(struct omap_dss_device *display); + void (*disable)(struct omap_dss_device *display); + int (*suspend)(struct omap_dss_device *display); + int (*resume)(struct omap_dss_device *display); + int (*run_test)(struct omap_dss_device *display, int test); + + void (*setup_update)(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h); + + int (*enable_te)(struct omap_dss_device *dssdev, bool enable); + int (*wait_for_te)(struct omap_dss_device *dssdev); + + u8 (*get_rotate)(struct omap_dss_device *dssdev); + int (*set_rotate)(struct omap_dss_device *dssdev, u8 rotate); + + bool (*get_mirror)(struct omap_dss_device *dssdev); + int (*set_mirror)(struct omap_dss_device *dssdev, bool enable); + + int (*memory_read)(struct omap_dss_device *dssdev, + void *buf, size_t size, + u16 x, u16 y, u16 w, u16 h); +}; + +int omap_dss_register_driver(struct omap_dss_driver *); +void omap_dss_unregister_driver(struct omap_dss_driver *); + +int omap_dss_register_device(struct omap_dss_device *); +void omap_dss_unregister_device(struct omap_dss_device *); + +void omap_dss_get_device(struct omap_dss_device *dssdev); +void omap_dss_put_device(struct omap_dss_device *dssdev); +#define for_each_dss_dev(d) while ((d = omap_dss_get_next_device(d)) != NULL) +struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from); +struct omap_dss_device *omap_dss_find_device(void *data, + int (*match)(struct omap_dss_device *dssdev, void *data)); + +int omap_dss_start_device(struct omap_dss_device *dssdev); +void omap_dss_stop_device(struct omap_dss_device *dssdev); + +int omap_dss_get_num_overlay_managers(void); +struct omap_overlay_manager *omap_dss_get_overlay_manager(int num); + +int omap_dss_get_num_overlays(void); +struct omap_overlay *omap_dss_get_overlay(int num); + +typedef void (*omap_dispc_isr_t) (void *arg, u32 mask); +int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask); +int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask); + +int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout); +int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, + unsigned long timeout); + +#define to_dss_driver(x) container_of((x), struct omap_dss_driver, driver) +#define to_dss_device(x) container_of((x), struct omap_dss_device, dev) + +#endif
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index d6d65ef..95e03fd 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig@@ -2146,6 +2146,7 @@ config FB_BROADSHEET a bridge adapter. source "drivers/video/omap/Kconfig" +source "drivers/video/omap2/Kconfig" source "drivers/video/backlight/Kconfig" source "drivers/video/display/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 01a819f..01bf7a5 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile@@ -123,6 +123,7 @@ obj-$(CONFIG_FB_SM501) += sm501fb.o obj-$(CONFIG_FB_XILINX) += xilinxfb.o obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o obj-$(CONFIG_FB_OMAP) += omap/ +obj-$(CONFIG_OMAP2_DSS) += omap2/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/
diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig
new file mode 100644
index 0000000..244b80d
--- /dev/null
+++ b/drivers/video/omap2/Kconfig@@ -0,0 +1,2 @@ +source "drivers/video/omap2/dss/Kconfig" +source "drivers/video/omap2/displays/Kconfig"
diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile
new file mode 100644
index 0000000..2d2c83e
--- /dev/null
+++ b/drivers/video/omap2/Makefile@@ -0,0 +1,2 @@ +obj-y += dss/ +obj-y += displays/
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig
new file mode 100644
index 0000000..8bdc33c
--- /dev/null
+++ b/drivers/video/omap2/displays/Kconfig@@ -0,0 +1,4 @@ +menu "OMAP2/3 Display Device Drivers" + depends on OMAP2_DSS + +endmenu
diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile
new file mode 100644
index 0000000..e69de29
diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig
new file mode 100644
index 0000000..71d8dec
--- /dev/null
+++ b/drivers/video/omap2/dss/Kconfig@@ -0,0 +1,89 @@ +menuconfig OMAP2_DSS + tristate "OMAP2/3 Display Subsystem support (EXPERIMENTAL)" + depends on ARCH_OMAP2 || ARCH_OMAP3 + help + OMAP2/3 Display Subsystem support. + +if OMAP2_DSS + +config OMAP2_VRAM_SIZE + int "VRAM size (MB)" + range 0 32 + default 0 + help + The amount of SDRAM to reserve at boot time for video RAM use. + This VRAM will be used by omapfb and other drivers that need + large continuous RAM area for video use. + + You can also set this with "vram=<bytes>" kernel argument, or + in the board file. + +config OMAP2_DSS_DEBUG_SUPPORT + bool "Debug support" + default y + help + This enables debug messages. You need to enable printing + with 'debug' module parameter. + +config OMAP2_DSS_RFBI + bool "RFBI support" + default n + help + MIPI DBI, or RFBI (Remote Framebuffer Interface), support. + +config OMAP2_DSS_VENC + bool "VENC support" + default y + help + OMAP Video Encoder support. + +config OMAP2_DSS_SDI + bool "SDI support" + depends on ARCH_OMAP3 + default n + help + SDI (Serial Display Interface) support. + +config OMAP2_DSS_DSI + bool "DSI support" + depends on ARCH_OMAP3 + default n + help + MIPI DSI support. + +config OMAP2_DSS_USE_DSI_PLL + bool "Use DSI PLL for PCLK (EXPERIMENTAL)" + default n + depends on OMAP2_DSS_DSI + help + Use DSI PLL to generate pixel clock. Currently only for DPI output. + DSI PLL can be used to generate higher and more precise pixel clocks. + +config OMAP2_DSS_FAKE_VSYNC + bool "Fake VSYNC irq from manual update displays" + default n + help + If this is selected, DSI will generate a fake DISPC VSYNC interrupt + when DSI has sent a frame. This is only needed with DSI or RFBI + displays using manual mode, and you want VSYNC to, for example, + time animation. + +config OMAP2_DSS_MIN_FCK_PER_PCK + int "Minimum FCK/PCK ratio (for scaling)" + range 0 32 + default 0 + help + This can be used to adjust the minimum FCK/PCK ratio. + + With this you can make sure that DISPC FCK is at least + n x PCK. Video plane scaling requires higher FCK than + normally. + + If this is set to 0, there's no extra constraint on the + DISPC FCK. However, the FCK will at minimum be + 2xPCK (if active matrix) or 3xPCK (if passive matrix). + + Max FCK is 173MHz, so this doesn't work if your PCK + is very high. + +endif
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
new file mode 100644
index 0000000..980c72c
--- /dev/null
+++ b/drivers/video/omap2/dss/Makefile@@ -0,0 +1,6 @@ +obj-$(CONFIG_OMAP2_DSS) += omapdss.o +omapdss-y := core.o dss.o dispc.o dpi.o display.o manager.o overlay.o +omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o +omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o +omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o +omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c
new file mode 100644
index 0000000..e8d430c
--- /dev/null
+++ b/drivers/video/omap2/dss/core.c@@ -0,0 +1,917 @@ +/* + * linux/drivers/video/omap2/dss/core.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "CORE" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <linux/io.h> +#include <linux/device.h> + +#include <mach/display.h> +#include <mach/clock.h> + +#include "dss.h" + +static struct { + struct platform_device *pdev; + int ctx_id; + + struct clk *dss_ick; + struct clk *dss1_fck; + struct clk *dss2_fck; + struct clk *dss_54m_fck; + struct clk *dss_96m_fck; + unsigned num_clks_enabled; +} core; + +static void dss_clk_enable_all_no_ctx(void); +static void dss_clk_disable_all_no_ctx(void); +static void dss_clk_enable_no_ctx(enum dss_clock clks); +static void dss_clk_disable_no_ctx(enum dss_clock clks); + +static char *def_disp_name; +module_param_named(def_disp, def_disp_name, charp, 0); +MODULE_PARM_DESC(def_disp_name, "default display name"); + +#ifdef DEBUG +unsigned int dss_debug; +module_param_named(debug, dss_debug, bool, 0644); +#endif + +/* CONTEXT */ +static int dss_get_ctx_id(void) +{ + struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; + int r; + + if (!pdata->get_last_off_on_transaction_id) + return 0; + r = pdata->get_last_off_on_transaction_id(&core.pdev->dev); + if (r < 0) { + dev_err(&core.pdev->dev, "getting transaction ID failed, " + "will force context restore\n"); + r = -1; + } + return r; +} + +int dss_need_ctx_restore(void) +{ + int id = dss_get_ctx_id(); + + if (id < 0 || id != core.ctx_id) { + DSSDBG("ctx id %d -> id %d\n", + core.ctx_id, id); + core.ctx_id = id; + return 1; + } else { + return 0; + } +} + +static void save_all_ctx(void) +{ + DSSDBG("save context\n"); + + dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1); + + dss_save_context(); + dispc_save_context(); +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_save_context(); +#endif + + dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1); +} + +static void restore_all_ctx(void) +{ + DSSDBG("restore context\n"); + + dss_clk_enable_all_no_ctx(); + + dss_restore_context(); + dispc_restore_context(); +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_restore_context(); +#endif + + dss_clk_disable_all_no_ctx(); +} + +/* CLOCKS */ +void dss_dump_clocks(struct seq_file *s) +{ + int i; + struct clk *clocks[5] = { + core.dss_ick, + core.dss1_fck, + core.dss2_fck, + core.dss_54m_fck, + core.dss_96m_fck + }; + + seq_printf(s, "- dss -\n"); + + seq_printf(s, "internal clk count\t%u\n", core.num_clks_enabled); + + for (i = 0; i < 5; i++) { + if (!clocks[i]) + continue; + seq_printf(s, "%-15s\t%lu\t%d\n", + clocks[i]->name, + clk_get_rate(clocks[i]), + clocks[i]->usecount); + } +} + +static int dss_get_clock(struct clk **clock, const char *clk_name) +{ + struct clk *clk; + + clk = clk_get(&core.pdev->dev, clk_name); + + if (IS_ERR(clk)) { + DSSERR("can't get clock %s", clk_name); + return PTR_ERR(clk); + } + + *clock = clk; + + DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk)); + + return 0; +} + +static int dss_get_clocks(void) +{ + int r; + + core.dss_ick = NULL; + core.dss1_fck = NULL; + core.dss2_fck = NULL; + core.dss_54m_fck = NULL; + core.dss_96m_fck = NULL; + + r = dss_get_clock(&core.dss_ick, "ick"); + if (r) + goto err; + + r = dss_get_clock(&core.dss1_fck, "dss1_fck"); + if (r) + goto err; + + r = dss_get_clock(&core.dss2_fck, "dss2_fck"); + if (r) + goto err; + + r = dss_get_clock(&core.dss_54m_fck, "tv_fck"); + if (r) + goto err; + + r = dss_get_clock(&core.dss_96m_fck, "video_fck"); + if (r) + goto err; + + return 0; + +err: + if (core.dss_ick) + clk_put(core.dss_ick); + if (core.dss1_fck) + clk_put(core.dss1_fck); + if (core.dss2_fck) + clk_put(core.dss2_fck); + if (core.dss_54m_fck) + clk_put(core.dss_54m_fck); + if (core.dss_96m_fck) + clk_put(core.dss_96m_fck); + + return r; +} + +static void dss_put_clocks(void) +{ + if (core.dss_96m_fck) + clk_put(core.dss_96m_fck); + clk_put(core.dss_54m_fck); + clk_put(core.dss1_fck); + clk_put(core.dss2_fck); + clk_put(core.dss_ick); +} + +unsigned long dss_clk_get_rate(enum dss_clock clk) +{ + switch (clk) { + case DSS_CLK_ICK: + return clk_get_rate(core.dss_ick); + case DSS_CLK_FCK1: + return clk_get_rate(core.dss1_fck); + case DSS_CLK_FCK2: + return clk_get_rate(core.dss2_fck); + case DSS_CLK_54M: + return clk_get_rate(core.dss_54m_fck); + case DSS_CLK_96M: + return clk_get_rate(core.dss_96m_fck); + } + + BUG(); + return 0; +} + +static unsigned count_clk_bits(enum dss_clock clks) +{ + unsigned num_clks = 0; + + if (clks & DSS_CLK_ICK) + ++num_clks; + if (clks & DSS_CLK_FCK1) + ++num_clks; + if (clks & DSS_CLK_FCK2) + ++num_clks; + if (clks & DSS_CLK_54M) + ++num_clks; + if (clks & DSS_CLK_96M) + ++num_clks; + + return num_clks; +} + +static void dss_clk_enable_no_ctx(enum dss_clock clks) +{ + unsigned num_clks = count_clk_bits(clks); + + if (clks & DSS_CLK_ICK) + clk_enable(core.dss_ick); + if (clks & DSS_CLK_FCK1) + clk_enable(core.dss1_fck); + if (clks & DSS_CLK_FCK2) + clk_enable(core.dss2_fck); + if (clks & DSS_CLK_54M) + clk_enable(core.dss_54m_fck); + if (clks & DSS_CLK_96M) + clk_enable(core.dss_96m_fck); + + core.num_clks_enabled += num_clks; +} + +void dss_clk_enable(enum dss_clock clks) +{ + dss_clk_enable_no_ctx(clks); + + if (cpu_is_omap34xx() && dss_need_ctx_restore()) + restore_all_ctx(); +} + +static void dss_clk_disable_no_ctx(enum dss_clock clks) +{ + unsigned num_clks = count_clk_bits(clks); + + if (clks & DSS_CLK_ICK) + clk_disable(core.dss_ick); + if (clks & DSS_CLK_FCK1) + clk_disable(core.dss1_fck); + if (clks & DSS_CLK_FCK2) + clk_disable(core.dss2_fck); + if (clks & DSS_CLK_54M) + clk_disable(core.dss_54m_fck); + if (clks & DSS_CLK_96M) + clk_disable(core.dss_96m_fck); + + core.num_clks_enabled -= num_clks; +} + +void dss_clk_disable(enum dss_clock clks) +{ + if (cpu_is_omap34xx()) { + unsigned num_clks = count_clk_bits(clks); + + BUG_ON(core.num_clks_enabled < num_clks); + + if (core.num_clks_enabled == num_clks) + save_all_ctx(); + } + + dss_clk_disable_no_ctx(clks); +} + +static void dss_clk_enable_all_no_ctx(void) +{ + enum dss_clock clks; + + clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M; + if (cpu_is_omap34xx()) + clks |= DSS_CLK_96M; + dss_clk_enable_no_ctx(clks); +} + +static void dss_clk_disable_all_no_ctx(void) +{ + enum dss_clock clks; + + clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M; + if (cpu_is_omap34xx()) + clks |= DSS_CLK_96M; + dss_clk_disable_no_ctx(clks); +} + +static void dss_clk_disable_all(void) +{ + enum dss_clock clks; + + clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M; + if (cpu_is_omap34xx()) + clks |= DSS_CLK_96M; + dss_clk_disable(clks); +} + +/* DEBUGFS */ +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) +static void dss_debug_dump_clocks(struct seq_file *s) +{ + dss_dump_clocks(s); + dispc_dump_clocks(s); +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_dump_clocks(s); +#endif +} + +static int dss_debug_show(struct seq_file *s, void *unused) +{ + void (*func)(struct seq_file *) = s->private; + func(s); + return 0; +} + +static int dss_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, dss_debug_show, inode->i_private); +} + +static const struct file_operations dss_debug_fops = { + .open = dss_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *dss_debugfs_dir; + +static int dss_initialize_debugfs(void) +{ + dss_debugfs_dir = debugfs_create_dir("omapdss", NULL); + if (IS_ERR(dss_debugfs_dir)) { + int err = PTR_ERR(dss_debugfs_dir); + dss_debugfs_dir = NULL; + return err; + } + + debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir, + &dss_debug_dump_clocks, &dss_debug_fops); + + debugfs_create_file("dss", S_IRUGO, dss_debugfs_dir, + &dss_dump_regs, &dss_debug_fops); + debugfs_create_file("dispc", S_IRUGO, dss_debugfs_dir, + &dispc_dump_regs, &dss_debug_fops); +#ifdef CONFIG_OMAP2_DSS_RFBI + debugfs_create_file("rfbi", S_IRUGO, dss_debugfs_dir, + &rfbi_dump_regs, &dss_debug_fops); +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + debugfs_create_file("dsi", S_IRUGO, dss_debugfs_dir, + &dsi_dump_regs, &dss_debug_fops); +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir, + &venc_dump_regs, &dss_debug_fops); +#endif + return 0; +} + +static void dss_uninitialize_debugfs(void) +{ + if (dss_debugfs_dir) + debugfs_remove_recursive(dss_debugfs_dir); +} +#endif /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */ + +/* PLATFORM DEVICE */ +static int omap_dss_probe(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int skip_init = 0; + int r; + int i; + + core.pdev = pdev; + + dss_init_overlay_managers(pdev); + dss_init_overlays(pdev); + + r = dss_get_clocks(); + if (r) + goto fail0; + + dss_clk_enable_all_no_ctx(); + + core.ctx_id = dss_get_ctx_id(); + DSSDBG("initial ctx id %u\n", core.ctx_id); + +#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT + /* DISPC_CONTROL */ + if (omap_readl(0x48050440) & 1) /* LCD enabled? */ + skip_init = 1; +#endif + + r = dss_init(skip_init); + if (r) { + DSSERR("Failed to initialize DSS\n"); + goto fail0; + } + +#ifdef CONFIG_OMAP2_DSS_RFBI + r = rfbi_init(); + if (r) { + DSSERR("Failed to initialize rfbi\n"); + goto fail0; + } +#endif + + r = dpi_init(); + if (r) { + DSSERR("Failed to initialize dpi\n"); + goto fail0; + } + + r = dispc_init(); + if (r) { + DSSERR("Failed to initialize dispc\n"); + goto fail0; + } +#ifdef CONFIG_OMAP2_DSS_VENC + r = venc_init(pdev); + if (r) { + DSSERR("Failed to initialize venc\n"); + goto fail0; + } +#endif + if (cpu_is_omap34xx()) { +#ifdef CONFIG_OMAP2_DSS_SDI + r = sdi_init(skip_init); + if (r) { + DSSERR("Failed to initialize SDI\n"); + goto fail0; + } +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + r = dsi_init(pdev); + if (r) { + DSSERR("Failed to initialize DSI\n"); + goto fail0; + } +#endif + } + +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) + r = dss_initialize_debugfs(); + if (r) + goto fail0; +#endif + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + r = omap_dss_register_device(dssdev); + if (r) + DSSERR("device reg failed %d\n", i); + + if (def_disp_name && strcmp(def_disp_name, dssdev->name) == 0) + pdata->default_device = dssdev; + } + + dss_clk_disable_all(); + + return 0; + + /* XXX fail correctly */ +fail0: + return r; +} + +static int omap_dss_remove(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int i; + int c; + +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) + dss_uninitialize_debugfs(); +#endif + +#ifdef CONFIG_OMAP2_DSS_VENC + venc_exit(); +#endif + dispc_exit(); + dpi_exit(); +#ifdef CONFIG_OMAP2_DSS_RFBI + rfbi_exit(); +#endif + if (cpu_is_omap34xx()) { +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_exit(); +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + sdi_exit(); +#endif + } + + dss_exit(); + + /* these should be removed at some point */ + c = core.dss_ick->usecount; + if (c > 0) { + DSSERR("warning: dss_ick usecount %d, disabling\n", c); + while (c-- > 0) + clk_disable(core.dss_ick); + } + + c = core.dss1_fck->usecount; + if (c > 0) { + DSSERR("warning: dss1_fck usecount %d, disabling\n", c); + while (c-- > 0) + clk_disable(core.dss1_fck); + } + + c = core.dss2_fck->usecount; + if (c > 0) { + DSSERR("warning: dss2_fck usecount %d, disabling\n", c); + while (c-- > 0) + clk_disable(core.dss2_fck); + } + + c = core.dss_54m_fck->usecount; + if (c > 0) { + DSSERR("warning: dss_54m_fck usecount %d, disabling\n", c); + while (c-- > 0) + clk_disable(core.dss_54m_fck); + } + + if (core.dss_96m_fck) { + c = core.dss_96m_fck->usecount; + if (c > 0) { + DSSERR("warning: dss_96m_fck usecount %d, disabling\n", + c); + while (c-- > 0) + clk_disable(core.dss_96m_fck); + } + } + + dss_put_clocks(); + + dss_uninit_overlays(pdev); + dss_uninit_overlay_managers(pdev); + + for (i = 0; i < pdata->num_devices; ++i) + omap_dss_unregister_device(pdata->devices[i]); + + return 0; +} + +static void omap_dss_shutdown(struct platform_device *pdev) +{ + DSSDBG("shutdown\n"); +} + +static int omap_dss_suspend(struct platform_device *pdev, pm_message_t state) +{ + DSSDBG("suspend %d\n", state.event); + + return dss_suspend_all_devices(); +} + +static int omap_dss_resume(struct platform_device *pdev) +{ + DSSDBG("resume\n"); + + return dss_resume_all_devices(); +} + +static struct platform_driver omap_dss_driver = { + .probe = omap_dss_probe, + .remove = omap_dss_remove, + .shutdown = omap_dss_shutdown, + .suspend = omap_dss_suspend, + .resume = omap_dss_resume, + .driver = { + .name = "omapdss", + .owner = THIS_MODULE, + }, +}; + +/* BUS */ +static int dss_bus_match(struct device *dev, struct device_driver *driver) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + + DSSDBG("bus_match. dev %s/%s, drv %s\n", + dev_name(dev), dssdev->driver_name, driver->name); + + return strcmp(dssdev->driver_name, driver->name) == 0; +} + +static ssize_t device_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", + dssdev->name ? + dssdev->name : ""); +} + +static struct device_attribute default_dev_attrs[] = { + __ATTR(name, S_IRUGO, device_name_show, NULL), + __ATTR_NULL, +}; + +static ssize_t driver_name_show(struct device_driver *drv, char *buf) +{ + struct omap_dss_driver *dssdrv = to_dss_driver(drv); + return snprintf(buf, PAGE_SIZE, "%s\n", + dssdrv->driver.name ? + dssdrv->driver.name : ""); +} +static struct driver_attribute default_drv_attrs[] = { + __ATTR(name, S_IRUGO, driver_name_show, NULL), + __ATTR_NULL, +}; + +static struct bus_type dss_bus_type = { + .name = "omapdss", + .match = dss_bus_match, + .dev_attrs = default_dev_attrs, + .drv_attrs = default_drv_attrs, +}; + +static void dss_bus_release(struct device *dev) +{ + DSSDBG("bus_release\n"); +} + +static struct device dss_bus = { + .release = dss_bus_release, +}; + +struct bus_type *dss_get_bus(void) +{ + return &dss_bus_type; +} + +/* DRIVER */ +static int dss_driver_probe(struct device *dev) +{ + int r; + struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver); + struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; + bool force; + + DSSDBG("driver_probe: dev %s/%s, drv %s\n", + dev_name(dev), dssdev->driver_name, + dssdrv->driver.name); + + dss_init_device(core.pdev, dssdev); + + /* skip this if the device is behind a ctrl */ + if (!dssdev->panel.ctrl) { + force = pdata->default_device == dssdev; + dss_recheck_connections(dssdev, force); + } + + r = dssdrv->probe(dssdev); + + if (r) { + DSSERR("driver probe failed: %d\n", r); + return r; + } + + DSSDBG("probe done for device %s\n", dev_name(dev)); + + dssdev->driver = dssdrv; + + return 0; +} + +static int dss_driver_remove(struct device *dev) +{ + struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver); + struct omap_dss_device *dssdev = to_dss_device(dev); + + DSSDBG("driver_remove: dev %s/%s\n", dev_name(dev), + dssdev->driver_name); + + dssdrv->remove(dssdev); + + dss_uninit_device(core.pdev, dssdev); + + dssdev->driver = NULL; + + return 0; +} + +int omap_dss_register_driver(struct omap_dss_driver *dssdriver) +{ + dssdriver->driver.bus = &dss_bus_type; + dssdriver->driver.probe = dss_driver_probe; + dssdriver->driver.remove = dss_driver_remove; + return driver_register(&dssdriver->driver); +} +EXPORT_SYMBOL(omap_dss_register_driver); + +void omap_dss_unregister_driver(struct omap_dss_driver *dssdriver) +{ + driver_unregister(&dssdriver->driver); +} +EXPORT_SYMBOL(omap_dss_unregister_driver); + +/* DEVICE */ +static void reset_device(struct device *dev, int check) +{ + u8 *dev_p = (u8 *)dev; + u8 *dev_end = dev_p + sizeof(*dev); + void *saved_pdata; + + saved_pdata = dev->platform_data; + if (check) { + /* + * Check if there is any other setting than platform_data + * in struct device; warn that these will be reset by our + * init. + */ + dev->platform_data = NULL; + while (dev_p < dev_end) { + if (*dev_p) { + WARN("%s: struct device fields will be " + "discarded\n", + __func__); + break; + } + dev_p++; + } + } + memset(dev, 0, sizeof(*dev)); + dev->platform_data = saved_pdata; +} + + +static void omap_dss_dev_release(struct device *dev) +{ + reset_device(dev, 0); +} + +int omap_dss_register_device(struct omap_dss_device *dssdev) +{ + static int dev_num; + static int panel_num; + int r; + + WARN_ON(!dssdev->driver_name); + + reset_device(&dssdev->dev, 1); + dssdev->dev.bus = &dss_bus_type; + dssdev->dev.parent = &dss_bus; + dssdev->dev.release = omap_dss_dev_release; + dev_set_name(&dssdev->dev, "display%d", dev_num++); + r = device_register(&dssdev->dev); + if (r) + return r; + + if (dssdev->ctrl.panel) { + struct omap_dss_device *panel = dssdev->ctrl.panel; + + panel->panel.ctrl = dssdev; + + reset_device(&panel->dev, 1); + panel->dev.bus = &dss_bus_type; + panel->dev.parent = &dssdev->dev; + panel->dev.release = omap_dss_dev_release; + dev_set_name(&panel->dev, "panel%d", panel_num++); + r = device_register(&panel->dev); + if (r) + return r; + } + + return 0; +} + +void omap_dss_unregister_device(struct omap_dss_device *dssdev) +{ + device_unregister(&dssdev->dev); + + if (dssdev->ctrl.panel) { + struct omap_dss_device *panel = dssdev->ctrl.panel; + device_unregister(&panel->dev); + } +} + +/* BUS */ +static int omap_dss_bus_register(void) +{ + int r; + + r = bus_register(&dss_bus_type); + if (r) { + DSSERR("bus register failed\n"); + return r; + } + + dev_set_name(&dss_bus, "omapdss"); + r = device_register(&dss_bus); + if (r) { + DSSERR("bus driver register failed\n"); + bus_unregister(&dss_bus_type); + return r; + } + + return 0; +} + +/* INIT */ + +#ifdef CONFIG_OMAP2_DSS_MODULE +static void omap_dss_bus_unregister(void) +{ + device_unregister(&dss_bus); + + bus_unregister(&dss_bus_type); +} + +static int __init omap_dss_init(void) +{ + int r; + + r = omap_dss_bus_register(); + if (r) + return r; + + r = platform_driver_register(&omap_dss_driver); + if (r) { + omap_dss_bus_unregister(); + return r; + } + + return 0; +} + +static void __exit omap_dss_exit(void) +{ + platform_driver_unregister(&omap_dss_driver); + + omap_dss_bus_unregister(); +} + +module_init(omap_dss_init); +module_exit(omap_dss_exit); +#else +static int __init omap_dss_init(void) +{ + return omap_dss_bus_register(); +} + +static int __init omap_dss_init2(void) +{ + return platform_driver_register(&omap_dss_driver); +} + +core_initcall(omap_dss_init); +device_initcall(omap_dss_init2); +#endif + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); +MODULE_DESCRIPTION("OMAP2/3 Display Subsystem"); +MODULE_LICENSE("GPL v2"); +
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
new file mode 100644
index 0000000..d061d75
--- /dev/null
+++ b/drivers/video/omap2/dss/dispc.c@@ -0,0 +1,3177 @@ +/* + * linux/drivers/video/omap2/dss/dispc.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DISPC" + +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/jiffies.h> +#include <linux/seq_file.h> +#include <linux/delay.h> +#include <linux/workqueue.h> + +#include <mach/sram.h> +#include <mach/board.h> +#include <mach/clock.h> + +#include <mach/display.h> + +#include "dss.h" + +/* DISPC */ +#define DISPC_BASE 0x48050400 + +#define DISPC_SZ_REGS SZ_1K + +struct dispc_reg { u16 idx; }; + +#define DISPC_REG(idx) ((const struct dispc_reg) { idx }) + +/* DISPC common */ +#define DISPC_REVISION DISPC_REG(0x0000) +#define DISPC_SYSCONFIG DISPC_REG(0x0010) +#define DISPC_SYSSTATUS DISPC_REG(0x0014) +#define DISPC_IRQSTATUS DISPC_REG(0x0018) +#define DISPC_IRQENABLE DISPC_REG(0x001C) +#define DISPC_CONTROL DISPC_REG(0x0040) +#define DISPC_CONFIG DISPC_REG(0x0044) +#define DISPC_CAPABLE DISPC_REG(0x0048) +#define DISPC_DEFAULT_COLOR0 DISPC_REG(0x004C) +#define DISPC_DEFAULT_COLOR1 DISPC_REG(0x0050) +#define DISPC_TRANS_COLOR0 DISPC_REG(0x0054) +#define DISPC_TRANS_COLOR1 DISPC_REG(0x0058) +#define DISPC_LINE_STATUS DISPC_REG(0x005C) +#define DISPC_LINE_NUMBER DISPC_REG(0x0060) +#define DISPC_TIMING_H DISPC_REG(0x0064) +#define DISPC_TIMING_V DISPC_REG(0x0068) +#define DISPC_POL_FREQ DISPC_REG(0x006C) +#define DISPC_DIVISOR DISPC_REG(0x0070) +#define DISPC_GLOBAL_ALPHA DISPC_REG(0x0074) +#define DISPC_SIZE_DIG DISPC_REG(0x0078) +#define DISPC_SIZE_LCD DISPC_REG(0x007C) + +/* DISPC GFX plane */ +#define DISPC_GFX_BA0 DISPC_REG(0x0080) +#define DISPC_GFX_BA1 DISPC_REG(0x0084) +#define DISPC_GFX_POSITION DISPC_REG(0x0088) +#define DISPC_GFX_SIZE DISPC_REG(0x008C) +#define DISPC_GFX_ATTRIBUTES DISPC_REG(0x00A0) +#define DISPC_GFX_FIFO_THRESHOLD DISPC_REG(0x00A4) +#define DISPC_GFX_FIFO_SIZE_STATUS DISPC_REG(0x00A8) +#define DISPC_GFX_ROW_INC DISPC_REG(0x00AC) +#define DISPC_GFX_PIXEL_INC DISPC_REG(0x00B0) +#define DISPC_GFX_WINDOW_SKIP DISPC_REG(0x00B4) +#define DISPC_GFX_TABLE_BA DISPC_REG(0x00B8) + +#define DISPC_DATA_CYCLE1 DISPC_REG(0x01D4) +#define DISPC_DATA_CYCLE2 DISPC_REG(0x01D8) +#define DISPC_DATA_CYCLE3 DISPC_REG(0x01DC) + +#define DISPC_CPR_COEF_R DISPC_REG(0x0220) +#define DISPC_CPR_COEF_G DISPC_REG(0x0224) +#define DISPC_CPR_COEF_B DISPC_REG(0x0228) + +#define DISPC_GFX_PRELOAD DISPC_REG(0x022C) + +/* DISPC Video plane, n = 0 for VID1 and n = 1 for VID2 */ +#define DISPC_VID_REG(n, idx) DISPC_REG(0x00BC + (n)*0x90 + idx) + +#define DISPC_VID_BA0(n) DISPC_VID_REG(n, 0x0000) +#define DISPC_VID_BA1(n) DISPC_VID_REG(n, 0x0004) +#define DISPC_VID_POSITION(n) DISPC_VID_REG(n, 0x0008) +#define DISPC_VID_SIZE(n) DISPC_VID_REG(n, 0x000C) +#define DISPC_VID_ATTRIBUTES(n) DISPC_VID_REG(n, 0x0010) +#define DISPC_VID_FIFO_THRESHOLD(n) DISPC_VID_REG(n, 0x0014) +#define DISPC_VID_FIFO_SIZE_STATUS(n) DISPC_VID_REG(n, 0x0018) +#define DISPC_VID_ROW_INC(n) DISPC_VID_REG(n, 0x001C) +#define DISPC_VID_PIXEL_INC(n) DISPC_VID_REG(n, 0x0020) +#define DISPC_VID_FIR(n) DISPC_VID_REG(n, 0x0024) +#define DISPC_VID_PICTURE_SIZE(n) DISPC_VID_REG(n, 0x0028) +#define DISPC_VID_ACCU0(n) DISPC_VID_REG(n, 0x002C) +#define DISPC_VID_ACCU1(n) DISPC_VID_REG(n, 0x0030) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_H(n, i) DISPC_REG(0x00F0 + (n)*0x90 + (i)*0x8) +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_HV(n, i) DISPC_REG(0x00F4 + (n)*0x90 + (i)*0x8) +/* coef index i = {0, 1, 2, 3, 4} */ +#define DISPC_VID_CONV_COEF(n, i) DISPC_REG(0x0130 + (n)*0x90 + (i)*0x4) +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_V(n, i) DISPC_REG(0x01E0 + (n)*0x20 + (i)*0x4) + +#define DISPC_VID_PRELOAD(n) DISPC_REG(0x230 + (n)*0x04) + + +#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ + DISPC_IRQ_OCP_ERR | \ + DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ + DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ + DISPC_IRQ_SYNC_LOST | \ + DISPC_IRQ_SYNC_LOST_DIGIT) + +#define DISPC_MAX_NR_ISRS 8 + +struct omap_dispc_isr_data { + omap_dispc_isr_t isr; + void *arg; + u32 mask; +}; + +#define REG_GET(idx, start, end) \ + FLD_GET(dispc_read_reg(idx), start, end) + +#define REG_FLD_MOD(idx, val, start, end) \ + dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end)) + +static const struct dispc_reg dispc_reg_att[] = { DISPC_GFX_ATTRIBUTES, + DISPC_VID_ATTRIBUTES(0), + DISPC_VID_ATTRIBUTES(1) }; + +static struct { + void __iomem *base; + + struct clk *dpll4_m4_ck; + + unsigned long cache_req_pck; + unsigned long cache_prate; + struct dispc_clock_info cache_cinfo; + + u32 fifo_size[3]; + + spinlock_t irq_lock; + u32 irq_error_mask; + struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; + u32 error_irqs; + struct work_struct error_work; + + u32 ctx[DISPC_SZ_REGS / sizeof(u32)]; +} dispc; + +static void _omap_dispc_set_irqs(void); + +static inline void dispc_write_reg(const struct dispc_reg idx, u32 val) +{ + __raw_writel(val, dispc.base + idx.idx); +} + +static inline u32 dispc_read_reg(const struct dispc_reg idx) +{ + return __raw_readl(dispc.base + idx.idx); +} + +#define SR(reg) \ + dispc.ctx[(DISPC_##reg).idx / sizeof(u32)] = dispc_read_reg(DISPC_##reg) +#define RR(reg) \ + dispc_write_reg(DISPC_##reg, dispc.ctx[(DISPC_##reg).idx / sizeof(u32)]) + +void dispc_save_context(void) +{ + if (cpu_is_omap24xx()) + return; + + SR(SYSCONFIG); + SR(IRQENABLE); + SR(CONTROL); + SR(CONFIG); + SR(DEFAULT_COLOR0); + SR(DEFAULT_COLOR1); + SR(TRANS_COLOR0); + SR(TRANS_COLOR1); + SR(LINE_NUMBER); + SR(TIMING_H); + SR(TIMING_V); + SR(POL_FREQ); + SR(DIVISOR); + SR(GLOBAL_ALPHA); + SR(SIZE_DIG); + SR(SIZE_LCD); + + SR(GFX_BA0); + SR(GFX_BA1); + SR(GFX_POSITION); + SR(GFX_SIZE); + SR(GFX_ATTRIBUTES); + SR(GFX_FIFO_THRESHOLD); + SR(GFX_ROW_INC); + SR(GFX_PIXEL_INC); + SR(GFX_WINDOW_SKIP); + SR(GFX_TABLE_BA); + + SR(DATA_CYCLE1); + SR(DATA_CYCLE2); + SR(DATA_CYCLE3); + + SR(CPR_COEF_R); + SR(CPR_COEF_G); + SR(CPR_COEF_B); + + SR(GFX_PRELOAD); + + /* VID1 */ + SR(VID_BA0(0)); + SR(VID_BA1(0)); + SR(VID_POSITION(0)); + SR(VID_SIZE(0)); + SR(VID_ATTRIBUTES(0)); + SR(VID_FIFO_THRESHOLD(0)); + SR(VID_ROW_INC(0)); + SR(VID_PIXEL_INC(0)); + SR(VID_FIR(0)); + SR(VID_PICTURE_SIZE(0)); + SR(VID_ACCU0(0)); + SR(VID_ACCU1(0)); + + SR(VID_FIR_COEF_H(0, 0)); + SR(VID_FIR_COEF_H(0, 1)); + SR(VID_FIR_COEF_H(0, 2)); + SR(VID_FIR_COEF_H(0, 3)); + SR(VID_FIR_COEF_H(0, 4)); + SR(VID_FIR_COEF_H(0, 5)); + SR(VID_FIR_COEF_H(0, 6)); + SR(VID_FIR_COEF_H(0, 7)); + + SR(VID_FIR_COEF_HV(0, 0)); + SR(VID_FIR_COEF_HV(0, 1)); + SR(VID_FIR_COEF_HV(0, 2)); + SR(VID_FIR_COEF_HV(0, 3)); + SR(VID_FIR_COEF_HV(0, 4)); + SR(VID_FIR_COEF_HV(0, 5)); + SR(VID_FIR_COEF_HV(0, 6)); + SR(VID_FIR_COEF_HV(0, 7)); + + SR(VID_CONV_COEF(0, 0)); + SR(VID_CONV_COEF(0, 1)); + SR(VID_CONV_COEF(0, 2)); + SR(VID_CONV_COEF(0, 3)); + SR(VID_CONV_COEF(0, 4)); + + SR(VID_FIR_COEF_V(0, 0)); + SR(VID_FIR_COEF_V(0, 1)); + SR(VID_FIR_COEF_V(0, 2)); + SR(VID_FIR_COEF_V(0, 3)); + SR(VID_FIR_COEF_V(0, 4)); + SR(VID_FIR_COEF_V(0, 5)); + SR(VID_FIR_COEF_V(0, 6)); + SR(VID_FIR_COEF_V(0, 7)); + + SR(VID_PRELOAD(0)); + + /* VID2 */ + SR(VID_BA0(1)); + SR(VID_BA1(1)); + SR(VID_POSITION(1)); + SR(VID_SIZE(1)); + SR(VID_ATTRIBUTES(1)); + SR(VID_FIFO_THRESHOLD(1)); + SR(VID_ROW_INC(1)); + SR(VID_PIXEL_INC(1)); + SR(VID_FIR(1)); + SR(VID_PICTURE_SIZE(1)); + SR(VID_ACCU0(1)); + SR(VID_ACCU1(1)); + + SR(VID_FIR_COEF_H(1, 0)); + SR(VID_FIR_COEF_H(1, 1)); + SR(VID_FIR_COEF_H(1, 2)); + SR(VID_FIR_COEF_H(1, 3)); + SR(VID_FIR_COEF_H(1, 4)); + SR(VID_FIR_COEF_H(1, 5)); + SR(VID_FIR_COEF_H(1, 6)); + SR(VID_FIR_COEF_H(1, 7)); + + SR(VID_FIR_COEF_HV(1, 0)); + SR(VID_FIR_COEF_HV(1, 1)); + SR(VID_FIR_COEF_HV(1, 2)); + SR(VID_FIR_COEF_HV(1, 3)); + SR(VID_FIR_COEF_HV(1, 4)); + SR(VID_FIR_COEF_HV(1, 5)); + SR(VID_FIR_COEF_HV(1, 6)); + SR(VID_FIR_COEF_HV(1, 7)); + + SR(VID_CONV_COEF(1, 0)); + SR(VID_CONV_COEF(1, 1)); + SR(VID_CONV_COEF(1, 2)); + SR(VID_CONV_COEF(1, 3)); + SR(VID_CONV_COEF(1, 4)); + + SR(VID_FIR_COEF_V(1, 0)); + SR(VID_FIR_COEF_V(1, 1)); + SR(VID_FIR_COEF_V(1, 2)); + SR(VID_FIR_COEF_V(1, 3)); + SR(VID_FIR_COEF_V(1, 4)); + SR(VID_FIR_COEF_V(1, 5)); + SR(VID_FIR_COEF_V(1, 6)); + SR(VID_FIR_COEF_V(1, 7)); + + SR(VID_PRELOAD(1)); +} + +void dispc_restore_context(void) +{ + RR(SYSCONFIG); + RR(IRQENABLE); + /*RR(CONTROL);*/ + RR(CONFIG); + RR(DEFAULT_COLOR0); + RR(DEFAULT_COLOR1); + RR(TRANS_COLOR0); + RR(TRANS_COLOR1); + RR(LINE_NUMBER); + RR(TIMING_H); + RR(TIMING_V); + RR(POL_FREQ); + RR(DIVISOR); + RR(GLOBAL_ALPHA); + RR(SIZE_DIG); + RR(SIZE_LCD); + + RR(GFX_BA0); + RR(GFX_BA1); + RR(GFX_POSITION); + RR(GFX_SIZE); + RR(GFX_ATTRIBUTES); + RR(GFX_FIFO_THRESHOLD); + RR(GFX_ROW_INC); + RR(GFX_PIXEL_INC); + RR(GFX_WINDOW_SKIP); + RR(GFX_TABLE_BA); + + RR(DATA_CYCLE1); + RR(DATA_CYCLE2); + RR(DATA_CYCLE3); + + RR(CPR_COEF_R); + RR(CPR_COEF_G); + RR(CPR_COEF_B); + + RR(GFX_PRELOAD); + + /* VID1 */ + RR(VID_BA0(0)); + RR(VID_BA1(0)); + RR(VID_POSITION(0)); + RR(VID_SIZE(0)); + RR(VID_ATTRIBUTES(0)); + RR(VID_FIFO_THRESHOLD(0)); + RR(VID_ROW_INC(0)); + RR(VID_PIXEL_INC(0)); + RR(VID_FIR(0)); + RR(VID_PICTURE_SIZE(0)); + RR(VID_ACCU0(0)); + RR(VID_ACCU1(0)); + + RR(VID_FIR_COEF_H(0, 0)); + RR(VID_FIR_COEF_H(0, 1)); + RR(VID_FIR_COEF_H(0, 2)); + RR(VID_FIR_COEF_H(0, 3)); + RR(VID_FIR_COEF_H(0, 4)); + RR(VID_FIR_COEF_H(0, 5)); + RR(VID_FIR_COEF_H(0, 6)); + RR(VID_FIR_COEF_H(0, 7)); + + RR(VID_FIR_COEF_HV(0, 0)); + RR(VID_FIR_COEF_HV(0, 1)); + RR(VID_FIR_COEF_HV(0, 2)); + RR(VID_FIR_COEF_HV(0, 3)); + RR(VID_FIR_COEF_HV(0, 4)); + RR(VID_FIR_COEF_HV(0, 5)); + RR(VID_FIR_COEF_HV(0, 6)); + RR(VID_FIR_COEF_HV(0, 7)); + + RR(VID_CONV_COEF(0, 0)); + RR(VID_CONV_COEF(0, 1)); + RR(VID_CONV_COEF(0, 2)); + RR(VID_CONV_COEF(0, 3)); + RR(VID_CONV_COEF(0, 4)); + + RR(VID_FIR_COEF_V(0, 0)); + RR(VID_FIR_COEF_V(0, 1)); + RR(VID_FIR_COEF_V(0, 2)); + RR(VID_FIR_COEF_V(0, 3)); + RR(VID_FIR_COEF_V(0, 4)); + RR(VID_FIR_COEF_V(0, 5)); + RR(VID_FIR_COEF_V(0, 6)); + RR(VID_FIR_COEF_V(0, 7)); + + RR(VID_PRELOAD(0)); + + /* VID2 */ + RR(VID_BA0(1)); + RR(VID_BA1(1)); + RR(VID_POSITION(1)); + RR(VID_SIZE(1)); + RR(VID_ATTRIBUTES(1)); + RR(VID_FIFO_THRESHOLD(1)); + RR(VID_ROW_INC(1)); + RR(VID_PIXEL_INC(1)); + RR(VID_FIR(1)); + RR(VID_PICTURE_SIZE(1)); + RR(VID_ACCU0(1)); + RR(VID_ACCU1(1)); + + RR(VID_FIR_COEF_H(1, 0)); + RR(VID_FIR_COEF_H(1, 1)); + RR(VID_FIR_COEF_H(1, 2)); + RR(VID_FIR_COEF_H(1, 3)); + RR(VID_FIR_COEF_H(1, 4)); + RR(VID_FIR_COEF_H(1, 5)); + RR(VID_FIR_COEF_H(1, 6)); + RR(VID_FIR_COEF_H(1, 7)); + + RR(VID_FIR_COEF_HV(1, 0)); + RR(VID_FIR_COEF_HV(1, 1)); + RR(VID_FIR_COEF_HV(1, 2)); + RR(VID_FIR_COEF_HV(1, 3)); + RR(VID_FIR_COEF_HV(1, 4)); + RR(VID_FIR_COEF_HV(1, 5)); + RR(VID_FIR_COEF_HV(1, 6)); + RR(VID_FIR_COEF_HV(1, 7)); + + RR(VID_CONV_COEF(1, 0)); + RR(VID_CONV_COEF(1, 1)); + RR(VID_CONV_COEF(1, 2)); + RR(VID_CONV_COEF(1, 3)); + RR(VID_CONV_COEF(1, 4)); + + RR(VID_FIR_COEF_V(1, 0)); + RR(VID_FIR_COEF_V(1, 1)); + RR(VID_FIR_COEF_V(1, 2)); + RR(VID_FIR_COEF_V(1, 3)); + RR(VID_FIR_COEF_V(1, 4)); + RR(VID_FIR_COEF_V(1, 5)); + RR(VID_FIR_COEF_V(1, 6)); + RR(VID_FIR_COEF_V(1, 7)); + + RR(VID_PRELOAD(1)); + + /* enable last, because LCD & DIGIT enable are here */ + RR(CONTROL); +} + +#undef SR +#undef RR + +static inline void enable_clocks(bool enable) +{ + if (enable) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + else + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +} + +bool dispc_go_busy(enum omap_channel channel) +{ + int bit; + + if (channel == OMAP_DSS_CHANNEL_LCD) + bit = 5; /* GOLCD */ + else + bit = 6; /* GODIGIT */ + + return REG_GET(DISPC_CONTROL, bit, bit) == 1; +} + +void dispc_go(enum omap_channel channel) +{ + int bit; + + enable_clocks(1); + + if (channel == OMAP_DSS_CHANNEL_LCD) + bit = 0; /* LCDENABLE */ + else + bit = 1; /* DIGITALENABLE */ + + /* if the channel is not enabled, we don't need GO */ + if (REG_GET(DISPC_CONTROL, bit, bit) == 0) + goto end; + + if (channel == OMAP_DSS_CHANNEL_LCD) + bit = 5; /* GOLCD */ + else + bit = 6; /* GODIGIT */ + + if (REG_GET(DISPC_CONTROL, bit, bit) == 1) { + DSSERR("GO bit not down for channel %d\n", channel); + goto end; + } + + DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : "DIGIT"); + + REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); +end: + enable_clocks(0); +} + +static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(DISPC_VID_FIR_COEF_H(plane-1, reg), value); +} + +static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(DISPC_VID_FIR_COEF_HV(plane-1, reg), value); +} + +static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(DISPC_VID_FIR_COEF_V(plane-1, reg), value); +} + +static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, + int vscaleup, int five_taps) +{ + /* Coefficients for horizontal up-sampling */ + static const u32 coef_hup[8] = { + 0x00800000, + 0x0D7CF800, + 0x1E70F5FF, + 0x335FF5FE, + 0xF74949F7, + 0xF55F33FB, + 0xF5701EFE, + 0xF87C0DFF, + }; + + /* Coefficients for horizontal down-sampling */ + static const u32 coef_hdown[8] = { + 0x24382400, + 0x28371FFE, + 0x2C361BFB, + 0x303516F9, + 0x11343311, + 0x1635300C, + 0x1B362C08, + 0x1F372804, + }; + + /* Coefficients for horizontal and vertical up-sampling */ + static const u32 coef_hvup[2][8] = { + { + 0x00800000, + 0x037B02FF, + 0x0C6F05FE, + 0x205907FB, + 0x00404000, + 0x075920FE, + 0x056F0CFF, + 0x027B0300, + }, + { + 0x00800000, + 0x0D7CF8FF, + 0x1E70F5FE, + 0x335FF5FB, + 0xF7404000, + 0xF55F33FE, + 0xF5701EFF, + 0xF87C0D00, + }, + }; + + /* Coefficients for horizontal and vertical down-sampling */ + static const u32 coef_hvdown[2][8] = { + { + 0x24382400, + 0x28391F04, + 0x2D381B08, + 0x3237170C, + 0x123737F7, + 0x173732F9, + 0x1B382DFB, + 0x1F3928FE, + }, + { + 0x24382400, + 0x28371F04, + 0x2C361B08, + 0x3035160C, + 0x113433F7, + 0x163530F9, + 0x1B362CFB, + 0x1F3728FE, + }, + }; + + /* Coefficients for vertical up-sampling */ + static const u32 coef_vup[8] = { + 0x00000000, + 0x0000FF00, + 0x0000FEFF, + 0x0000FBFE, + 0x000000F7, + 0x0000FEFB, + 0x0000FFFE, + 0x000000FF, + }; + + + /* Coefficients for vertical down-sampling */ + static const u32 coef_vdown[8] = { + 0x00000000, + 0x000004FE, + 0x000008FB, + 0x00000CF9, + 0x0000F711, + 0x0000F90C, + 0x0000FB08, + 0x0000FE04, + }; + + const u32 *h_coef; + const u32 *hv_coef; + const u32 *hv_coef_mod; + const u32 *v_coef; + int i; + + if (hscaleup) + h_coef = coef_hup; + else + h_coef = coef_hdown; + + if (vscaleup) { + hv_coef = coef_hvup[five_taps]; + v_coef = coef_vup; + + if (hscaleup) + hv_coef_mod = NULL; + else + hv_coef_mod = coef_hvdown[five_taps]; + } else { + hv_coef = coef_hvdown[five_taps]; + v_coef = coef_vdown; + + if (hscaleup) + hv_coef_mod = coef_hvup[five_taps]; + else + hv_coef_mod = NULL; + } + + for (i = 0; i < 8; i++) { + u32 h, hv; + + h = h_coef[i]; + + hv = hv_coef[i]; + + if (hv_coef_mod) { + hv &= 0xffffff00; + hv |= (hv_coef_mod[i] & 0xff); + } + + _dispc_write_firh_reg(plane, i, h); + _dispc_write_firhv_reg(plane, i, hv); + } + + if (!five_taps) + return; + + for (i = 0; i < 8; i++) { + u32 v; + v = v_coef[i]; + _dispc_write_firv_reg(plane, i, v); + } +} + +static void _dispc_setup_color_conv_coef(void) +{ + const struct color_conv_coef { + int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb; + int full_range; + } ctbl_bt601_5 = { + 298, 409, 0, 298, -208, -100, 298, 0, 517, 0, + }; + + const struct color_conv_coef *ct; + +#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0)) + + ct = &ctbl_bt601_5; + + dispc_write_reg(DISPC_VID_CONV_COEF(0, 0), CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 1), CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 2), CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 3), CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 4), CVAL(0, ct->bcb)); + + dispc_write_reg(DISPC_VID_CONV_COEF(1, 0), CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 1), CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 2), CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 3), CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 4), CVAL(0, ct->bcb)); + +#undef CVAL + + REG_FLD_MOD(DISPC_VID_ATTRIBUTES(0), ct->full_range, 11, 11); + REG_FLD_MOD(DISPC_VID_ATTRIBUTES(1), ct->full_range, 11, 11); +} + + +static void _dispc_set_plane_ba0(enum omap_plane plane, u32 paddr) +{ + const struct dispc_reg ba0_reg[] = { DISPC_GFX_BA0, + DISPC_VID_BA0(0), + DISPC_VID_BA0(1) }; + + dispc_write_reg(ba0_reg[plane], paddr); +} + +static void _dispc_set_plane_ba1(enum omap_plane plane, u32 paddr) +{ + const struct dispc_reg ba1_reg[] = { DISPC_GFX_BA1, + DISPC_VID_BA1(0), + DISPC_VID_BA1(1) }; + + dispc_write_reg(ba1_reg[plane], paddr); +} + +static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y) +{ + const struct dispc_reg pos_reg[] = { DISPC_GFX_POSITION, + DISPC_VID_POSITION(0), + DISPC_VID_POSITION(1) }; + + u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0); + dispc_write_reg(pos_reg[plane], val); +} + +static void _dispc_set_pic_size(enum omap_plane plane, int width, int height) +{ + const struct dispc_reg siz_reg[] = { DISPC_GFX_SIZE, + DISPC_VID_PICTURE_SIZE(0), + DISPC_VID_PICTURE_SIZE(1) }; + u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + dispc_write_reg(siz_reg[plane], val); +} + +static void _dispc_set_vid_size(enum omap_plane plane, int width, int height) +{ + u32 val; + const struct dispc_reg vsi_reg[] = { DISPC_VID_SIZE(0), + DISPC_VID_SIZE(1) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + dispc_write_reg(vsi_reg[plane-1], val); +} + +static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha) +{ + + BUG_ON(plane == OMAP_DSS_VIDEO1); + + if (plane == OMAP_DSS_GFX) + REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 7, 0); + else if (plane == OMAP_DSS_VIDEO2) + REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 23, 16); +} + +static void _dispc_set_pix_inc(enum omap_plane plane, s32 inc) +{ + const struct dispc_reg ri_reg[] = { DISPC_GFX_PIXEL_INC, + DISPC_VID_PIXEL_INC(0), + DISPC_VID_PIXEL_INC(1) }; + + dispc_write_reg(ri_reg[plane], inc); +} + +static void _dispc_set_row_inc(enum omap_plane plane, s32 inc) +{ + const struct dispc_reg ri_reg[] = { DISPC_GFX_ROW_INC, + DISPC_VID_ROW_INC(0), + DISPC_VID_ROW_INC(1) }; + + dispc_write_reg(ri_reg[plane], inc); +} + +static void _dispc_set_color_mode(enum omap_plane plane, + enum omap_color_mode color_mode) +{ + u32 m = 0; + + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + m = 0x0; break; + case OMAP_DSS_COLOR_CLUT2: + m = 0x1; break; + case OMAP_DSS_COLOR_CLUT4: + m = 0x2; break; + case OMAP_DSS_COLOR_CLUT8: + m = 0x3; break; + case OMAP_DSS_COLOR_RGB12U: + m = 0x4; break; + case OMAP_DSS_COLOR_ARGB16: + m = 0x5; break; + case OMAP_DSS_COLOR_RGB16: + m = 0x6; break; + case OMAP_DSS_COLOR_RGB24U: + m = 0x8; break; + case OMAP_DSS_COLOR_RGB24P: + m = 0x9; break; + case OMAP_DSS_COLOR_YUV2: + m = 0xa; break; + case OMAP_DSS_COLOR_UYVY: + m = 0xb; break; + case OMAP_DSS_COLOR_ARGB32: + m = 0xc; break; + case OMAP_DSS_COLOR_RGBA32: + m = 0xd; break; + case OMAP_DSS_COLOR_RGBX32: + m = 0xe; break; + default: + BUG(); break; + } + + REG_FLD_MOD(dispc_reg_att[plane], m, 4, 1); +} + +static void _dispc_set_channel_out(enum omap_plane plane, + enum omap_channel channel) +{ + int shift; + u32 val; + + switch (plane) { + case OMAP_DSS_GFX: + shift = 8; + break; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + shift = 16; + break; + default: + BUG(); + return; + } + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, channel, shift, shift); + dispc_write_reg(dispc_reg_att[plane], val); +} + +void dispc_set_burst_size(enum omap_plane plane, + enum omap_burst_size burst_size) +{ + int shift; + u32 val; + + enable_clocks(1); + + switch (plane) { + case OMAP_DSS_GFX: + shift = 6; + break; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + shift = 14; + break; + default: + BUG(); + return; + } + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, burst_size, shift+1, shift); + dispc_write_reg(dispc_reg_att[plane], val); + + enable_clocks(0); +} + +static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable) +{ + u32 val; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, enable, 9, 9); + dispc_write_reg(dispc_reg_att[plane], val); +} + +void dispc_enable_replication(enum omap_plane plane, bool enable) +{ + int bit; + + if (plane == OMAP_DSS_GFX) + bit = 5; + else + bit = 10; + + enable_clocks(1); + REG_FLD_MOD(dispc_reg_att[plane], enable, bit, bit); + enable_clocks(0); +} + +void dispc_set_lcd_size(u16 width, u16 height) +{ + u32 val; + BUG_ON((width > (1 << 11)) || (height > (1 << 11))); + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + enable_clocks(1); + dispc_write_reg(DISPC_SIZE_LCD, val); + enable_clocks(0); +} + +void dispc_set_digit_size(u16 width, u16 height) +{ + u32 val; + BUG_ON((width > (1 << 11)) || (height > (1 << 11))); + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + enable_clocks(1); + dispc_write_reg(DISPC_SIZE_DIG, val); + enable_clocks(0); +} + +static void dispc_read_plane_fifo_sizes(void) +{ + const struct dispc_reg fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS, + DISPC_VID_FIFO_SIZE_STATUS(0), + DISPC_VID_FIFO_SIZE_STATUS(1) }; + u32 size; + int plane; + + enable_clocks(1); + + for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) { + if (cpu_is_omap24xx()) + size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 8, 0); + else if (cpu_is_omap34xx()) + size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 10, 0); + else + BUG(); + + dispc.fifo_size[plane] = size; + } + + enable_clocks(0); +} + +u32 dispc_get_plane_fifo_size(enum omap_plane plane) +{ + return dispc.fifo_size[plane]; +} + +void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high) +{ + const struct dispc_reg ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD, + DISPC_VID_FIFO_THRESHOLD(0), + DISPC_VID_FIFO_THRESHOLD(1) }; + enable_clocks(1); + + DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n", + plane, + REG_GET(ftrs_reg[plane], 11, 0), + REG_GET(ftrs_reg[plane], 27, 16), + low, high); + + if (cpu_is_omap24xx()) + dispc_write_reg(ftrs_reg[plane], + FLD_VAL(high, 24, 16) | FLD_VAL(low, 8, 0)); + else + dispc_write_reg(ftrs_reg[plane], + FLD_VAL(high, 27, 16) | FLD_VAL(low, 11, 0)); + + enable_clocks(0); +} + +void dispc_enable_fifomerge(bool enable) +{ + enable_clocks(1); + + DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled"); + REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14); + + enable_clocks(0); +} + +static void _dispc_set_fir(enum omap_plane plane, int hinc, int vinc) +{ + u32 val; + const struct dispc_reg fir_reg[] = { DISPC_VID_FIR(0), + DISPC_VID_FIR(1) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + if (cpu_is_omap24xx()) + val = FLD_VAL(vinc, 27, 16) | FLD_VAL(hinc, 11, 0); + else + val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0); + dispc_write_reg(fir_reg[plane-1], val); +} + +static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + const struct dispc_reg ac0_reg[] = { DISPC_VID_ACCU0(0), + DISPC_VID_ACCU0(1) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); + dispc_write_reg(ac0_reg[plane-1], val); +} + +static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + const struct dispc_reg ac1_reg[] = { DISPC_VID_ACCU1(0), + DISPC_VID_ACCU1(1) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); + dispc_write_reg(ac1_reg[plane-1], val); +} + + +static void _dispc_set_scaling(enum omap_plane plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, bool five_taps, + bool fieldmode) +{ + int fir_hinc; + int fir_vinc; + int hscaleup, vscaleup; + int accu0 = 0; + int accu1 = 0; + u32 l; + + BUG_ON(plane == OMAP_DSS_GFX); + + hscaleup = orig_width <= out_width; + vscaleup = orig_height <= out_height; + + _dispc_set_scale_coef(plane, hscaleup, vscaleup, five_taps); + + if (!orig_width || orig_width == out_width) + fir_hinc = 0; + else + fir_hinc = 1024 * orig_width / out_width; + + if (!orig_height || orig_height == out_height) + fir_vinc = 0; + else + fir_vinc = 1024 * orig_height / out_height; + + _dispc_set_fir(plane, fir_hinc, fir_vinc); + + l = dispc_read_reg(dispc_reg_att[plane]); + l &= ~((0x0f << 5) | (0x3 << 21)); + + l |= fir_hinc ? (1 << 5) : 0; + l |= fir_vinc ? (1 << 6) : 0; + + l |= hscaleup ? 0 : (1 << 7); + l |= vscaleup ? 0 : (1 << 8); + + l |= five_taps ? (1 << 21) : 0; + l |= five_taps ? (1 << 22) : 0; + + dispc_write_reg(dispc_reg_att[plane], l); + + /* + * field 0 = even field = bottom field + * field 1 = odd field = top field + */ + if (ilace && !fieldmode) { + accu1 = 0; + accu0 = (fir_vinc / 2) & 0x3ff; + if (accu0 >= 1024/2) { + accu1 = 1024/2; + accu0 -= accu1; + } + } + + _dispc_set_vid_accu0(plane, 0, accu0); + _dispc_set_vid_accu1(plane, 0, accu1); +} + +static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation, + bool mirroring, enum omap_color_mode color_mode) +{ + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) { + int vidrot = 0; + + if (mirroring) { + switch (rotation) { + case 0: + vidrot = 2; + break; + case 1: + vidrot = 1; + break; + case 2: + vidrot = 0; + break; + case 3: + vidrot = 3; + break; + } + } else { + switch (rotation) { + case 0: + vidrot = 0; + break; + case 1: + vidrot = 1; + break; + case 2: + vidrot = 2; + break; + case 3: + vidrot = 3; + break; + } + } + + REG_FLD_MOD(dispc_reg_att[plane], vidrot, 13, 12); + + if (rotation == 1 || rotation == 3) + REG_FLD_MOD(dispc_reg_att[plane], 0x1, 18, 18); + else + REG_FLD_MOD(dispc_reg_att[plane], 0x0, 18, 18); + } else { + REG_FLD_MOD(dispc_reg_att[plane], 0, 13, 12); + REG_FLD_MOD(dispc_reg_att[plane], 0, 18, 18); + } +} + +static s32 pixinc(int pixels, u8 ps) +{ + if (pixels == 1) + return 1; + else if (pixels > 1) + return 1 + (pixels - 1) * ps; + else if (pixels < 0) + return 1 - (-pixels + 1) * ps; + else + BUG(); +} + +static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, + u16 screen_width, + u16 width, u16 height, + enum omap_color_mode color_mode, bool fieldmode, + unsigned int field_offset, + unsigned *offset0, unsigned *offset1, + s32 *row_inc, s32 *pix_inc) +{ + u8 ps; + + switch (color_mode) { + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_ARGB16: + ps = 2; + break; + + case OMAP_DSS_COLOR_RGB24P: + ps = 3; + break; + + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + ps = 4; + break; + + default: + BUG(); + return; + } + + DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width, + width, height); + + /* + * field 0 = even field = bottom field + * field 1 = odd field = top field + */ + switch (rotation + mirror * 4) { + case 0: + case 2: + /* + * If the pixel format is YUV or UYVY divide the width + * of the image by 2 for 0 and 180 degree rotation. + */ + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + width = width >> 1; + case 1: + case 3: + *offset1 = 0; + if (field_offset) + *offset0 = field_offset * screen_width * ps; + else + *offset0 = 0; + + *row_inc = pixinc(1 + (screen_width - width) + + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(1, ps); + break; + + case 4: + case 6: + /* If the pixel format is YUV or UYVY divide the width + * of the image by 2 for 0 degree and 180 degree + */ + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + width = width >> 1; + case 5: + case 7: + *offset1 = 0; + if (field_offset) + *offset0 = field_offset * screen_width * ps; + else + *offset0 = 0; + *row_inc = pixinc(1 - (screen_width + width) - + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(1, ps); + break; + + default: + BUG(); + } +} + +static void calc_dma_rotation_offset(u8 rotation, bool mirror, + u16 screen_width, + u16 width, u16 height, + enum omap_color_mode color_mode, bool fieldmode, + unsigned int field_offset, + unsigned *offset0, unsigned *offset1, + s32 *row_inc, s32 *pix_inc) +{ + u8 ps; + u16 fbw, fbh; + + switch (color_mode) { + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_ARGB16: + ps = 2; + break; + + case OMAP_DSS_COLOR_RGB24P: + ps = 3; + break; + + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + ps = 4; + break; + + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + ps = 2; + break; + default: + BUG(); + return; + } + + DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width, + width, height); + + /* width & height are overlay sizes, convert to fb sizes */ + + if (rotation == 0 || rotation == 2) { + fbw = width; + fbh = height; + } else { + fbw = height; + fbh = width; + } + + /* + * field 0 = even field = bottom field + * field 1 = odd field = top field + */ + switch (rotation + mirror * 4) { + case 0: + *offset1 = 0; + if (field_offset) + *offset0 = *offset1 + field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(1 + (screen_width - fbw) + + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(1, ps); + break; + case 1: + *offset1 = screen_width * (fbh - 1) * ps; + if (field_offset) + *offset0 = *offset1 + field_offset * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(screen_width * (fbh - 1) + 1 + + (fieldmode ? 1 : 0), ps); + *pix_inc = pixinc(-screen_width, ps); + break; + case 2: + *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; + if (field_offset) + *offset0 = *offset1 - field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(-1 - + (screen_width - fbw) - + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(-1, ps); + break; + case 3: + *offset1 = (fbw - 1) * ps; + if (field_offset) + *offset0 = *offset1 - field_offset * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(-screen_width * (fbh - 1) - 1 - + (fieldmode ? 1 : 0), ps); + *pix_inc = pixinc(screen_width, ps); + break; + + /* mirroring */ + case 0 + 4: + *offset1 = (fbw - 1) * ps; + if (field_offset) + *offset0 = *offset1 + field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(screen_width * 2 - 1 + + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(-1, ps); + break; + + case 1 + 4: + *offset1 = 0; + if (field_offset) + *offset0 = *offset1 + field_offset * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(-screen_width * (fbh - 1) + 1 + + (fieldmode ? 1 : 0), + ps); + *pix_inc = pixinc(screen_width, ps); + break; + + case 2 + 4: + *offset1 = screen_width * (fbh - 1) * ps; + if (field_offset) + *offset0 = *offset1 - field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(1 - screen_width * 2 - + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(1, ps); + break; + + case 3 + 4: + *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; + if (field_offset) + *offset0 = *offset1 - field_offset * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(screen_width * (fbh - 1) - 1 - + (fieldmode ? 1 : 0), + ps); + *pix_inc = pixinc(-screen_width, ps); + break; + + default: + BUG(); + } +} + +static unsigned long calc_fclk_five_taps(u16 width, u16 height, + u16 out_width, u16 out_height, enum omap_color_mode color_mode) +{ + u32 fclk = 0; + /* FIXME venc pclk? */ + u64 tmp, pclk = dispc_pclk_rate(); + + if (height > out_height) { + /* FIXME get real display PPL */ + unsigned int ppl = 800; + + tmp = pclk * height * out_width; + do_div(tmp, 2 * out_height * ppl); + fclk = tmp; + + if (height > 2 * out_height && ppl != out_width) { + tmp = pclk * (height - 2 * out_height) * out_width; + do_div(tmp, 2 * out_height * (ppl - out_width)); + fclk = max(fclk, (u32) tmp); + } + } + + if (width > out_width) { + tmp = pclk * width; + do_div(tmp, out_width); + fclk = max(fclk, (u32) tmp); + + if (color_mode == OMAP_DSS_COLOR_RGB24U) + fclk <<= 1; + } + + return fclk; +} + +static unsigned long calc_fclk(u16 width, u16 height, + u16 out_width, u16 out_height) +{ + unsigned int hf, vf; + + /* + * FIXME how to determine the 'A' factor + * for the no downscaling case ? + */ + + if (width > 3 * out_width) + hf = 4; + else if (width > 2 * out_width) + hf = 3; + else if (width > out_width) + hf = 2; + else + hf = 1; + + if (height > out_height) + vf = 2; + else + vf = 1; + + /* FIXME venc pclk? */ + return dispc_pclk_rate() * vf * hf; +} + +void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out) +{ + enable_clocks(1); + _dispc_set_channel_out(plane, channel_out); + enable_clocks(0); +} + +static int _dispc_setup_plane(enum omap_plane plane, + u32 paddr, u16 screen_width, + u16 pos_x, u16 pos_y, + u16 width, u16 height, + u16 out_width, u16 out_height, + enum omap_color_mode color_mode, + bool ilace, + enum omap_dss_rotation_type rotation_type, + u8 rotation, int mirror, + u8 global_alpha) +{ + const int maxdownscale = cpu_is_omap34xx() ? 4 : 2; + bool five_taps = 0; + bool fieldmode = 0; + int cconv = 0; + unsigned offset0, offset1; + s32 row_inc; + s32 pix_inc; + u16 frame_height = height; + unsigned int field_offset = 0; + + if (paddr == 0) + return -EINVAL; + + if (ilace && height == out_height) + fieldmode = 1; + + if (ilace) { + if (fieldmode) + height /= 2; + pos_y /= 2; + out_height /= 2; + + DSSDBG("adjusting for ilace: height %d, pos_y %d, " + "out_height %d\n", + height, pos_y, out_height); + } + + if (plane == OMAP_DSS_GFX) { + if (width != out_width || height != out_height) + return -EINVAL; + + switch (color_mode) { + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_RGB24P: + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + break; + + default: + return -EINVAL; + } + } else { + /* video plane */ + + unsigned long fclk = 0; + + if (out_width < width / maxdownscale || + out_width > width * 8) + return -EINVAL; + + if (out_height < height / maxdownscale || + out_height > height * 8) + return -EINVAL; + + switch (color_mode) { + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_RGB24P: + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_RGBX32: + break; + + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + if (plane == OMAP_DSS_VIDEO1) + return -EINVAL; + break; + + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + cconv = 1; + break; + + default: + return -EINVAL; + } + + /* Must use 5-tap filter? */ + five_taps = height > out_height * 2; + + if (!five_taps) { + fclk = calc_fclk(width, height, + out_width, out_height); + + /* Try 5-tap filter if 3-tap fclk is too high */ + if (cpu_is_omap34xx() && height > out_height && + fclk > dispc_fclk_rate()) + five_taps = true; + } + + if (width > (2048 >> five_taps)) + return -EINVAL; + + if (five_taps) + fclk = calc_fclk_five_taps(width, height, + out_width, out_height, color_mode); + + DSSDBG("required fclk rate = %lu Hz\n", fclk); + DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate()); + + if (fclk > dispc_fclk_rate()) + return -EINVAL; + } + + if (ilace && !fieldmode) { + /* + * when downscaling the bottom field may have to start several + * source lines below the top field. Unfortunately ACCUI + * registers will only hold the fractional part of the offset + * so the integer part must be added to the base address of the + * bottom field. + */ + if (!height || height == out_height) + field_offset = 0; + else + field_offset = height / out_height / 2; + } + + /* Fields are independent but interleaved in memory. */ + if (fieldmode) + field_offset = 1; + + if (rotation_type == OMAP_DSS_ROT_DMA) + calc_dma_rotation_offset(rotation, mirror, + screen_width, width, frame_height, color_mode, + fieldmode, field_offset, + &offset0, &offset1, &row_inc, &pix_inc); + else + calc_vrfb_rotation_offset(rotation, mirror, + screen_width, width, frame_height, color_mode, + fieldmode, field_offset, + &offset0, &offset1, &row_inc, &pix_inc); + + DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n", + offset0, offset1, row_inc, pix_inc); + + _dispc_set_color_mode(plane, color_mode); + + _dispc_set_plane_ba0(plane, paddr + offset0); + _dispc_set_plane_ba1(plane, paddr + offset1); + + _dispc_set_row_inc(plane, row_inc); + _dispc_set_pix_inc(plane, pix_inc); + + DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, width, height, + out_width, out_height); + + _dispc_set_plane_pos(plane, pos_x, pos_y); + + _dispc_set_pic_size(plane, width, height); + + if (plane != OMAP_DSS_GFX) { + _dispc_set_scaling(plane, width, height, + out_width, out_height, + ilace, five_taps, fieldmode); + _dispc_set_vid_size(plane, out_width, out_height); + _dispc_set_vid_color_conv(plane, cconv); + } + + _dispc_set_rotation_attrs(plane, rotation, mirror, color_mode); + + if (plane != OMAP_DSS_VIDEO1) + _dispc_setup_global_alpha(plane, global_alpha); + + return 0; +} + +static void _dispc_enable_plane(enum omap_plane plane, bool enable) +{ + REG_FLD_MOD(dispc_reg_att[plane], enable ? 1 : 0, 0, 0); +} + +static void dispc_disable_isr(void *data, u32 mask) +{ + struct completion *compl = data; + complete(compl); +} + +static void _enable_lcd_out(bool enable) +{ + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0); +} + +void dispc_enable_lcd_out(bool enable) +{ + struct completion frame_done_completion; + bool is_on; + int r; + + enable_clocks(1); + + /* When we disable LCD output, we need to wait until frame is done. + * Otherwise the DSS is still working, and turning off the clocks + * prevents DSS from going to OFF mode */ + is_on = REG_GET(DISPC_CONTROL, 0, 0); + + if (!enable && is_on) { + init_completion(&frame_done_completion); + + r = omap_dispc_register_isr(dispc_disable_isr, + &frame_done_completion, + DISPC_IRQ_FRAMEDONE); + + if (r) + DSSERR("failed to register FRAMEDONE isr\n"); + } + + _enable_lcd_out(enable); + + if (!enable && is_on) { + if (!wait_for_completion_timeout(&frame_done_completion, + msecs_to_jiffies(100))) + DSSERR("timeout waiting for FRAME DONE\n"); + + r = omap_dispc_unregister_isr(dispc_disable_isr, + &frame_done_completion, + DISPC_IRQ_FRAMEDONE); + + if (r) + DSSERR("failed to unregister FRAMEDONE isr\n"); + } + + enable_clocks(0); +} + +static void _enable_digit_out(bool enable) +{ + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1); +} + +void dispc_enable_digit_out(bool enable) +{ + struct completion frame_done_completion; + int r; + + enable_clocks(1); + + if (REG_GET(DISPC_CONTROL, 1, 1) == enable) { + enable_clocks(0); + return; + } + + if (enable) { + unsigned long flags; + /* When we enable digit output, we'll get an extra digit + * sync lost interrupt, that we need to ignore */ + spin_lock_irqsave(&dispc.irq_lock, flags); + dispc.irq_error_mask &= ~DISPC_IRQ_SYNC_LOST_DIGIT; + _omap_dispc_set_irqs(); + spin_unlock_irqrestore(&dispc.irq_lock, flags); + } + + /* When we disable digit output, we need to wait until fields are done. + * Otherwise the DSS is still working, and turning off the clocks + * prevents DSS from going to OFF mode. And when enabling, we need to + * wait for the extra sync losts */ + init_completion(&frame_done_completion); + + r = omap_dispc_register_isr(dispc_disable_isr, &frame_done_completion, + DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD); + if (r) + DSSERR("failed to register EVSYNC isr\n"); + + _enable_digit_out(enable); + + /* XXX I understand from TRM that we should only wait for the + * current field to complete. But it seems we have to wait + * for both fields */ + if (!wait_for_completion_timeout(&frame_done_completion, + msecs_to_jiffies(100))) + DSSERR("timeout waiting for EVSYNC\n"); + + if (!wait_for_completion_timeout(&frame_done_completion, + msecs_to_jiffies(100))) + DSSERR("timeout waiting for EVSYNC\n"); + + r = omap_dispc_unregister_isr(dispc_disable_isr, + &frame_done_completion, + DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD); + if (r) + DSSERR("failed to unregister EVSYNC isr\n"); + + if (enable) { + unsigned long flags; + spin_lock_irqsave(&dispc.irq_lock, flags); + dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; + dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT); + _omap_dispc_set_irqs(); + spin_unlock_irqrestore(&dispc.irq_lock, flags); + } + + enable_clocks(0); +} + +void dispc_lcd_enable_signal_polarity(bool act_high) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29); + enable_clocks(0); +} + +void dispc_lcd_enable_signal(bool enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28); + enable_clocks(0); +} + +void dispc_pck_free_enable(bool enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27); + enable_clocks(0); +} + +void dispc_enable_fifohandcheck(bool enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16); + enable_clocks(0); +} + + +void dispc_set_lcd_display_type(enum omap_lcd_display_type type) +{ + int mode; + + switch (type) { + case OMAP_DSS_LCD_DISPLAY_STN: + mode = 0; + break; + + case OMAP_DSS_LCD_DISPLAY_TFT: + mode = 1; + break; + + default: + BUG(); + return; + } + + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3); + enable_clocks(0); +} + +void dispc_set_loadmode(enum omap_dss_load_mode mode) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1); + enable_clocks(0); +} + + +void dispc_set_default_color(enum omap_channel channel, u32 color) +{ + const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0, + DISPC_DEFAULT_COLOR1 }; + + enable_clocks(1); + dispc_write_reg(def_reg[channel], color); + enable_clocks(0); +} + +u32 dispc_get_default_color(enum omap_channel channel) +{ + const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0, + DISPC_DEFAULT_COLOR1 }; + u32 l; + + BUG_ON(channel != OMAP_DSS_CHANNEL_DIGIT && + channel != OMAP_DSS_CHANNEL_LCD); + + enable_clocks(1); + l = dispc_read_reg(def_reg[channel]); + enable_clocks(0); + + return l; +} + +void dispc_set_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type type, + u32 trans_key) +{ + const struct dispc_reg tr_reg[] = { + DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 }; + + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + REG_FLD_MOD(DISPC_CONFIG, type, 11, 11); + else /* OMAP_DSS_CHANNEL_DIGIT */ + REG_FLD_MOD(DISPC_CONFIG, type, 13, 13); + + dispc_write_reg(tr_reg[ch], trans_key); + enable_clocks(0); +} + +void dispc_get_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type *type, + u32 *trans_key) +{ + const struct dispc_reg tr_reg[] = { + DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 }; + + enable_clocks(1); + if (type) { + if (ch == OMAP_DSS_CHANNEL_LCD) + *type = REG_GET(DISPC_CONFIG, 11, 11); + else if (ch == OMAP_DSS_CHANNEL_DIGIT) + *type = REG_GET(DISPC_CONFIG, 13, 13); + else + BUG(); + } + + if (trans_key) + *trans_key = dispc_read_reg(tr_reg[ch]); + enable_clocks(0); +} + +void dispc_enable_trans_key(enum omap_channel ch, bool enable) +{ + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10); + else /* OMAP_DSS_CHANNEL_DIGIT */ + REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12); + enable_clocks(0); +} +void dispc_enable_alpha_blending(enum omap_channel ch, bool enable) +{ + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18); + else /* OMAP_DSS_CHANNEL_DIGIT */ + REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19); + enable_clocks(0); +} +bool dispc_alpha_blending_enabled(enum omap_channel ch) +{ + bool enabled; + + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + enabled = REG_GET(DISPC_CONFIG, 18, 18); + else if (ch == OMAP_DSS_CHANNEL_DIGIT) + enabled = REG_GET(DISPC_CONFIG, 18, 18); + else + BUG(); + enable_clocks(0); + + return enabled; + +} + + +bool dispc_trans_key_enabled(enum omap_channel ch) +{ + bool enabled; + + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + enabled = REG_GET(DISPC_CONFIG, 10, 10); + else if (ch == OMAP_DSS_CHANNEL_DIGIT) + enabled = REG_GET(DISPC_CONFIG, 12, 12); + else + BUG(); + enable_clocks(0); + + return enabled; +} + + +void dispc_set_tft_data_lines(u8 data_lines) +{ + int code; + + switch (data_lines) { + case 12: + code = 0; + break; + case 16: + code = 1; + break; + case 18: + code = 2; + break; + case 24: + code = 3; + break; + default: + BUG(); + return; + } + + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, code, 9, 8); + enable_clocks(0); +} + +void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode) +{ + u32 l; + int stallmode; + int gpout0 = 1; + int gpout1; + + switch (mode) { + case OMAP_DSS_PARALLELMODE_BYPASS: + stallmode = 0; + gpout1 = 1; + break; + + case OMAP_DSS_PARALLELMODE_RFBI: + stallmode = 1; + gpout1 = 0; + break; + + case OMAP_DSS_PARALLELMODE_DSI: + stallmode = 1; + gpout1 = 1; + break; + + default: + BUG(); + return; + } + + enable_clocks(1); + + l = dispc_read_reg(DISPC_CONTROL); + + l = FLD_MOD(l, stallmode, 11, 11); + l = FLD_MOD(l, gpout0, 15, 15); + l = FLD_MOD(l, gpout1, 16, 16); + + dispc_write_reg(DISPC_CONTROL, l); + + enable_clocks(0); +} + +static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, + int vsw, int vfp, int vbp) +{ + if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) { + if (hsw < 1 || hsw > 64 || + hfp < 1 || hfp > 256 || + hbp < 1 || hbp > 256 || + vsw < 1 || vsw > 64 || + vfp < 0 || vfp > 255 || + vbp < 0 || vbp > 255) + return false; + } else { + if (hsw < 1 || hsw > 256 || + hfp < 1 || hfp > 4096 || + hbp < 1 || hbp > 4096 || + vsw < 1 || vsw > 256 || + vfp < 0 || vfp > 4095 || + vbp < 0 || vbp > 4095) + return false; + } + + return true; +} + +bool dispc_lcd_timings_ok(struct omap_video_timings *timings) +{ + return _dispc_lcd_timings_ok(timings->hsw, timings->hfp, + timings->hbp, timings->vsw, + timings->vfp, timings->vbp); +} + +static void _dispc_set_lcd_timings(int hsw, int hfp, int hbp, + int vsw, int vfp, int vbp) +{ + u32 timing_h, timing_v; + + if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) { + timing_h = FLD_VAL(hsw-1, 5, 0) | FLD_VAL(hfp-1, 15, 8) | + FLD_VAL(hbp-1, 27, 20); + + timing_v = FLD_VAL(vsw-1, 5, 0) | FLD_VAL(vfp, 15, 8) | + FLD_VAL(vbp, 27, 20); + } else { + timing_h = FLD_VAL(hsw-1, 7, 0) | FLD_VAL(hfp-1, 19, 8) | + FLD_VAL(hbp-1, 31, 20); + + timing_v = FLD_VAL(vsw-1, 7, 0) | FLD_VAL(vfp, 19, 8) | + FLD_VAL(vbp, 31, 20); + } + + enable_clocks(1); + dispc_write_reg(DISPC_TIMING_H, timing_h); + dispc_write_reg(DISPC_TIMING_V, timing_v); + enable_clocks(0); +} + +/* change name to mode? */ +void dispc_set_lcd_timings(struct omap_video_timings *timings) +{ + unsigned xtot, ytot; + unsigned long ht, vt; + + if (!_dispc_lcd_timings_ok(timings->hsw, timings->hfp, + timings->hbp, timings->vsw, + timings->vfp, timings->vbp)) + BUG(); + + _dispc_set_lcd_timings(timings->hsw, timings->hfp, timings->hbp, + timings->vsw, timings->vfp, timings->vbp); + + dispc_set_lcd_size(timings->x_res, timings->y_res); + + xtot = timings->x_res + timings->hfp + timings->hsw + timings->hbp; + ytot = timings->y_res + timings->vfp + timings->vsw + timings->vbp; + + ht = (timings->pixel_clock * 1000) / xtot; + vt = (timings->pixel_clock * 1000) / xtot / ytot; + + DSSDBG("xres %u yres %u\n", timings->x_res, timings->y_res); + DSSDBG("pck %u\n", timings->pixel_clock); + DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", + timings->hsw, timings->hfp, timings->hbp, + timings->vsw, timings->vfp, timings->vbp); + + DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); +} + +void dispc_set_lcd_divisor(u16 lck_div, u16 pck_div) +{ + BUG_ON(lck_div < 1); + BUG_ON(pck_div < 2); + + enable_clocks(1); + dispc_write_reg(DISPC_DIVISOR, + FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); + enable_clocks(0); +} + +static void dispc_get_lcd_divisor(int *lck_div, int *pck_div) +{ + u32 l; + l = dispc_read_reg(DISPC_DIVISOR); + *lck_div = FLD_GET(l, 23, 16); + *pck_div = FLD_GET(l, 7, 0); +} + +unsigned long dispc_fclk_rate(void) +{ + unsigned long r = 0; + + if (dss_get_dispc_clk_source() == 0) + r = dss_clk_get_rate(DSS_CLK_FCK1); + else +#ifdef CONFIG_OMAP2_DSS_DSI + r = dsi_get_dsi1_pll_rate(); +#else + BUG(); +#endif + return r; +} + +unsigned long dispc_lclk_rate(void) +{ + int lcd; + unsigned long r; + u32 l; + + l = dispc_read_reg(DISPC_DIVISOR); + + lcd = FLD_GET(l, 23, 16); + + r = dispc_fclk_rate(); + + return r / lcd; +} + +unsigned long dispc_pclk_rate(void) +{ + int lcd, pcd; + unsigned long r; + u32 l; + + l = dispc_read_reg(DISPC_DIVISOR); + + lcd = FLD_GET(l, 23, 16); + pcd = FLD_GET(l, 7, 0); + + r = dispc_fclk_rate(); + + return r / lcd / pcd; +} + +void dispc_dump_clocks(struct seq_file *s) +{ + int lcd, pcd; + + enable_clocks(1); + + dispc_get_lcd_divisor(&lcd, &pcd); + + seq_printf(s, "- dispc -\n"); + + seq_printf(s, "dispc fclk source = %s\n", + dss_get_dispc_clk_source() == 0 ? + "dss1_alwon_fclk" : "dsi1_pll_fclk"); + + seq_printf(s, "pixel clk = %lu / %d / %d = %lu\n", + dispc_fclk_rate(), + lcd, pcd, + dispc_pclk_rate()); + + enable_clocks(0); +} + +void dispc_dump_regs(struct seq_file *s) +{ +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dispc_read_reg(r)) + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + DUMPREG(DISPC_REVISION); + DUMPREG(DISPC_SYSCONFIG); + DUMPREG(DISPC_SYSSTATUS); + DUMPREG(DISPC_IRQSTATUS); + DUMPREG(DISPC_IRQENABLE); + DUMPREG(DISPC_CONTROL); + DUMPREG(DISPC_CONFIG); + DUMPREG(DISPC_CAPABLE); + DUMPREG(DISPC_DEFAULT_COLOR0); + DUMPREG(DISPC_DEFAULT_COLOR1); + DUMPREG(DISPC_TRANS_COLOR0); + DUMPREG(DISPC_TRANS_COLOR1); + DUMPREG(DISPC_LINE_STATUS); + DUMPREG(DISPC_LINE_NUMBER); + DUMPREG(DISPC_TIMING_H); + DUMPREG(DISPC_TIMING_V); + DUMPREG(DISPC_POL_FREQ); + DUMPREG(DISPC_DIVISOR); + DUMPREG(DISPC_GLOBAL_ALPHA); + DUMPREG(DISPC_SIZE_DIG); + DUMPREG(DISPC_SIZE_LCD); + + DUMPREG(DISPC_GFX_BA0); + DUMPREG(DISPC_GFX_BA1); + DUMPREG(DISPC_GFX_POSITION); + DUMPREG(DISPC_GFX_SIZE); + DUMPREG(DISPC_GFX_ATTRIBUTES); + DUMPREG(DISPC_GFX_FIFO_THRESHOLD); + DUMPREG(DISPC_GFX_FIFO_SIZE_STATUS); + DUMPREG(DISPC_GFX_ROW_INC); + DUMPREG(DISPC_GFX_PIXEL_INC); + DUMPREG(DISPC_GFX_WINDOW_SKIP); + DUMPREG(DISPC_GFX_TABLE_BA); + + DUMPREG(DISPC_DATA_CYCLE1); + DUMPREG(DISPC_DATA_CYCLE2); + DUMPREG(DISPC_DATA_CYCLE3); + + DUMPREG(DISPC_CPR_COEF_R); + DUMPREG(DISPC_CPR_COEF_G); + DUMPREG(DISPC_CPR_COEF_B); + + DUMPREG(DISPC_GFX_PRELOAD); + + DUMPREG(DISPC_VID_BA0(0)); + DUMPREG(DISPC_VID_BA1(0)); + DUMPREG(DISPC_VID_POSITION(0)); + DUMPREG(DISPC_VID_SIZE(0)); + DUMPREG(DISPC_VID_ATTRIBUTES(0)); + DUMPREG(DISPC_VID_FIFO_THRESHOLD(0)); + DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(0)); + DUMPREG(DISPC_VID_ROW_INC(0)); + DUMPREG(DISPC_VID_PIXEL_INC(0)); + DUMPREG(DISPC_VID_FIR(0)); + DUMPREG(DISPC_VID_PICTURE_SIZE(0)); + DUMPREG(DISPC_VID_ACCU0(0)); + DUMPREG(DISPC_VID_ACCU1(0)); + + DUMPREG(DISPC_VID_BA0(1)); + DUMPREG(DISPC_VID_BA1(1)); + DUMPREG(DISPC_VID_POSITION(1)); + DUMPREG(DISPC_VID_SIZE(1)); + DUMPREG(DISPC_VID_ATTRIBUTES(1)); + DUMPREG(DISPC_VID_FIFO_THRESHOLD(1)); + DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(1)); + DUMPREG(DISPC_VID_ROW_INC(1)); + DUMPREG(DISPC_VID_PIXEL_INC(1)); + DUMPREG(DISPC_VID_FIR(1)); + DUMPREG(DISPC_VID_PICTURE_SIZE(1)); + DUMPREG(DISPC_VID_ACCU0(1)); + DUMPREG(DISPC_VID_ACCU1(1)); + + DUMPREG(DISPC_VID_FIR_COEF_H(0, 0)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 1)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 2)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 3)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 4)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 5)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 6)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 7)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 0)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 1)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 2)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 3)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 4)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 5)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 6)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 7)); + DUMPREG(DISPC_VID_CONV_COEF(0, 0)); + DUMPREG(DISPC_VID_CONV_COEF(0, 1)); + DUMPREG(DISPC_VID_CONV_COEF(0, 2)); + DUMPREG(DISPC_VID_CONV_COEF(0, 3)); + DUMPREG(DISPC_VID_CONV_COEF(0, 4)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 0)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 1)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 2)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 3)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 4)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 5)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 6)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 7)); + + DUMPREG(DISPC_VID_FIR_COEF_H(1, 0)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 1)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 2)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 3)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 4)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 5)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 6)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 7)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 0)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 1)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 2)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 3)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 4)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 5)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 6)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 7)); + DUMPREG(DISPC_VID_CONV_COEF(1, 0)); + DUMPREG(DISPC_VID_CONV_COEF(1, 1)); + DUMPREG(DISPC_VID_CONV_COEF(1, 2)); + DUMPREG(DISPC_VID_CONV_COEF(1, 3)); + DUMPREG(DISPC_VID_CONV_COEF(1, 4)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 0)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 1)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 2)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 3)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 4)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 5)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 6)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 7)); + + DUMPREG(DISPC_VID_PRELOAD(0)); + DUMPREG(DISPC_VID_PRELOAD(1)); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +#undef DUMPREG +} + +static void _dispc_set_pol_freq(bool onoff, bool rf, bool ieo, bool ipc, + bool ihs, bool ivs, u8 acbi, u8 acb) +{ + u32 l = 0; + + DSSDBG("onoff %d rf %d ieo %d ipc %d ihs %d ivs %d acbi %d acb %d\n", + onoff, rf, ieo, ipc, ihs, ivs, acbi, acb); + + l |= FLD_VAL(onoff, 17, 17); + l |= FLD_VAL(rf, 16, 16); + l |= FLD_VAL(ieo, 15, 15); + l |= FLD_VAL(ipc, 14, 14); + l |= FLD_VAL(ihs, 13, 13); + l |= FLD_VAL(ivs, 12, 12); + l |= FLD_VAL(acbi, 11, 8); + l |= FLD_VAL(acb, 7, 0); + + enable_clocks(1); + dispc_write_reg(DISPC_POL_FREQ, l); + enable_clocks(0); +} + +void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb) +{ + _dispc_set_pol_freq((config & OMAP_DSS_LCD_ONOFF) != 0, + (config & OMAP_DSS_LCD_RF) != 0, + (config & OMAP_DSS_LCD_IEO) != 0, + (config & OMAP_DSS_LCD_IPC) != 0, + (config & OMAP_DSS_LCD_IHS) != 0, + (config & OMAP_DSS_LCD_IVS) != 0, + acbi, acb); +} + +void find_lck_pck_divs(bool is_tft, unsigned long req_pck, unsigned long fck, + u16 *lck_div, u16 *pck_div) +{ + u16 pcd_min = is_tft ? 2 : 3; + unsigned long best_pck; + u16 best_ld, cur_ld; + u16 best_pd, cur_pd; + + best_pck = 0; + best_ld = 0; + best_pd = 0; + + for (cur_ld = 1; cur_ld <= 255; ++cur_ld) { + unsigned long lck = fck / cur_ld; + + for (cur_pd = pcd_min; cur_pd <= 255; ++cur_pd) { + unsigned long pck = lck / cur_pd; + long old_delta = abs(best_pck - req_pck); + long new_delta = abs(pck - req_pck); + + if (best_pck == 0 || new_delta < old_delta) { + best_pck = pck; + best_ld = cur_ld; + best_pd = cur_pd; + + if (pck == req_pck) + goto found; + } + + if (pck < req_pck) + break; + } + + if (lck / pcd_min < req_pck) + break; + } + +found: + *lck_div = best_ld; + *pck_div = best_pd; +} + +int dispc_calc_clock_div(bool is_tft, unsigned long req_pck, + struct dispc_clock_info *cinfo) +{ + unsigned long prate; + struct dispc_clock_info cur, best; + int match = 0; + int min_fck_per_pck; + unsigned long fck_rate = dss_clk_get_rate(DSS_CLK_FCK1); + + if (cpu_is_omap34xx()) + prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); + else + prate = 0; + + if (req_pck == dispc.cache_req_pck && + ((cpu_is_omap34xx() && prate == dispc.cache_prate) || + dispc.cache_cinfo.fck == fck_rate)) { + DSSDBG("dispc clock info found from cache.\n"); + *cinfo = dispc.cache_cinfo; + return 0; + } + + min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK; + + if (min_fck_per_pck && + req_pck * min_fck_per_pck > DISPC_MAX_FCK) { + DSSERR("Requested pixel clock not possible with the current " + "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning " + "the constraint off.\n"); + min_fck_per_pck = 0; + } + +retry: + memset(&cur, 0, sizeof(cur)); + memset(&best, 0, sizeof(best)); + + if (cpu_is_omap24xx()) { + /* XXX can we change the clock on omap2? */ + cur.fck = dss_clk_get_rate(DSS_CLK_FCK1); + cur.fck_div = 1; + + match = 1; + + find_lck_pck_divs(is_tft, req_pck, cur.fck, + &cur.lck_div, &cur.pck_div); + + cur.lck = cur.fck / cur.lck_div; + cur.pck = cur.lck / cur.pck_div; + + best = cur; + + goto found; + } else if (cpu_is_omap34xx()) { + for (cur.fck_div = 16; cur.fck_div > 0; --cur.fck_div) { + cur.fck = prate / cur.fck_div * 2; + + if (cur.fck > DISPC_MAX_FCK) + continue; + + if (min_fck_per_pck && + cur.fck < req_pck * min_fck_per_pck) + continue; + + match = 1; + + find_lck_pck_divs(is_tft, req_pck, cur.fck, + &cur.lck_div, &cur.pck_div); + + cur.lck = cur.fck / cur.lck_div; + cur.pck = cur.lck / cur.pck_div; + + if (abs(cur.pck - req_pck) < abs(best.pck - req_pck)) { + best = cur; + + if (cur.pck == req_pck) + goto found; + } + } + } else { + BUG(); + } + +found: + if (!match) { + if (min_fck_per_pck) { + DSSERR("Could not find suitable clock settings.\n" + "Turning FCK/PCK constraint off and" + "trying again.\n"); + min_fck_per_pck = 0; + goto retry; + } + + DSSERR("Could not find suitable clock settings.\n"); + + return -EINVAL; + } + + if (cinfo) + *cinfo = best; + + dispc.cache_req_pck = req_pck; + dispc.cache_prate = prate; + dispc.cache_cinfo = best; + + return 0; +} + +int dispc_set_clock_div(struct dispc_clock_info *cinfo) +{ + unsigned long prate; + int r; + + if (cpu_is_omap34xx()) { + prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); + DSSDBG("dpll4_m4 = %ld\n", prate); + } + + DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div); + DSSDBG("lck = %ld (%d)\n", cinfo->lck, cinfo->lck_div); + DSSDBG("pck = %ld (%d)\n", cinfo->pck, cinfo->pck_div); + + if (cpu_is_omap34xx()) { + r = clk_set_rate(dispc.dpll4_m4_ck, prate / cinfo->fck_div); + if (r) + return r; + } + + dispc_set_lcd_divisor(cinfo->lck_div, cinfo->pck_div); + + return 0; +} + +int dispc_get_clock_div(struct dispc_clock_info *cinfo) +{ + cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK1); + + if (cpu_is_omap34xx()) { + unsigned long prate; + prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); + cinfo->fck_div = prate / (cinfo->fck / 2); + } else { + cinfo->fck_div = 0; + } + + cinfo->lck_div = REG_GET(DISPC_DIVISOR, 23, 16); + cinfo->pck_div = REG_GET(DISPC_DIVISOR, 7, 0); + + cinfo->lck = cinfo->fck / cinfo->lck_div; + cinfo->pck = cinfo->lck / cinfo->pck_div; + + return 0; +} + +/* dispc.irq_lock has to be locked by the caller */ +static void _omap_dispc_set_irqs(void) +{ + u32 mask; + u32 old_mask; + int i; + struct omap_dispc_isr_data *isr_data; + + mask = dispc.irq_error_mask; + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = &dispc.registered_isr[i]; + + if (isr_data->isr == NULL) + continue; + + mask |= isr_data->mask; + } + + enable_clocks(1); + + old_mask = dispc_read_reg(DISPC_IRQENABLE); + /* clear the irqstatus for newly enabled irqs */ + dispc_write_reg(DISPC_IRQSTATUS, (mask ^ old_mask) & mask); + + dispc_write_reg(DISPC_IRQENABLE, mask); + + enable_clocks(0); +} + +int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask) +{ + int i; + int ret; + unsigned long flags; + struct omap_dispc_isr_data *isr_data; + + if (isr == NULL) + return -EINVAL; + + spin_lock_irqsave(&dispc.irq_lock, flags); + + /* check for duplicate entry */ + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = &dispc.registered_isr[i]; + if (isr_data->isr == isr && isr_data->arg == arg && + isr_data->mask == mask) { + ret = -EINVAL; + goto err; + } + } + + isr_data = NULL; + ret = -EBUSY; + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = &dispc.registered_isr[i]; + + if (isr_data->isr != NULL) + continue; + + isr_data->isr = isr; + isr_data->arg = arg; + isr_data->mask = mask; + ret = 0; + + break; + } + + _omap_dispc_set_irqs(); + + spin_unlock_irqrestore(&dispc.irq_lock, flags); + + return 0; +err: + spin_unlock_irqrestore(&dispc.irq_lock, flags); + + return ret; +} +EXPORT_SYMBOL(omap_dispc_register_isr); + +int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask) +{ + int i; + unsigned long flags; + int ret = -EINVAL; + struct omap_dispc_isr_data *isr_data; + + spin_lock_irqsave(&dispc.irq_lock, flags); + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = &dispc.registered_isr[i]; + if (isr_data->isr != isr || isr_data->arg != arg || + isr_data->mask != mask) + continue; + + /* found the correct isr */ + + isr_data->isr = NULL; + isr_data->arg = NULL; + isr_data->mask = 0; + + ret = 0; + break; + } + + if (ret == 0) + _omap_dispc_set_irqs(); + + spin_unlock_irqrestore(&dispc.irq_lock, flags); + + return ret; +} +EXPORT_SYMBOL(omap_dispc_unregister_isr); + +#ifdef DEBUG +static void print_irq_status(u32 status) +{ + if ((status & dispc.irq_error_mask) == 0) + return; + + printk(KERN_DEBUG "DISPC IRQ: 0x%x: ", status); + +#define PIS(x) \ + if (status & DISPC_IRQ_##x) \ + printk(#x " "); + PIS(GFX_FIFO_UNDERFLOW); + PIS(OCP_ERR); + PIS(VID1_FIFO_UNDERFLOW); + PIS(VID2_FIFO_UNDERFLOW); + PIS(SYNC_LOST); + PIS(SYNC_LOST_DIGIT); +#undef PIS + + printk("\n"); +} +#endif + +/* Called from dss.c. Note that we don't touch clocks here, + * but we presume they are on because we got an IRQ. However, + * an irq handler may turn the clocks off, so we may not have + * clock later in the function. */ +void dispc_irq_handler(void) +{ + int i; + u32 irqstatus; + u32 handledirqs = 0; + u32 unhandled_errors; + struct omap_dispc_isr_data *isr_data; + struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; + + spin_lock(&dispc.irq_lock); + + irqstatus = dispc_read_reg(DISPC_IRQSTATUS); + +#ifdef DEBUG + if (dss_debug) + print_irq_status(irqstatus); +#endif + /* Ack the interrupt. Do it here before clocks are possibly turned + * off */ + dispc_write_reg(DISPC_IRQSTATUS, irqstatus); + + /* make a copy and unlock, so that isrs can unregister + * themselves */ + memcpy(registered_isr, dispc.registered_isr, + sizeof(registered_isr)); + + spin_unlock(&dispc.irq_lock); + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = ®istered_isr[i]; + + if (!isr_data->isr) + continue; + + if (isr_data->mask & irqstatus) { + isr_data->isr(isr_data->arg, irqstatus); + handledirqs |= isr_data->mask; + } + } + + spin_lock(&dispc.irq_lock); + + unhandled_errors = irqstatus & ~handledirqs & dispc.irq_error_mask; + + if (unhandled_errors) { + dispc.error_irqs |= unhandled_errors; + + dispc.irq_error_mask &= ~unhandled_errors; + _omap_dispc_set_irqs(); + + schedule_work(&dispc.error_work); + } + + spin_unlock(&dispc.irq_lock); +} + +static void dispc_error_worker(struct work_struct *work) +{ + int i; + u32 errors; + unsigned long flags; + + spin_lock_irqsave(&dispc.irq_lock, flags); + errors = dispc.error_irqs; + dispc.error_irqs = 0; + spin_unlock_irqrestore(&dispc.irq_lock, flags); + + if (errors & DISPC_IRQ_GFX_FIFO_UNDERFLOW) { + DSSERR("GFX_FIFO_UNDERFLOW, disabling GFX\n"); + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id == 0) { + dispc_enable_plane(ovl->id, 0); + dispc_go(ovl->manager->id); + mdelay(50); + break; + } + } + } + + if (errors & DISPC_IRQ_VID1_FIFO_UNDERFLOW) { + DSSERR("VID1_FIFO_UNDERFLOW, disabling VID1\n"); + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id == 1) { + dispc_enable_plane(ovl->id, 0); + dispc_go(ovl->manager->id); + mdelay(50); + break; + } + } + } + + if (errors & DISPC_IRQ_VID2_FIFO_UNDERFLOW) { + DSSERR("VID2_FIFO_UNDERFLOW, disabling VID2\n"); + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id == 2) { + dispc_enable_plane(ovl->id, 0); + dispc_go(ovl->manager->id); + mdelay(50); + break; + } + } + } + + if (errors & DISPC_IRQ_SYNC_LOST) { + struct omap_overlay_manager *manager = NULL; + bool enable = false; + + DSSERR("SYNC_LOST, disabling LCD\n"); + + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + struct omap_overlay_manager *mgr; + mgr = omap_dss_get_overlay_manager(i); + + if (mgr->id == OMAP_DSS_CHANNEL_LCD) { + manager = mgr; + enable = mgr->device->state == + OMAP_DSS_DISPLAY_ACTIVE; + mgr->device->disable(mgr->device); + break; + } + } + + if (manager) { + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id != 0 && ovl->manager == manager) + dispc_enable_plane(ovl->id, 0); + } + + dispc_go(manager->id); + mdelay(50); + if (enable) + manager->device->enable(manager->device); + } + } + + if (errors & DISPC_IRQ_SYNC_LOST_DIGIT) { + struct omap_overlay_manager *manager = NULL; + bool enable = false; + + DSSERR("SYNC_LOST_DIGIT, disabling TV\n"); + + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + struct omap_overlay_manager *mgr; + mgr = omap_dss_get_overlay_manager(i); + + if (mgr->id == OMAP_DSS_CHANNEL_DIGIT) { + manager = mgr; + enable = mgr->device->state == + OMAP_DSS_DISPLAY_ACTIVE; + mgr->device->disable(mgr->device); + break; + } + } + + if (manager) { + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id != 0 && ovl->manager == manager) + dispc_enable_plane(ovl->id, 0); + } + + dispc_go(manager->id); + mdelay(50); + if (enable) + manager->device->enable(manager->device); + } + } + + if (errors & DISPC_IRQ_OCP_ERR) { + DSSERR("OCP_ERR\n"); + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + struct omap_overlay_manager *mgr; + mgr = omap_dss_get_overlay_manager(i); + + if (mgr->caps & OMAP_DSS_OVL_CAP_DISPC) + mgr->device->disable(mgr->device); + } + } + + spin_lock_irqsave(&dispc.irq_lock, flags); + dispc.irq_error_mask |= errors; + _omap_dispc_set_irqs(); + spin_unlock_irqrestore(&dispc.irq_lock, flags); +} + +int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout) +{ + void dispc_irq_wait_handler(void *data, u32 mask) + { + complete((struct completion *)data); + } + + int r; + DECLARE_COMPLETION_ONSTACK(completion); + + r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion, + irqmask); + + if (r) + return r; + + timeout = wait_for_completion_timeout(&completion, timeout); + + omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask); + + if (timeout == 0) + return -ETIMEDOUT; + + if (timeout == -ERESTARTSYS) + return -ERESTARTSYS; + + return 0; +} + +int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, + unsigned long timeout) +{ + void dispc_irq_wait_handler(void *data, u32 mask) + { + complete((struct completion *)data); + } + + int r; + DECLARE_COMPLETION_ONSTACK(completion); + + r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion, + irqmask); + + if (r) + return r; + + timeout = wait_for_completion_interruptible_timeout(&completion, + timeout); + + omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask); + + if (timeout == 0) + return -ETIMEDOUT; + + if (timeout == -ERESTARTSYS) + return -ERESTARTSYS; + + return 0; +} + +#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC +void dispc_fake_vsync_irq(void) +{ + u32 irqstatus = DISPC_IRQ_VSYNC; + int i; + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + struct omap_dispc_isr_data *isr_data; + isr_data = &dispc.registered_isr[i]; + + if (!isr_data->isr) + continue; + + if (isr_data->mask & irqstatus) + isr_data->isr(isr_data->arg, irqstatus); + } +} +#endif + +static void _omap_dispc_initialize_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&dispc.irq_lock, flags); + + memset(dispc.registered_isr, 0, sizeof(dispc.registered_isr)); + + dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; + + /* there's SYNC_LOST_DIGIT waiting after enabling the DSS, + * so clear it */ + dispc_write_reg(DISPC_IRQSTATUS, dispc_read_reg(DISPC_IRQSTATUS)); + + _omap_dispc_set_irqs(); + + spin_unlock_irqrestore(&dispc.irq_lock, flags); +} + +void dispc_enable_sidle(void) +{ + REG_FLD_MOD(DISPC_SYSCONFIG, 2, 4, 3); /* SIDLEMODE: smart idle */ +} + +void dispc_disable_sidle(void) +{ + REG_FLD_MOD(DISPC_SYSCONFIG, 1, 4, 3); /* SIDLEMODE: no idle */ +} + +static void _omap_dispc_initial_config(void) +{ + u32 l; + + l = dispc_read_reg(DISPC_SYSCONFIG); + l = FLD_MOD(l, 2, 13, 12); /* MIDLEMODE: smart standby */ + l = FLD_MOD(l, 2, 4, 3); /* SIDLEMODE: smart idle */ + l = FLD_MOD(l, 1, 2, 2); /* ENWAKEUP */ + l = FLD_MOD(l, 1, 0, 0); /* AUTOIDLE */ + dispc_write_reg(DISPC_SYSCONFIG, l); + + /* FUNCGATED */ + REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); + + /* L3 firewall setting: enable access to OCM RAM */ + if (cpu_is_omap24xx()) + __raw_writel(0x402000b0, IO_ADDRESS(0x680050a0)); + + _dispc_setup_color_conv_coef(); + + dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY); + + dispc_read_plane_fifo_sizes(); +} + +int dispc_init(void) +{ + u32 rev; + + spin_lock_init(&dispc.irq_lock); + + INIT_WORK(&dispc.error_work, dispc_error_worker); + + dispc.base = ioremap(DISPC_BASE, DISPC_SZ_REGS); + if (!dispc.base) { + DSSERR("can't ioremap DISPC\n"); + return -ENOMEM; + } + + if (cpu_is_omap34xx()) { + dispc.dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck"); + if (IS_ERR(dispc.dpll4_m4_ck)) { + DSSERR("Failed to get dpll4_m4_ck\n"); + return -ENODEV; + } + } + + enable_clocks(1); + + _omap_dispc_initial_config(); + + _omap_dispc_initialize_irq(); + + dispc_save_context(); + + rev = dispc_read_reg(DISPC_REVISION); + printk(KERN_INFO "OMAP DISPC rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + enable_clocks(0); + + return 0; +} + +void dispc_exit(void) +{ + if (cpu_is_omap34xx()) + clk_put(dispc.dpll4_m4_ck); + iounmap(dispc.base); +} + +int dispc_enable_plane(enum omap_plane plane, bool enable) +{ + DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); + + enable_clocks(1); + _dispc_enable_plane(plane, enable); + enable_clocks(0); + + return 0; +} + +int dispc_setup_plane(enum omap_plane plane, + u32 paddr, u16 screen_width, + u16 pos_x, u16 pos_y, + u16 width, u16 height, + u16 out_width, u16 out_height, + enum omap_color_mode color_mode, + bool ilace, + enum omap_dss_rotation_type rotation_type, + u8 rotation, bool mirror, u8 global_alpha) +{ + int r = 0; + + DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> " + "%dx%d, ilace %d, cmode %x, rot %d, mir %d\n", + plane, paddr, screen_width, pos_x, pos_y, + width, height, + out_width, out_height, + ilace, color_mode, + rotation, mirror); + + enable_clocks(1); + + r = _dispc_setup_plane(plane, + paddr, screen_width, + pos_x, pos_y, + width, height, + out_width, out_height, + color_mode, ilace, + rotation_type, + rotation, mirror, + global_alpha); + + enable_clocks(0); + + return r; +}
diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
new file mode 100644
index 0000000..6b5d0cf
--- /dev/null
+++ b/drivers/video/omap2/dss/display.c@@ -0,0 +1,658 @@ +/* + * linux/drivers/video/omap2/dss/display.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DISPLAY" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/jiffies.h> +#include <linux/list.h> +#include <linux/platform_device.h> + +#include <mach/display.h> +#include "dss.h" + +static LIST_HEAD(display_list); + +static ssize_t display_enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + bool enabled = dssdev->state != OMAP_DSS_DISPLAY_DISABLED; + + return snprintf(buf, PAGE_SIZE, "%d\n", enabled); +} + +static ssize_t display_enabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + bool enabled, r; + + enabled = simple_strtoul(buf, NULL, 10); + + if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) { + if (enabled) { + r = dssdev->enable(dssdev); + if (r) + return r; + } else { + dssdev->disable(dssdev); + } + } + + return size; +} + +static ssize_t display_upd_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + enum omap_dss_update_mode mode = OMAP_DSS_UPDATE_AUTO; + if (dssdev->get_update_mode) + mode = dssdev->get_update_mode(dssdev); + return snprintf(buf, PAGE_SIZE, "%d\n", mode); +} + +static ssize_t display_upd_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + int val, r; + enum omap_dss_update_mode mode; + + val = simple_strtoul(buf, NULL, 10); + + switch (val) { + case OMAP_DSS_UPDATE_DISABLED: + case OMAP_DSS_UPDATE_AUTO: + case OMAP_DSS_UPDATE_MANUAL: + mode = (enum omap_dss_update_mode)val; + break; + default: + return -EINVAL; + } + + r = dssdev->set_update_mode(dssdev, mode); + if (r) + return r; + + return size; +} + +static ssize_t display_tear_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", + dssdev->get_te ? dssdev->get_te(dssdev) : 0); +} + +static ssize_t display_tear_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned long te; + int r; + + if (!dssdev->enable_te || !dssdev->get_te) + return -ENOENT; + + te = simple_strtoul(buf, NULL, 0); + + r = dssdev->enable_te(dssdev, te); + if (r) + return r; + + return size; +} + +static ssize_t display_timings_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_video_timings t; + + if (!dssdev->get_timings) + return -ENOENT; + + dssdev->get_timings(dssdev, &t); + + return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n", + t.pixel_clock, + t.x_res, t.hfp, t.hbp, t.hsw, + t.y_res, t.vfp, t.vbp, t.vsw); +} + +static ssize_t display_timings_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_video_timings t; + int r, found; + + if (!dssdev->set_timings || !dssdev->check_timings) + return -ENOENT; + + found = 0; +#ifdef CONFIG_OMAP2_DSS_VENC + if (strncmp("pal", buf, 3) == 0) { + t = omap_dss_pal_timings; + found = 1; + } else if (strncmp("ntsc", buf, 4) == 0) { + t = omap_dss_ntsc_timings; + found = 1; + } +#endif + if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu", + &t.pixel_clock, + &t.x_res, &t.hfp, &t.hbp, &t.hsw, + &t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9) + return -EINVAL; + + r = dssdev->check_timings(dssdev, &t); + if (r) + return r; + + dssdev->set_timings(dssdev, &t); + + return size; +} + +static ssize_t display_rotate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + int rotate; + if (!dssdev->get_rotate) + return -ENOENT; + rotate = dssdev->get_rotate(dssdev); + return snprintf(buf, PAGE_SIZE, "%u\n", rotate); +} + +static ssize_t display_rotate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned long rot; + int r; + + if (!dssdev->set_rotate || !dssdev->get_rotate) + return -ENOENT; + + rot = simple_strtoul(buf, NULL, 0); + + r = dssdev->set_rotate(dssdev, rot); + if (r) + return r; + + return size; +} + +static ssize_t display_mirror_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + int mirror; + if (!dssdev->get_mirror) + return -ENOENT; + mirror = dssdev->get_mirror(dssdev); + return snprintf(buf, PAGE_SIZE, "%u\n", mirror); +} + +static ssize_t display_mirror_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned long mirror; + int r; + + if (!dssdev->set_mirror || !dssdev->get_mirror) + return -ENOENT; + + mirror = simple_strtoul(buf, NULL, 0); + + r = dssdev->set_mirror(dssdev, mirror); + if (r) + return r; + + return size; +} + +static ssize_t display_wss_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned int wss; + + if (!dssdev->get_wss) + return -ENOENT; + + wss = dssdev->get_wss(dssdev); + + return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss); +} + +static ssize_t display_wss_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned long wss; + int r; + + if (!dssdev->get_wss || !dssdev->set_wss) + return -ENOENT; + + if (strict_strtoul(buf, 0, &wss)) + return -EINVAL; + + if (wss > 0xfffff) + return -EINVAL; + + r = dssdev->set_wss(dssdev, wss); + if (r) + return r; + + return size; +} + +static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR, + display_enabled_show, display_enabled_store); +static DEVICE_ATTR(update_mode, S_IRUGO|S_IWUSR, + display_upd_mode_show, display_upd_mode_store); +static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR, + display_tear_show, display_tear_store); +static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR, + display_timings_show, display_timings_store); +static DEVICE_ATTR(rotate, S_IRUGO|S_IWUSR, + display_rotate_show, display_rotate_store); +static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR, + display_mirror_show, display_mirror_store); +static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR, + display_wss_show, display_wss_store); + +static struct device_attribute *display_sysfs_attrs[] = { + &dev_attr_enabled, + &dev_attr_update_mode, + &dev_attr_tear_elim, + &dev_attr_timings, + &dev_attr_rotate, + &dev_attr_mirror, + &dev_attr_wss, + NULL +}; + +static void default_get_resolution(struct omap_dss_device *dssdev, + u16 *xres, u16 *yres) +{ + *xres = dssdev->panel.timings.x_res; + *yres = dssdev->panel.timings.y_res; +} + +void default_get_overlay_fifo_thresholds(enum omap_plane plane, + u32 fifo_size, enum omap_burst_size *burst_size, + u32 *fifo_low, u32 *fifo_high) +{ + unsigned burst_size_bytes; + + *burst_size = OMAP_DSS_BURST_16x32; + burst_size_bytes = 16 * 32 / 8; + + *fifo_high = fifo_size - 1; + *fifo_low = fifo_size - burst_size_bytes; +} + +static int default_wait_vsync(struct omap_dss_device *dssdev) +{ + unsigned long timeout = msecs_to_jiffies(500); + u32 irq; + + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) + irq = DISPC_IRQ_EVSYNC_ODD; + else + irq = DISPC_IRQ_VSYNC; + + return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); +} + +static int default_get_recommended_bpp(struct omap_dss_device *dssdev) +{ + if (dssdev->panel.recommended_bpp) + return dssdev->panel.recommended_bpp; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: + if (dssdev->phy.dpi.data_lines == 24) + return 24; + else + return 16; + + case OMAP_DISPLAY_TYPE_DBI: + case OMAP_DISPLAY_TYPE_DSI: + if (dssdev->ctrl.pixel_size == 24) + return 24; + else + return 16; + case OMAP_DISPLAY_TYPE_VENC: + case OMAP_DISPLAY_TYPE_SDI: + return 24; + return 24; + default: + BUG(); + } +} + +/* Checks if replication logic should be used. Only use for active matrix, + * when overlay is in RGB12U or RGB16 mode, and LCD interface is + * 18bpp or 24bpp */ +bool dss_use_replication(struct omap_dss_device *dssdev, + enum omap_color_mode mode) +{ + int bpp; + + if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16) + return false; + + if (dssdev->type == OMAP_DISPLAY_TYPE_DPI && + (dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0) + return false; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: + bpp = dssdev->phy.dpi.data_lines; + break; + case OMAP_DISPLAY_TYPE_VENC: + case OMAP_DISPLAY_TYPE_SDI: + bpp = 24; + break; + case OMAP_DISPLAY_TYPE_DBI: + case OMAP_DISPLAY_TYPE_DSI: + bpp = dssdev->ctrl.pixel_size; + break; + default: + BUG(); + } + + return bpp > 16; +} + +void dss_init_device(struct platform_device *pdev, + struct omap_dss_device *dssdev) +{ + struct device_attribute *attr; + int i; + int r; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: +#ifdef CONFIG_OMAP2_DSS_RFBI + case OMAP_DISPLAY_TYPE_DBI: +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + case OMAP_DISPLAY_TYPE_SDI: +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + case OMAP_DISPLAY_TYPE_DSI: +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + case OMAP_DISPLAY_TYPE_VENC: +#endif + break; + default: + DSSERR("Support for display '%s' not compiled in.\n", + dssdev->name); + return; + } + + dssdev->get_resolution = default_get_resolution; + dssdev->get_recommended_bpp = default_get_recommended_bpp; + dssdev->wait_vsync = default_wait_vsync; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: + r = dpi_init_display(dssdev); + break; +#ifdef CONFIG_OMAP2_DSS_RFBI + case OMAP_DISPLAY_TYPE_DBI: + r = rfbi_init_display(dssdev); + break; +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + case OMAP_DISPLAY_TYPE_VENC: + r = venc_init_display(dssdev); + break; +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + case OMAP_DISPLAY_TYPE_SDI: + r = sdi_init_display(dssdev); + break; +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + case OMAP_DISPLAY_TYPE_DSI: + r = dsi_init_display(dssdev); + break; +#endif + default: + BUG(); + } + + if (r) { + DSSERR("failed to init display %s\n", dssdev->name); + return; + } + + /* create device sysfs files */ + i = 0; + while ((attr = display_sysfs_attrs[i++]) != NULL) { + r = device_create_file(&dssdev->dev, attr); + if (r) + DSSERR("failed to create sysfs file\n"); + } + + /* create display? sysfs links */ + r = sysfs_create_link(&pdev->dev.kobj, &dssdev->dev.kobj, + dev_name(&dssdev->dev)); + if (r) + DSSERR("failed to create sysfs display link\n"); +} + +void dss_uninit_device(struct platform_device *pdev, + struct omap_dss_device *dssdev) +{ + struct device_attribute *attr; + int i = 0; + + sysfs_remove_link(&pdev->dev.kobj, dev_name(&dssdev->dev)); + + while ((attr = display_sysfs_attrs[i++]) != NULL) + device_remove_file(&dssdev->dev, attr); + + if (dssdev->manager) + dssdev->manager->unset_device(dssdev->manager); +} + +static int dss_suspend_device(struct device *dev, void *data) +{ + int r; + struct omap_dss_device *dssdev = to_dss_device(dev); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + dssdev->activate_after_resume = false; + return 0; + } + + if (!dssdev->suspend) { + DSSERR("display '%s' doesn't implement suspend\n", + dssdev->name); + return -ENOSYS; + } + + r = dssdev->suspend(dssdev); + if (r) + return r; + + dssdev->activate_after_resume = true; + + return 0; +} + +int dss_suspend_all_devices(void) +{ + int r; + struct bus_type *bus = dss_get_bus(); + + r = bus_for_each_dev(bus, NULL, NULL, dss_suspend_device); + if (r) { + /* resume all displays that were suspended */ + dss_resume_all_devices(); + return r; + } + + return 0; +} + +static int dss_resume_device(struct device *dev, void *data) +{ + int r; + struct omap_dss_device *dssdev = to_dss_device(dev); + + if (dssdev->activate_after_resume && dssdev->resume) { + r = dssdev->resume(dssdev); + if (r) + return r; + } + + dssdev->activate_after_resume = false; + + return 0; +} + +int dss_resume_all_devices(void) +{ + struct bus_type *bus = dss_get_bus(); + + return bus_for_each_dev(bus, NULL, NULL, dss_resume_device); +} + + +void omap_dss_get_device(struct omap_dss_device *dssdev) +{ + get_device(&dssdev->dev); +} +EXPORT_SYMBOL(omap_dss_get_device); + +void omap_dss_put_device(struct omap_dss_device *dssdev) +{ + put_device(&dssdev->dev); +} +EXPORT_SYMBOL(omap_dss_put_device); + +/* ref count of the found device is incremented. ref count + * of from-device is decremented. */ +struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from) +{ + struct device *dev; + struct device *dev_start = NULL; + struct omap_dss_device *dssdev = NULL; + + int match(struct device *dev, void *data) + { + /* skip panels connected to controllers */ + if (to_dss_device(dev)->panel.ctrl) + return 0; + + return 1; + } + + if (from) + dev_start = &from->dev; + dev = bus_find_device(dss_get_bus(), dev_start, NULL, match); + if (dev) + dssdev = to_dss_device(dev); + if (from) + put_device(&from->dev); + + return dssdev; +} +EXPORT_SYMBOL(omap_dss_get_next_device); + +struct omap_dss_device *omap_dss_find_device(void *data, + int (*match)(struct omap_dss_device *dssdev, void *data)) +{ + struct omap_dss_device *dssdev = NULL; + + while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) { + if (match(dssdev, data)) + return dssdev; + } + + return NULL; +} +EXPORT_SYMBOL(omap_dss_find_device); + +int omap_dss_start_device(struct omap_dss_device *dssdev) +{ + int r; + + if (!dssdev->driver) { + DSSDBG("no driver\n"); + r = -ENODEV; + goto err0; + } + + if (dssdev->ctrl.panel && !dssdev->ctrl.panel->driver) { + DSSDBG("no panel driver\n"); + r = -ENODEV; + goto err0; + } + + if (!try_module_get(dssdev->dev.driver->owner)) { + r = -ENODEV; + goto err0; + } + + if (dssdev->ctrl.panel) { + if (!try_module_get(dssdev->ctrl.panel->dev.driver->owner)) { + r = -ENODEV; + goto err1; + } + } + + return 0; +err1: + module_put(dssdev->dev.driver->owner); +err0: + return r; +} +EXPORT_SYMBOL(omap_dss_start_device); + +void omap_dss_stop_device(struct omap_dss_device *dssdev) +{ + if (dssdev->ctrl.panel) + module_put(dssdev->ctrl.panel->dev.driver->owner); + + module_put(dssdev->dev.driver->owner); +} +EXPORT_SYMBOL(omap_dss_stop_device); +
diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
new file mode 100644
index 0000000..b6543c8
--- /dev/null
+++ b/drivers/video/omap2/dss/dpi.c@@ -0,0 +1,388 @@ +/* + * linux/drivers/video/omap2/dss/dpi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DPI" + +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/errno.h> + +#include <mach/board.h> +#include <mach/display.h> +#include <mach/cpu.h> + +#include "dss.h" + +static struct { + int update_enabled; +} dpi; + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL +static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req, + unsigned long *fck, int *lck_div, int *pck_div) +{ + struct dsi_clock_info cinfo; + int r; + + r = dsi_pll_calc_pck(is_tft, pck_req, &cinfo); + if (r) + return r; + + r = dsi_pll_program(&cinfo); + if (r) + return r; + + dss_select_clk_source(0, 1); + + dispc_set_lcd_divisor(cinfo.lck_div, cinfo.pck_div); + + *fck = cinfo.dsi1_pll_fclk; + *lck_div = cinfo.lck_div; + *pck_div = cinfo.pck_div; + + return 0; +} +#else +static int dpi_set_dispc_clk(bool is_tft, unsigned long pck_req, + unsigned long *fck, int *lck_div, int *pck_div) +{ + struct dispc_clock_info cinfo; + int r; + + r = dispc_calc_clock_div(is_tft, pck_req, &cinfo); + if (r) + return r; + + r = dispc_set_clock_div(&cinfo); + if (r) + return r; + + *fck = cinfo.fck; + *lck_div = cinfo.lck_div; + *pck_div = cinfo.pck_div; + + return 0; +} +#endif + +static int dpi_set_mode(struct omap_dss_device *dssdev) +{ + struct omap_video_timings *t = &dssdev->panel.timings; + int lck_div, pck_div; + unsigned long fck; + unsigned long pck; + bool is_tft; + int r = 0; + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi, + dssdev->panel.acb); + + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + r = dpi_set_dsi_clk(is_tft, t->pixel_clock * 1000, + &fck, &lck_div, &pck_div); +#else + r = dpi_set_dispc_clk(is_tft, t->pixel_clock * 1000, + &fck, &lck_div, &pck_div); +#endif + if (r) + goto err0; + + pck = fck / lck_div / pck_div / 1000; + + if (pck != t->pixel_clock) { + DSSWARN("Could not find exact pixel clock. " + "Requested %d kHz, got %lu kHz\n", + t->pixel_clock, pck); + + t->pixel_clock = pck; + } + + dispc_set_lcd_timings(t); + +err0: + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + return r; +} + +static int dpi_basic_init(struct omap_dss_device *dssdev) +{ + bool is_tft; + + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; + + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS); + dispc_set_lcd_display_type(is_tft ? OMAP_DSS_LCD_DISPLAY_TFT : + OMAP_DSS_LCD_DISPLAY_STN); + dispc_set_tft_data_lines(dssdev->phy.dpi.data_lines); + + return 0; +} + +static int dpi_display_enable(struct omap_dss_device *dssdev) +{ + int r; + + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err0; + } + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + DSSERR("display already enabled\n"); + r = -EINVAL; + goto err1; + } + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + r = dpi_basic_init(dssdev); + if (r) + goto err2; + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + dss_clk_enable(DSS_CLK_FCK2); + r = dsi_pll_init(0, 1); + if (r) + goto err3; +#endif + r = dpi_set_mode(dssdev); + if (r) + goto err4; + + mdelay(2); + + dispc_enable_lcd_out(1); + + r = dssdev->driver->enable(dssdev); + if (r) + goto err5; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; + +err5: + dispc_enable_lcd_out(0); +err4: +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + dsi_pll_uninit(); +err3: + dss_clk_disable(DSS_CLK_FCK2); +#endif +err2: + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +err1: + omap_dss_stop_device(dssdev); +err0: + return r; +} + +static int dpi_display_resume(struct omap_dss_device *dssdev); + +static void dpi_display_disable(struct omap_dss_device *dssdev) +{ + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) + return; + + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) + dpi_display_resume(dssdev); + + dssdev->driver->disable(dssdev); + + dispc_enable_lcd_out(0); + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + dss_select_clk_source(0, 0); + dsi_pll_uninit(); + dss_clk_disable(DSS_CLK_FCK2); +#endif + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + omap_dss_stop_device(dssdev); +} + +static int dpi_display_suspend(struct omap_dss_device *dssdev) +{ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return -EINVAL; + + DSSDBG("dpi_display_suspend\n"); + + if (dssdev->driver->suspend) + dssdev->driver->suspend(dssdev); + + dispc_enable_lcd_out(0); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + return 0; +} + +static int dpi_display_resume(struct omap_dss_device *dssdev) +{ + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) + return -EINVAL; + + DSSDBG("dpi_display_resume\n"); + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dispc_enable_lcd_out(1); + + if (dssdev->driver->resume) + dssdev->driver->resume(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void dpi_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + DSSDBG("dpi_set_timings\n"); + dssdev->panel.timings = *timings; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + dpi_set_mode(dssdev); + dispc_go(OMAP_DSS_CHANNEL_LCD); + } +} + +static int dpi_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + bool is_tft; + int r; + int lck_div, pck_div; + unsigned long fck; + unsigned long pck; + + if (!dispc_lcd_timings_ok(timings)) + return -EINVAL; + + if (timings->pixel_clock == 0) + return -EINVAL; + + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + { + struct dsi_clock_info cinfo; + r = dsi_pll_calc_pck(is_tft, timings->pixel_clock * 1000, + &cinfo); + + if (r) + return r; + + fck = cinfo.dsi1_pll_fclk; + lck_div = cinfo.lck_div; + pck_div = cinfo.pck_div; + } +#else + { + struct dispc_clock_info cinfo; + r = dispc_calc_clock_div(is_tft, timings->pixel_clock * 1000, + &cinfo); + + if (r) + return r; + + fck = cinfo.fck; + lck_div = cinfo.lck_div; + pck_div = cinfo.pck_div; + } +#endif + + pck = fck / lck_div / pck_div / 1000; + + timings->pixel_clock = pck; + + return 0; +} + +static void dpi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static int dpi_display_set_update_mode(struct omap_dss_device *dssdev, + enum omap_dss_update_mode mode) +{ + if (mode == OMAP_DSS_UPDATE_MANUAL) + return -EINVAL; + + if (mode == OMAP_DSS_UPDATE_DISABLED) { + dispc_enable_lcd_out(0); + dpi.update_enabled = 0; + } else { + dispc_enable_lcd_out(1); + dpi.update_enabled = 1; + } + + return 0; +} + +static enum omap_dss_update_mode dpi_display_get_update_mode( + struct omap_dss_device *dssdev) +{ + return dpi.update_enabled ? OMAP_DSS_UPDATE_AUTO : + OMAP_DSS_UPDATE_DISABLED; +} + +int dpi_init_display(struct omap_dss_device *dssdev) +{ + DSSDBG("init_display\n"); + + dssdev->enable = dpi_display_enable; + dssdev->disable = dpi_display_disable; + dssdev->suspend = dpi_display_suspend; + dssdev->resume = dpi_display_resume; + dssdev->set_timings = dpi_set_timings; + dssdev->check_timings = dpi_check_timings; + dssdev->get_timings = dpi_get_timings; + dssdev->set_update_mode = dpi_display_set_update_mode; + dssdev->get_update_mode = dpi_display_get_update_mode; + + return 0; +} + +int dpi_init(void) +{ + return 0; +} + +void dpi_exit(void) +{ +} +
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
new file mode 100644
index 0000000..aab9758
--- /dev/null
+++ b/drivers/video/omap2/dss/dss.c@@ -0,0 +1,347 @@ +/* + * linux/drivers/video/omap2/dss/dss.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DSS" + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/seq_file.h> + +#include <mach/display.h> +#include "dss.h" + +#define DSS_BASE 0x48050000 + +#define DSS_SZ_REGS SZ_512 + +struct dss_reg { + u16 idx; +}; + +#define DSS_REG(idx) ((const struct dss_reg) { idx }) + +#define DSS_REVISION DSS_REG(0x0000) +#define DSS_SYSCONFIG DSS_REG(0x0010) +#define DSS_SYSSTATUS DSS_REG(0x0014) +#define DSS_IRQSTATUS DSS_REG(0x0018) +#define DSS_CONTROL DSS_REG(0x0040) +#define DSS_SDI_CONTROL DSS_REG(0x0044) +#define DSS_PLL_CONTROL DSS_REG(0x0048) +#define DSS_SDI_STATUS DSS_REG(0x005C) + +#define REG_GET(idx, start, end) \ + FLD_GET(dss_read_reg(idx), start, end) + +#define REG_FLD_MOD(idx, val, start, end) \ + dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end)) + +static struct { + void __iomem *base; + + u32 ctx[DSS_SZ_REGS / sizeof(u32)]; +} dss; + +static int _omap_dss_wait_reset(void); + +static inline void dss_write_reg(const struct dss_reg idx, u32 val) +{ + __raw_writel(val, dss.base + idx.idx); +} + +static inline u32 dss_read_reg(const struct dss_reg idx) +{ + return __raw_readl(dss.base + idx.idx); +} + +#define SR(reg) \ + dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg) +#define RR(reg) \ + dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)]) + +void dss_save_context(void) +{ + if (cpu_is_omap24xx()) + return; + + SR(SYSCONFIG); + SR(CONTROL); + +#ifdef CONFIG_OMAP2_DSS_SDI + SR(SDI_CONTROL); + SR(PLL_CONTROL); +#endif +} + +void dss_restore_context(void) +{ + if (_omap_dss_wait_reset()) + DSSERR("DSS not coming out of reset after sleep\n"); + + RR(SYSCONFIG); + RR(CONTROL); + +#ifdef CONFIG_OMAP2_DSS_SDI + RR(SDI_CONTROL); + RR(PLL_CONTROL); +#endif +} + +#undef SR +#undef RR + +void dss_sdi_init(u8 datapairs) +{ + u32 l; + + BUG_ON(datapairs > 3 || datapairs < 1); + + l = dss_read_reg(DSS_SDI_CONTROL); + l = FLD_MOD(l, 0xf, 19, 15); /* SDI_PDIV */ + l = FLD_MOD(l, datapairs-1, 3, 2); /* SDI_PRSEL */ + l = FLD_MOD(l, 2, 1, 0); /* SDI_BWSEL */ + dss_write_reg(DSS_SDI_CONTROL, l); + + l = dss_read_reg(DSS_PLL_CONTROL); + l = FLD_MOD(l, 0x7, 25, 22); /* SDI_PLL_FREQSEL */ + l = FLD_MOD(l, 0xb, 16, 11); /* SDI_PLL_REGN */ + l = FLD_MOD(l, 0xb4, 10, 1); /* SDI_PLL_REGM */ + dss_write_reg(DSS_PLL_CONTROL, l); +} + +void dss_sdi_enable(void) +{ + dispc_pck_free_enable(1); + + /* Reset SDI PLL */ + REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */ + udelay(1); /* wait 2x PCLK */ + + /* Lock SDI PLL */ + REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */ + + /* Waiting for PLL lock request to complete */ + while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) + ; + + /* Clearing PLL_GO bit */ + REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28); + + /* Waiting for PLL to lock */ + while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) + ; + + dispc_lcd_enable_signal(1); + + /* Waiting for SDI reset to complete */ + while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) + ; +} + +void dss_sdi_disable(void) +{ + dispc_lcd_enable_signal(0); + + dispc_pck_free_enable(0); + + /* Reset SDI PLL */ + REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ +} + +void dss_dump_regs(struct seq_file *s) +{ +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r)) + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + DUMPREG(DSS_REVISION); + DUMPREG(DSS_SYSCONFIG); + DUMPREG(DSS_SYSSTATUS); + DUMPREG(DSS_IRQSTATUS); + DUMPREG(DSS_CONTROL); + DUMPREG(DSS_SDI_CONTROL); + DUMPREG(DSS_PLL_CONTROL); + DUMPREG(DSS_SDI_STATUS); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +#undef DUMPREG +} + +void dss_select_clk_source(bool dsi, bool dispc) +{ + u32 r; + r = dss_read_reg(DSS_CONTROL); + r = FLD_MOD(r, dsi, 1, 1); /* DSI_CLK_SWITCH */ + r = FLD_MOD(r, dispc, 0, 0); /* DISPC_CLK_SWITCH */ + dss_write_reg(DSS_CONTROL, r); +} + +int dss_get_dsi_clk_source(void) +{ + return FLD_GET(dss_read_reg(DSS_CONTROL), 1, 1); +} + +int dss_get_dispc_clk_source(void) +{ + return FLD_GET(dss_read_reg(DSS_CONTROL), 0, 0); +} + +static irqreturn_t dss_irq_handler_omap2(int irq, void *arg) +{ + dispc_irq_handler(); + + return IRQ_HANDLED; +} + +static irqreturn_t dss_irq_handler_omap3(int irq, void *arg) +{ + u32 irqstatus; + + irqstatus = dss_read_reg(DSS_IRQSTATUS); + + if (irqstatus & (1<<0)) /* DISPC_IRQ */ + dispc_irq_handler(); +#ifdef CONFIG_OMAP2_DSS_DSI + if (irqstatus & (1<<1)) /* DSI_IRQ */ + dsi_irq_handler(); +#endif + + return IRQ_HANDLED; +} + +static int _omap_dss_wait_reset(void) +{ + unsigned timeout = 1000; + + while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) { + udelay(1); + if (!--timeout) { + DSSERR("soft reset failed\n"); + return -ENODEV; + } + } + + return 0; +} + +static int _omap_dss_reset(void) +{ + /* Soft reset */ + REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1); + return _omap_dss_wait_reset(); +} + +void dss_set_venc_output(enum omap_dss_venc_type type) +{ + int l = 0; + + if (type == OMAP_DSS_VENC_TYPE_COMPOSITE) + l = 0; + else if (type == OMAP_DSS_VENC_TYPE_SVIDEO) + l = 1; + else + BUG(); + + /* venc out selection. 0 = comp, 1 = svideo */ + REG_FLD_MOD(DSS_CONTROL, l, 6, 6); +} + +void dss_set_dac_pwrdn_bgz(bool enable) +{ + REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */ +} + +int dss_init(bool skip_init) +{ + int r; + u32 rev; + + dss.base = ioremap(DSS_BASE, DSS_SZ_REGS); + if (!dss.base) { + DSSERR("can't ioremap DSS\n"); + r = -ENOMEM; + goto fail0; + } + + if (!skip_init) { + /* disable LCD and DIGIT output. This seems to fix the synclost + * problem that we get, if the bootloader starts the DSS and + * the kernel resets it */ + omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440); + + /* We need to wait here a bit, otherwise we sometimes start to + * get synclost errors, and after that only power cycle will + * restore DSS functionality. I have no idea why this happens. + * And we have to wait _before_ resetting the DSS, but after + * enabling clocks. + */ + msleep(50); + + _omap_dss_reset(); + } + + /* autoidle */ + REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0); + + /* Select DPLL */ + REG_FLD_MOD(DSS_CONTROL, 0, 0, 0); + +#ifdef CONFIG_OMAP2_DSS_VENC + REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */ + REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */ + REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */ +#endif + + r = request_irq(INT_24XX_DSS_IRQ, + cpu_is_omap24xx() + ? dss_irq_handler_omap2 + : dss_irq_handler_omap3, + 0, "OMAP DSS", NULL); + + if (r < 0) { + DSSERR("omap2 dss: request_irq failed\n"); + goto fail1; + } + + dss_save_context(); + + rev = dss_read_reg(DSS_REVISION); + printk(KERN_INFO "OMAP DSS rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + return 0; + +fail1: + iounmap(dss.base); +fail0: + return r; +} + +void dss_exit(void) +{ + free_irq(INT_24XX_DSS_IRQ, NULL); + + iounmap(dss.base); +} +
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
new file mode 100644
index 0000000..50f7f5f
--- /dev/null
+++ b/drivers/video/omap2/dss/dss.h@@ -0,0 +1,356 @@ +/* + * linux/drivers/video/omap2/dss/dss.h + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAP2_DSS_H +#define __OMAP2_DSS_H + +#ifdef CONFIG_OMAP2_DSS_DEBUG_SUPPORT +#define DEBUG +#endif + +#ifdef DEBUG +extern unsigned int dss_debug; +#ifdef DSS_SUBSYS_NAME +#define DSSDBG(format, ...) \ + if (dss_debug) \ + printk(KERN_DEBUG "omapdss " DSS_SUBSYS_NAME ": " format, \ + ## __VA_ARGS__) +#else +#define DSSDBG(format, ...) \ + if (dss_debug) \ + printk(KERN_DEBUG "omapdss: " format, ## __VA_ARGS__) +#endif + +#ifdef DSS_SUBSYS_NAME +#define DSSDBGF(format, ...) \ + if (dss_debug) \ + printk(KERN_DEBUG "omapdss " DSS_SUBSYS_NAME \ + ": %s(" format ")\n", \ + __func__, \ + ## __VA_ARGS__) +#else +#define DSSDBGF(format, ...) \ + if (dss_debug) \ + printk(KERN_DEBUG "omapdss: " \ + ": %s(" format ")\n", \ + __func__, \ + ## __VA_ARGS__) +#endif + +#else /* DEBUG */ +#define DSSDBG(format, ...) +#define DSSDBGF(format, ...) +#endif + + +#ifdef DSS_SUBSYS_NAME +#define DSSERR(format, ...) \ + printk(KERN_ERR "omapdss " DSS_SUBSYS_NAME " error: " format, \ + ## __VA_ARGS__) +#else +#define DSSERR(format, ...) \ + printk(KERN_ERR "omapdss error: " format, ## __VA_ARGS__) +#endif + +#ifdef DSS_SUBSYS_NAME +#define DSSINFO(format, ...) \ + printk(KERN_INFO "omapdss " DSS_SUBSYS_NAME ": " format, \ + ## __VA_ARGS__) +#else +#define DSSINFO(format, ...) \ + printk(KERN_INFO "omapdss: " format, ## __VA_ARGS__) +#endif + +#ifdef DSS_SUBSYS_NAME +#define DSSWARN(format, ...) \ + printk(KERN_WARNING "omapdss " DSS_SUBSYS_NAME ": " format, \ + ## __VA_ARGS__) +#else +#define DSSWARN(format, ...) \ + printk(KERN_WARNING "omapdss: " format, ## __VA_ARGS__) +#endif + +/* OMAP TRM gives bitfields as start:end, where start is the higher bit + number. For example 7:0 */ +#define FLD_MASK(start, end) (((1 << (start - end + 1)) - 1) << (end)) +#define FLD_VAL(val, start, end) (((val) << end) & FLD_MASK(start, end)) +#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end)) +#define FLD_MOD(orig, val, start, end) \ + (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) + +#define DISPC_MAX_FCK 173000000 + +enum omap_burst_size { + OMAP_DSS_BURST_4x32 = 0, + OMAP_DSS_BURST_8x32 = 1, + OMAP_DSS_BURST_16x32 = 2, +}; + +enum omap_parallel_interface_mode { + OMAP_DSS_PARALLELMODE_BYPASS, /* MIPI DPI */ + OMAP_DSS_PARALLELMODE_RFBI, /* MIPI DBI */ + OMAP_DSS_PARALLELMODE_DSI, +}; + +enum dss_clock { + DSS_CLK_ICK = 1 << 0, + DSS_CLK_FCK1 = 1 << 1, + DSS_CLK_FCK2 = 1 << 2, + DSS_CLK_54M = 1 << 3, + DSS_CLK_96M = 1 << 4, +}; + +struct dispc_clock_info { + /* rates that we get with dividers below */ + unsigned long fck; + unsigned long lck; + unsigned long pck; + + /* dividers */ + u16 fck_div; + u16 lck_div; + u16 pck_div; +}; + +struct dsi_clock_info { + /* rates that we get with dividers below */ + unsigned long fint; + unsigned long dsiphy; + unsigned long clkin; + unsigned long dsi1_pll_fclk; + unsigned long dsi2_pll_fclk; + unsigned long lck; + unsigned long pck; + + /* dividers */ + u16 regn; + u16 regm; + u16 regm3; + u16 regm4; + + u16 lck_div; + u16 pck_div; + + u8 highfreq; + bool use_dss2_fck; +}; + +struct seq_file; +struct platform_device; + +/* core */ +void dss_clk_enable(enum dss_clock clks); +void dss_clk_disable(enum dss_clock clks); +unsigned long dss_clk_get_rate(enum dss_clock clk); +int dss_need_ctx_restore(void); +void dss_dump_clocks(struct seq_file *s); +struct bus_type *dss_get_bus(void); + +/* display */ +int dss_suspend_all_devices(void); +int dss_resume_all_devices(void); + +void dss_init_device(struct platform_device *pdev, + struct omap_dss_device *dssdev); +void dss_uninit_device(struct platform_device *pdev, + struct omap_dss_device *dssdev); +bool dss_use_replication(struct omap_dss_device *dssdev, + enum omap_color_mode mode); +void default_get_overlay_fifo_thresholds(enum omap_plane plane, + u32 fifo_size, enum omap_burst_size *burst_size, + u32 *fifo_low, u32 *fifo_high); + +/* manager */ +int dss_init_overlay_managers(struct platform_device *pdev); +void dss_uninit_overlay_managers(struct platform_device *pdev); +int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl); +void dss_setup_partial_planes(struct omap_dss_device *dssdev, + u16 *x, u16 *y, u16 *w, u16 *h); +void dss_start_update(struct omap_dss_device *dssdev); + +/* overlay */ +void dss_init_overlays(struct platform_device *pdev); +void dss_uninit_overlays(struct platform_device *pdev); +int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev); +void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr); +#ifdef L4_EXAMPLE +void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr); +#endif +void dss_recheck_connections(struct omap_dss_device *dssdev, bool force); + +/* DSS */ +int dss_init(bool skip_init); +void dss_exit(void); + +void dss_save_context(void); +void dss_restore_context(void); + +void dss_dump_regs(struct seq_file *s); + +void dss_sdi_init(u8 datapairs); +void dss_sdi_enable(void); +void dss_sdi_disable(void); + +void dss_select_clk_source(bool dsi, bool dispc); +int dss_get_dsi_clk_source(void); +int dss_get_dispc_clk_source(void); +void dss_set_venc_output(enum omap_dss_venc_type type); +void dss_set_dac_pwrdn_bgz(bool enable); + +/* SDI */ +int sdi_init(bool skip_init); +void sdi_exit(void); +int sdi_init_display(struct omap_dss_device *display); + +/* DSI */ +int dsi_init(struct platform_device *pdev); +void dsi_exit(void); + +void dsi_dump_clocks(struct seq_file *s); +void dsi_dump_regs(struct seq_file *s); + +void dsi_save_context(void); +void dsi_restore_context(void); + +int dsi_init_display(struct omap_dss_device *display); +void dsi_irq_handler(void); +unsigned long dsi_get_dsi1_pll_rate(void); +unsigned long dsi_get_dsi2_pll_rate(void); +int dsi_pll_calc_pck(bool is_tft, unsigned long req_pck, + struct dsi_clock_info *cinfo); +int dsi_pll_program(struct dsi_clock_info *cinfo); +int dsi_pll_init(bool enable_hsclk, bool enable_hsdiv); +void dsi_pll_uninit(void); +void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, + u32 fifo_size, enum omap_burst_size *burst_size, + u32 *fifo_low, u32 *fifo_high); + +/* DPI */ +int dpi_init(void); +void dpi_exit(void); +int dpi_init_display(struct omap_dss_device *dssdev); + +/* DISPC */ +int dispc_init(void); +void dispc_exit(void); +void dispc_dump_clocks(struct seq_file *s); +void dispc_dump_regs(struct seq_file *s); +void dispc_irq_handler(void); +void dispc_fake_vsync_irq(void); + +void dispc_save_context(void); +void dispc_restore_context(void); + +void dispc_enable_sidle(void); +void dispc_disable_sidle(void); + +void dispc_lcd_enable_signal_polarity(bool act_high); +void dispc_lcd_enable_signal(bool enable); +void dispc_pck_free_enable(bool enable); +void dispc_enable_fifohandcheck(bool enable); + +void dispc_set_lcd_size(u16 width, u16 height); +void dispc_set_digit_size(u16 width, u16 height); +u32 dispc_get_plane_fifo_size(enum omap_plane plane); +void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high); +void dispc_enable_fifomerge(bool enable); +void dispc_set_burst_size(enum omap_plane plane, + enum omap_burst_size burst_size); + +void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr); +void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr); +void dispc_set_plane_pos(enum omap_plane plane, u16 x, u16 y); +void dispc_set_plane_size(enum omap_plane plane, u16 width, u16 height); +void dispc_set_channel_out(enum omap_plane plane, + enum omap_channel channel_out); + +int dispc_setup_plane(enum omap_plane plane, + u32 paddr, u16 screen_width, + u16 pos_x, u16 pos_y, + u16 width, u16 height, + u16 out_width, u16 out_height, + enum omap_color_mode color_mode, + bool ilace, + enum omap_dss_rotation_type rotation_type, + u8 rotation, bool mirror, + u8 global_alpha); + +bool dispc_go_busy(enum omap_channel channel); +void dispc_go(enum omap_channel channel); +void dispc_enable_lcd_out(bool enable); +void dispc_enable_digit_out(bool enable); +int dispc_enable_plane(enum omap_plane plane, bool enable); +void dispc_enable_replication(enum omap_plane plane, bool enable); + +void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode); +void dispc_set_tft_data_lines(u8 data_lines); +void dispc_set_lcd_display_type(enum omap_lcd_display_type type); +void dispc_set_loadmode(enum omap_dss_load_mode mode); + +void dispc_set_default_color(enum omap_channel channel, u32 color); +u32 dispc_get_default_color(enum omap_channel channel); +void dispc_set_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type type, + u32 trans_key); +void dispc_get_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type *type, + u32 *trans_key); +void dispc_enable_trans_key(enum omap_channel ch, bool enable); +void dispc_enable_alpha_blending(enum omap_channel ch, bool enable); +bool dispc_trans_key_enabled(enum omap_channel ch); +bool dispc_alpha_blending_enabled(enum omap_channel ch); + +bool dispc_lcd_timings_ok(struct omap_video_timings *timings); +void dispc_set_lcd_timings(struct omap_video_timings *timings); +unsigned long dispc_fclk_rate(void); +unsigned long dispc_lclk_rate(void); +unsigned long dispc_pclk_rate(void); +void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb); +void find_lck_pck_divs(bool is_tft, unsigned long req_pck, unsigned long fck, + u16 *lck_div, u16 *pck_div); +int dispc_calc_clock_div(bool is_tft, unsigned long req_pck, + struct dispc_clock_info *cinfo); +int dispc_set_clock_div(struct dispc_clock_info *cinfo); +int dispc_get_clock_div(struct dispc_clock_info *cinfo); +void dispc_set_lcd_divisor(u16 lck_div, u16 pck_div); + + +/* VENC */ +int venc_init(struct platform_device *pdev); +void venc_exit(void); +void venc_dump_regs(struct seq_file *s); +int venc_init_display(struct omap_dss_device *display); + +/* RFBI */ +int rfbi_init(void); +void rfbi_exit(void); +void rfbi_dump_regs(struct seq_file *s); + +int rfbi_configure(int rfbi_module, int bpp, int lines); +void rfbi_enable_rfbi(bool enable); +void rfbi_transfer_area(u16 width, u16 height, + void (callback)(void *data), void *data); +void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t); +unsigned long rfbi_get_max_tx_rate(void); +int rfbi_init_display(struct omap_dss_device *display); + +#endif
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
new file mode 100644
index 0000000..7ee0b2b
--- /dev/null
+++ b/drivers/video/omap2/dss/manager.c@@ -0,0 +1,1487 @@ +/* + * linux/drivers/video/omap2/dss/manager.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "MANAGER" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/jiffies.h> + +#include <mach/display.h> +#include <mach/cpu.h> + +#include "dss.h" + +static int num_managers; +static struct list_head manager_list; + +static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name); +} + +static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", + mgr->device ? mgr->device->name : "<none>"); +} + +static ssize_t manager_display_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + int r = 0; + size_t len = size; + struct omap_dss_device *dssdev = NULL; + + int match(struct omap_dss_device *dssdev, void *data) + { + const char *str = data; + return sysfs_streq(dssdev->name, str); + } + + if (buf[size-1] == '\n') + --len; + + if (len > 0) + dssdev = omap_dss_find_device((void *)buf, match); + + if (len > 0 && dssdev == NULL) + return -EINVAL; + + if (dssdev) + DSSDBG("display %s found\n", dssdev->name); + + if (mgr->device) { + r = mgr->unset_device(mgr); + if (r) { + DSSERR("failed to unset display\n"); + goto put_device; + } + } + + if (dssdev) { + r = mgr->set_device(mgr, dssdev); + if (r) { + DSSERR("failed to set manager\n"); + goto put_device; + } + + r = mgr->apply(mgr); + if (r) { + DSSERR("failed to apply dispc config\n"); + goto put_device; + } + } + +put_device: + if (dssdev) + omap_dss_put_device(dssdev); + + return r ? r : size; +} + +static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.default_color); +} + +static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + u32 color; + int r; + + if (sscanf(buf, "%d", &color) != 1) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.default_color = color; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +static const char *trans_key_type_str[] = { + "gfx-destination", + "video-source", +}; + +static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr, + char *buf) +{ + enum omap_dss_trans_key_type key_type; + + key_type = mgr->info.trans_key_type; + BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str)); + + return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]); +} + +static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + enum omap_dss_trans_key_type key_type; + struct omap_overlay_manager_info info; + int r; + + for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + key_type < ARRAY_SIZE(trans_key_type_str); key_type++) { + if (sysfs_streq(buf, trans_key_type_str[key_type])) + break; + } + + if (key_type == ARRAY_SIZE(trans_key_type_str)) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.trans_key_type = key_type; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_key); +} + +static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + u32 key_value; + int r; + + if (sscanf(buf, "%d", &key_value) != 1) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.trans_key = key_value; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_enabled); +} + +static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + int enable; + int r; + + if (sscanf(buf, "%d", &enable) != 1) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.trans_enabled = enable ? true : false; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +static ssize_t manager_alpha_blending_enabled_show( + struct omap_overlay_manager *mgr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.alpha_enabled); +} + +static ssize_t manager_alpha_blending_enabled_store( + struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + int enable; + int r; + + if (sscanf(buf, "%d", &enable) != 1) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.alpha_enabled = enable ? true : false; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +struct manager_attribute { + struct attribute attr; + ssize_t (*show)(struct omap_overlay_manager *, char *); + ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t); +}; + +#define MANAGER_ATTR(_name, _mode, _show, _store) \ + struct manager_attribute manager_attr_##_name = \ + __ATTR(_name, _mode, _show, _store) + +static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL); +static MANAGER_ATTR(display, S_IRUGO|S_IWUSR, + manager_display_show, manager_display_store); +static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR, + manager_default_color_show, manager_default_color_store); +static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR, + manager_trans_key_type_show, manager_trans_key_type_store); +static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR, + manager_trans_key_value_show, manager_trans_key_value_store); +static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR, + manager_trans_key_enabled_show, + manager_trans_key_enabled_store); +static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR, + manager_alpha_blending_enabled_show, + manager_alpha_blending_enabled_store); + + +static struct attribute *manager_sysfs_attrs[] = { + &manager_attr_name.attr, + &manager_attr_display.attr, + &manager_attr_default_color.attr, + &manager_attr_trans_key_type.attr, + &manager_attr_trans_key_value.attr, + &manager_attr_trans_key_enabled.attr, + &manager_attr_alpha_blending_enabled.attr, + NULL +}; + +static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct omap_overlay_manager *manager; + struct manager_attribute *manager_attr; + + manager = container_of(kobj, struct omap_overlay_manager, kobj); + manager_attr = container_of(attr, struct manager_attribute, attr); + + if (!manager_attr->show) + return -ENOENT; + + return manager_attr->show(manager, buf); +} + +static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t size) +{ + struct omap_overlay_manager *manager; + struct manager_attribute *manager_attr; + + manager = container_of(kobj, struct omap_overlay_manager, kobj); + manager_attr = container_of(attr, struct manager_attribute, attr); + + if (!manager_attr->store) + return -ENOENT; + + return manager_attr->store(manager, buf, size); +} + +static struct sysfs_ops manager_sysfs_ops = { + .show = manager_attr_show, + .store = manager_attr_store, +}; + +static struct kobj_type manager_ktype = { + .sysfs_ops = &manager_sysfs_ops, + .default_attrs = manager_sysfs_attrs, +}; + +/* + * We have 4 levels of cache for the dispc settings. First two are in SW and + * the latter two in HW. + * + * +--------------------+ + * |overlay/manager_info| + * +--------------------+ + * v + * apply() + * v + * +--------------------+ + * | dss_cache | + * +--------------------+ + * v + * configure() + * v + * +--------------------+ + * | shadow registers | + * +--------------------+ + * v + * VFP or lcd/digit_enable + * v + * +--------------------+ + * | registers | + * +--------------------+ + */ + +struct overlay_cache_data { + /* If true, cache changed, but not written to shadow registers. Set + * in apply(), cleared when registers written. */ + bool dirty; + /* If true, shadow registers contain changed values not yet in real + * registers. Set when writing to shadow registers, cleared at + * VSYNC/EVSYNC */ + bool shadow_dirty; + + bool enabled; + + u32 paddr; + void __iomem *vaddr; + u16 screen_width; + u16 width; + u16 height; + enum omap_color_mode color_mode; + u8 rotation; + enum omap_dss_rotation_type rotation_type; + bool mirror; + + u16 pos_x; + u16 pos_y; + u16 out_width; /* if 0, out_width == width */ + u16 out_height; /* if 0, out_height == height */ + u8 global_alpha; + + enum omap_channel channel; + bool replication; + bool ilace; + + enum omap_burst_size burst_size; + u32 fifo_low; + u32 fifo_high; + + bool manual_update; +}; + +struct manager_cache_data { + /* If true, cache changed, but not written to shadow registers. Set + * in apply(), cleared when registers written. */ + bool dirty; + /* If true, shadow registers contain changed values not yet in real + * registers. Set when writing to shadow registers, cleared at + * VSYNC/EVSYNC */ + bool shadow_dirty; + + u32 default_color; + + enum omap_dss_trans_key_type trans_key_type; + u32 trans_key; + bool trans_enabled; + + bool alpha_enabled; + + bool manual_upd_display; + bool manual_update; + bool do_manual_update; + + /* manual update region */ + u16 x, y, w, h; +}; + +static struct { + spinlock_t lock; + struct overlay_cache_data overlay_cache[3]; + struct manager_cache_data manager_cache[2]; + + bool irq_enabled; +} dss_cache; + + + +static int omap_dss_set_device(struct omap_overlay_manager *mgr, + struct omap_dss_device *dssdev) +{ + int i; + int r; + + if (dssdev->manager) { + DSSERR("display '%s' already has a manager '%s'\n", + dssdev->name, dssdev->manager->name); + return -EINVAL; + } + + if ((mgr->supported_displays & dssdev->type) == 0) { + DSSERR("display '%s' does not support manager '%s'\n", + dssdev->name, mgr->name); + return -EINVAL; + } + + for (i = 0; i < mgr->num_overlays; i++) { + struct omap_overlay *ovl = mgr->overlays[i]; + + if (ovl->manager != mgr || !ovl->info.enabled) + continue; + + r = dss_check_overlay(ovl, dssdev); + if (r) + return r; + } + + dssdev->manager = mgr; + mgr->device = dssdev; + mgr->device_changed = true; + + return 0; +} + +static int omap_dss_unset_device(struct omap_overlay_manager *mgr) +{ + if (!mgr->device) { + DSSERR("failed to unset display, display not set.\n"); + return -EINVAL; + } + + mgr->device->manager = NULL; + mgr->device = NULL; + mgr->device_changed = true; + + return 0; +} + +static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) +{ + unsigned long timeout = msecs_to_jiffies(500); + struct manager_cache_data *mc; + enum omap_channel channel; + u32 irq; + int r; + int i; + + if (!mgr->device) + return 0; + + if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) { + irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; + channel = OMAP_DSS_CHANNEL_DIGIT; + } else { + if (mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { + enum omap_dss_update_mode mode; + mode = mgr->device->get_update_mode(mgr->device); + if (mode != OMAP_DSS_UPDATE_AUTO) + return 0; + + irq = DISPC_IRQ_FRAMEDONE; + } else { + irq = DISPC_IRQ_VSYNC; + } + channel = OMAP_DSS_CHANNEL_LCD; + } + + mc = &dss_cache.manager_cache[mgr->id]; + i = 0; + while (1) { + unsigned long flags; + bool shadow_dirty, dirty; + + spin_lock_irqsave(&dss_cache.lock, flags); + dirty = mc->dirty; + shadow_dirty = mc->shadow_dirty; + spin_unlock_irqrestore(&dss_cache.lock, flags); + + if (!dirty && !shadow_dirty) { + r = 0; + break; + } + + /* 4 iterations is the worst case: + * 1 - initial iteration, dirty = true (between VFP and VSYNC) + * 2 - first VSYNC, dirty = true + * 3 - dirty = false, shadow_dirty = true + * 4 - shadow_dirty = false */ + if (i++ == 3) { + DSSERR("mgr(%d)->wait_for_go() not finishing\n", + mgr->id); + r = 0; + break; + } + + r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); + if (r == -ERESTARTSYS) + break; + + if (r) { + DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id); + break; + } + } + + return r; +} + +int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) +{ + unsigned long timeout = msecs_to_jiffies(500); + enum omap_channel channel; + struct overlay_cache_data *oc; + struct omap_dss_device *dssdev; + u32 irq; + int r; + int i; + + if (!ovl->manager || !ovl->manager->device) + return 0; + + dssdev = ovl->manager->device; + + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { + irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; + channel = OMAP_DSS_CHANNEL_DIGIT; + } else { + if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { + enum omap_dss_update_mode mode; + mode = dssdev->get_update_mode(dssdev); + if (mode != OMAP_DSS_UPDATE_AUTO) + return 0; + + irq = DISPC_IRQ_FRAMEDONE; + } else { + irq = DISPC_IRQ_VSYNC; + } + channel = OMAP_DSS_CHANNEL_LCD; + } + + oc = &dss_cache.overlay_cache[ovl->id]; + i = 0; + while (1) { + unsigned long flags; + bool shadow_dirty, dirty; + + spin_lock_irqsave(&dss_cache.lock, flags); + dirty = oc->dirty; + shadow_dirty = oc->shadow_dirty; + spin_unlock_irqrestore(&dss_cache.lock, flags); + + if (!dirty && !shadow_dirty) { + r = 0; + break; + } + + /* 4 iterations is the worst case: + * 1 - initial iteration, dirty = true (between VFP and VSYNC) + * 2 - first VSYNC, dirty = true + * 3 - dirty = false, shadow_dirty = true + * 4 - shadow_dirty = false */ + if (i++ == 3) { + DSSERR("ovl(%d)->wait_for_go() not finishing\n", + ovl->id); + r = 0; + break; + } + + r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); + if (r == -ERESTARTSYS) + break; + + if (r) { + DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id); + break; + } + } + + return r; +} + +static int overlay_enabled(struct omap_overlay *ovl) +{ + return ovl->info.enabled && ovl->manager && ovl->manager->device; +} + +/* Is rect1 a subset of rect2? */ +static bool rectangle_subset(int x1, int y1, int w1, int h1, + int x2, int y2, int w2, int h2) +{ + if (x1 < x2 || y1 < y2) + return false; + + if (x1 + w1 > x2 + w2) + return false; + + if (y1 + h1 > y2 + h2) + return false; + + return true; +} + +/* Do rect1 and rect2 overlap? */ +static bool rectangle_intersects(int x1, int y1, int w1, int h1, + int x2, int y2, int w2, int h2) +{ + if (x1 >= x2 + w2) + return false; + + if (x2 >= x1 + w1) + return false; + + if (y1 >= y2 + h2) + return false; + + if (y2 >= y1 + h1) + return false; + + return true; +} + +static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc) +{ + if (oc->out_width != 0 && oc->width != oc->out_width) + return true; + + if (oc->out_height != 0 && oc->height != oc->out_height) + return true; + + return false; +} + +static int configure_overlay(enum omap_plane plane) +{ + struct overlay_cache_data *c; + struct manager_cache_data *mc; + u16 outw, outh; + u16 x, y, w, h; + u32 paddr; + int r; + + DSSDBGF("%d", plane); + + c = &dss_cache.overlay_cache[plane]; + + if (!c->enabled) { + dispc_enable_plane(plane, 0); + return 0; + } + + mc = &dss_cache.manager_cache[c->channel]; + + x = c->pos_x; + y = c->pos_y; + w = c->width; + h = c->height; + outw = c->out_width == 0 ? c->width : c->out_width; + outh = c->out_height == 0 ? c->height : c->out_height; + paddr = c->paddr; + + if (c->manual_update && mc->do_manual_update) { + unsigned bpp; + /* If the overlay is outside the update region, disable it */ + if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h, + x, y, outw, outh)) { + dispc_enable_plane(plane, 0); + return 0; + } + + switch (c->color_mode) { + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + bpp = 16; + break; + + case OMAP_DSS_COLOR_RGB24P: + bpp = 24; + break; + + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + bpp = 32; + break; + + default: + BUG(); + } + + if (dispc_is_overlay_scaled(c)) { + /* If the overlay is scaled, the update area has + * already been enlarged to cover the whole overlay. We + * only need to adjust x/y here */ + x = c->pos_x - mc->x; + y = c->pos_y - mc->y; + } else { + if (mc->x > c->pos_x) { + x = 0; + w -= (mc->x - c->pos_x); + paddr += (mc->x - c->pos_x) * bpp / 8; + } else { + x = c->pos_x - mc->x; + } + + if (mc->y > c->pos_y) { + y = 0; + h -= (mc->y - c->pos_y); + paddr += (mc->y - c->pos_y) * c->screen_width * + bpp / 8; + } else { + y = c->pos_y - mc->y; + } + + if (mc->w < (x+w)) + w -= (x+w) - (mc->w); + + if (mc->h < (y+h)) + h -= (y+h) - (mc->h); + + outw = w; + outh = h; + } + } + + r = dispc_setup_plane(plane, + paddr, + c->screen_width, + x, y, + w, h, + outw, outh, + c->color_mode, + c->ilace, + c->rotation_type, + c->rotation, + c->mirror, + c->global_alpha); + + if (r) { + /* this shouldn't happen */ + DSSERR("dispc_setup_plane failed for ovl %d\n", plane); + dispc_enable_plane(plane, 0); + return r; + } + + dispc_enable_replication(plane, c->replication); + + dispc_set_burst_size(plane, c->burst_size); + dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high); + + dispc_enable_plane(plane, 1); + + return 0; +} + +static void configure_manager(enum omap_channel channel) +{ + struct manager_cache_data *c; + + DSSDBGF("%d", channel); + + c = &dss_cache.manager_cache[channel]; + + dispc_set_trans_key(channel, c->trans_key_type, c->trans_key); + dispc_enable_trans_key(channel, c->trans_enabled); + dispc_enable_alpha_blending(channel, c->alpha_enabled); +} + +/* configure_dispc() tries to write values from cache to shadow registers. + * It writes only to those managers/overlays that are not busy. + * returns 0 if everything could be written to shadow registers. + * returns 1 if not everything could be written to shadow registers. */ +static int configure_dispc(void) +{ + struct overlay_cache_data *oc; + struct manager_cache_data *mc; + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); + const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + int i; + int r; + bool mgr_busy[2]; + bool mgr_go[2]; + bool busy; + + r = 0; + busy = false; + + mgr_busy[0] = dispc_go_busy(0); + mgr_busy[1] = dispc_go_busy(1); + mgr_go[0] = false; + mgr_go[1] = false; + + /* Commit overlay settings */ + for (i = 0; i < num_ovls; ++i) { + oc = &dss_cache.overlay_cache[i]; + mc = &dss_cache.manager_cache[oc->channel]; + + if (!oc->dirty) + continue; + + if (oc->manual_update && !mc->do_manual_update) + continue; + + if (mgr_busy[oc->channel]) { + busy = true; + continue; + } + + r = configure_overlay(i); + if (r) + DSSERR("configure_overlay %d failed\n", i); + + oc->dirty = false; + oc->shadow_dirty = true; + mgr_go[oc->channel] = true; + } + + /* Commit manager settings */ + for (i = 0; i < num_mgrs; ++i) { + mc = &dss_cache.manager_cache[i]; + + if (!mc->dirty) + continue; + + if (mc->manual_update && !mc->do_manual_update) + continue; + + if (mgr_busy[i]) { + busy = true; + continue; + } + + configure_manager(i); + mc->dirty = false; + mc->shadow_dirty = true; + mgr_go[i] = true; + } + + /* set GO */ + for (i = 0; i < num_mgrs; ++i) { + mc = &dss_cache.manager_cache[i]; + + if (!mgr_go[i]) + continue; + + /* We don't need GO with manual update display. LCD iface will + * always be turned off after frame, and new settings will be + * taken in to use at next update */ + if (!mc->manual_upd_display) + dispc_go(i); + } + + if (busy) + r = 1; + else + r = 0; + + return r; +} + +/* Configure dispc for partial update. Return possibly modified update + * area */ +void dss_setup_partial_planes(struct omap_dss_device *dssdev, + u16 *xi, u16 *yi, u16 *wi, u16 *hi) +{ + struct overlay_cache_data *oc; + struct manager_cache_data *mc; + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); + struct omap_overlay_manager *mgr; + int i; + u16 x, y, w, h; + unsigned long flags; + + x = *xi; + y = *yi; + w = *wi; + h = *hi; + + DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n", + *xi, *yi, *wi, *hi); + + mgr = dssdev->manager; + + if (!mgr) { + DSSDBG("no manager\n"); + return; + } + + spin_lock_irqsave(&dss_cache.lock, flags); + + /* We need to show the whole overlay if it is scaled. So look for + * those, and make the update area larger if found. + * Also mark the overlay cache dirty */ + for (i = 0; i < num_ovls; ++i) { + unsigned x1, y1, x2, y2; + unsigned outw, outh; + + oc = &dss_cache.overlay_cache[i]; + + if (oc->channel != mgr->id) + continue; + + oc->dirty = true; + + if (!oc->enabled) + continue; + + if (!dispc_is_overlay_scaled(oc)) + continue; + + outw = oc->out_width == 0 ? oc->width : oc->out_width; + outh = oc->out_height == 0 ? oc->height : oc->out_height; + + /* is the overlay outside the update region? */ + if (!rectangle_intersects(x, y, w, h, + oc->pos_x, oc->pos_y, + outw, outh)) + continue; + + /* if the overlay totally inside the update region? */ + if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh, + x, y, w, h)) + continue; + + if (x > oc->pos_x) + x1 = oc->pos_x; + else + x1 = x; + + if (y > oc->pos_y) + y1 = oc->pos_y; + else + y1 = y; + + if ((x + w) < (oc->pos_x + outw)) + x2 = oc->pos_x + outw; + else + x2 = x + w; + + if ((y + h) < (oc->pos_y + outh)) + y2 = oc->pos_y + outh; + else + y2 = y + h; + + x = x1; + y = y1; + w = x2 - x1; + h = y2 - y1; + + DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n", + i, x, y, w, h); + } + + mc = &dss_cache.manager_cache[mgr->id]; + mc->do_manual_update = true; + mc->x = x; + mc->y = y; + mc->w = w; + mc->h = h; + + configure_dispc(); + + mc->do_manual_update = false; + + spin_unlock_irqrestore(&dss_cache.lock, flags); + + *xi = x; + *yi = y; + *wi = w; + *hi = h; +} + +void dss_start_update(struct omap_dss_device *dssdev) +{ + struct manager_cache_data *mc; + struct overlay_cache_data *oc; + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); + const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + struct omap_overlay_manager *mgr; + int i; + + mgr = dssdev->manager; + + for (i = 0; i < num_ovls; ++i) { + oc = &dss_cache.overlay_cache[i]; + if (oc->channel != mgr->id) + continue; + + oc->shadow_dirty = false; + } + + for (i = 0; i < num_mgrs; ++i) { + mc = &dss_cache.manager_cache[i]; + if (mgr->id != i) + continue; + + mc->shadow_dirty = false; + } + + dispc_enable_lcd_out(1); +} + +static void dss_apply_irq_handler(void *data, u32 mask) +{ + struct manager_cache_data *mc; + struct overlay_cache_data *oc; + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); + const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + int i, r; + bool mgr_busy[2]; + + mgr_busy[0] = dispc_go_busy(0); + mgr_busy[1] = dispc_go_busy(1); + + spin_lock(&dss_cache.lock); + + for (i = 0; i < num_ovls; ++i) { + oc = &dss_cache.overlay_cache[i]; + if (!mgr_busy[oc->channel]) + oc->shadow_dirty = false; + } + + for (i = 0; i < num_mgrs; ++i) { + mc = &dss_cache.manager_cache[i]; + if (!mgr_busy[i]) + mc->shadow_dirty = false; + } + + r = configure_dispc(); + if (r == 1) + goto end; + + /* re-read busy flags */ + mgr_busy[0] = dispc_go_busy(0); + mgr_busy[1] = dispc_go_busy(1); + + /* keep running as long as there are busy managers, so that + * we can collect overlay-applied information */ + for (i = 0; i < num_mgrs; ++i) { + if (mgr_busy[i]) + goto end; + } + + omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, + DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | + DISPC_IRQ_EVSYNC_EVEN); + dss_cache.irq_enabled = false; + +end: + spin_unlock(&dss_cache.lock); +} + +static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) +{ + struct overlay_cache_data *oc; + struct manager_cache_data *mc; + int i; + struct omap_overlay *ovl; + int num_planes_enabled = 0; + bool use_fifomerge; + unsigned long flags; + int r; + + DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name); + + spin_lock_irqsave(&dss_cache.lock, flags); + + /* Configure overlays */ + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_dss_device *dssdev; + + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + oc = &dss_cache.overlay_cache[ovl->id]; + + if (!overlay_enabled(ovl)) { + if (oc->enabled) { + oc->enabled = false; + oc->dirty = true; + } + continue; + } + + if (!ovl->info_dirty) { + if (oc->enabled) + ++num_planes_enabled; + continue; + } + + dssdev = ovl->manager->device; + + if (dss_check_overlay(ovl, dssdev)) { + if (oc->enabled) { + oc->enabled = false; + oc->dirty = true; + } + continue; + } + + ovl->info_dirty = false; + oc->dirty = true; + + oc->paddr = ovl->info.paddr; + oc->vaddr = ovl->info.vaddr; + oc->screen_width = ovl->info.screen_width; + oc->width = ovl->info.width; + oc->height = ovl->info.height; + oc->color_mode = ovl->info.color_mode; + oc->rotation = ovl->info.rotation; + oc->rotation_type = ovl->info.rotation_type; + oc->mirror = ovl->info.mirror; + oc->pos_x = ovl->info.pos_x; + oc->pos_y = ovl->info.pos_y; + oc->out_width = ovl->info.out_width; + oc->out_height = ovl->info.out_height; + oc->global_alpha = ovl->info.global_alpha; + + oc->replication = + dss_use_replication(dssdev, ovl->info.color_mode); + + oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC; + + oc->channel = ovl->manager->id; + + oc->enabled = true; + + oc->manual_update = + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && + dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO; + + ++num_planes_enabled; + } + + /* Configure managers */ + list_for_each_entry(mgr, &manager_list, list) { + struct omap_dss_device *dssdev; + + if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC)) + continue; + + mc = &dss_cache.manager_cache[mgr->id]; + + if (mgr->device_changed) { + mgr->device_changed = false; + mgr->info_dirty = true; + } + + if (!mgr->info_dirty) + continue; + + if (!mgr->device) + continue; + + dssdev = mgr->device; + + mgr->info_dirty = false; + mc->dirty = true; + + mc->default_color = mgr->info.default_color; + mc->trans_key_type = mgr->info.trans_key_type; + mc->trans_key = mgr->info.trans_key; + mc->trans_enabled = mgr->info.trans_enabled; + mc->alpha_enabled = mgr->info.alpha_enabled; + + mc->manual_upd_display = + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; + + mc->manual_update = + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && + dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO; + } + + /* XXX TODO: Try to get fifomerge working. The problem is that it + * affects both managers, not individually but at the same time. This + * means the change has to be well synchronized. I guess the proper way + * is to have a two step process for fifo merge: + * fifomerge enable: + * 1. disable other planes, leaving one plane enabled + * 2. wait until the planes are disabled on HW + * 3. config merged fifo thresholds, enable fifomerge + * fifomerge disable: + * 1. config unmerged fifo thresholds, disable fifomerge + * 2. wait until fifo changes are in HW + * 3. enable planes + */ + use_fifomerge = false; + + /* Configure overlay fifos */ + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_dss_device *dssdev; + u32 size; + + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + oc = &dss_cache.overlay_cache[ovl->id]; + + if (!oc->enabled) + continue; + + dssdev = ovl->manager->device; + + size = dispc_get_plane_fifo_size(ovl->id); + if (use_fifomerge) + size *= 3; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: + case OMAP_DISPLAY_TYPE_DBI: + case OMAP_DISPLAY_TYPE_SDI: + case OMAP_DISPLAY_TYPE_VENC: + default_get_overlay_fifo_thresholds(ovl->id, size, + &oc->burst_size, &oc->fifo_low, + &oc->fifo_high); + break; +#ifdef CONFIG_OMAP2_DSS_DSI + case OMAP_DISPLAY_TYPE_DSI: + dsi_get_overlay_fifo_thresholds(ovl->id, size, + &oc->burst_size, &oc->fifo_low, + &oc->fifo_high); + break; +#endif + default: + BUG(); + } + } + + r = 0; + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + if (!dss_cache.irq_enabled) { + r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, + DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | + DISPC_IRQ_EVSYNC_EVEN); + dss_cache.irq_enabled = true; + } + configure_dispc(); + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + spin_unlock_irqrestore(&dss_cache.lock, flags); + + return r; +} + +static int dss_check_manager(struct omap_overlay_manager *mgr) +{ + /* OMAP supports only graphics source transparency color key and alpha + * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */ + + if (mgr->info.alpha_enabled && mgr->info.trans_enabled && + mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) + return -EINVAL; + + return 0; +} + +static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr, + struct omap_overlay_manager_info *info) +{ + int r; + struct omap_overlay_manager_info old_info; + + old_info = mgr->info; + mgr->info = *info; + + r = dss_check_manager(mgr); + if (r) { + mgr->info = old_info; + return r; + } + + mgr->info_dirty = true; + + return 0; +} + +static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr, + struct omap_overlay_manager_info *info) +{ + *info = mgr->info; +} + +static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager) +{ + ++num_managers; + list_add_tail(&manager->list, &manager_list); +} + +int dss_init_overlay_managers(struct platform_device *pdev) +{ + int i, r; + + spin_lock_init(&dss_cache.lock); + + INIT_LIST_HEAD(&manager_list); + + num_managers = 0; + + for (i = 0; i < 2; ++i) { + struct omap_overlay_manager *mgr; + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + + BUG_ON(mgr == NULL); + + switch (i) { + case 0: + mgr->name = "lcd"; + mgr->id = OMAP_DSS_CHANNEL_LCD; + mgr->supported_displays = + OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | + OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI; + break; + case 1: + mgr->name = "tv"; + mgr->id = OMAP_DSS_CHANNEL_DIGIT; + mgr->supported_displays = OMAP_DISPLAY_TYPE_VENC; + break; + } + + mgr->set_device = &omap_dss_set_device; + mgr->unset_device = &omap_dss_unset_device; + mgr->apply = &omap_dss_mgr_apply; + mgr->set_manager_info = &omap_dss_mgr_set_info; + mgr->get_manager_info = &omap_dss_mgr_get_info; + mgr->wait_for_go = &dss_mgr_wait_for_go; + + mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC; + + dss_overlay_setup_dispc_manager(mgr); + + omap_dss_add_overlay_manager(mgr); + + r = kobject_init_and_add(&mgr->kobj, &manager_ktype, + &pdev->dev.kobj, "manager%d", i); + + if (r) { + DSSERR("failed to create sysfs file\n"); + continue; + } + } + +#ifdef L4_EXAMPLE + { + int omap_dss_mgr_apply_l4(struct omap_overlay_manager *mgr) + { + DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name); + + return 0; + } + + struct omap_overlay_manager *mgr; + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + + BUG_ON(mgr == NULL); + + mgr->name = "l4"; + mgr->supported_displays = + OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI; + + mgr->set_device = &omap_dss_set_device; + mgr->unset_device = &omap_dss_unset_device; + mgr->apply = &omap_dss_mgr_apply_l4; + mgr->set_manager_info = &omap_dss_mgr_set_info; + mgr->get_manager_info = &omap_dss_mgr_get_info; + + dss_overlay_setup_l4_manager(mgr); + + omap_dss_add_overlay_manager(mgr); + + r = kobject_init_and_add(&mgr->kobj, &manager_ktype, + &pdev->dev.kobj, "managerl4"); + + if (r) + DSSERR("failed to create sysfs file\n"); + } +#endif + + return 0; +} + +void dss_uninit_overlay_managers(struct platform_device *pdev) +{ + struct omap_overlay_manager *mgr; + + while (!list_empty(&manager_list)) { + mgr = list_first_entry(&manager_list, + struct omap_overlay_manager, list); + list_del(&mgr->list); + kobject_del(&mgr->kobj); + kobject_put(&mgr->kobj); + kfree(mgr); + } + + num_managers = 0; +} + +int omap_dss_get_num_overlay_managers(void) +{ + return num_managers; +} +EXPORT_SYMBOL(omap_dss_get_num_overlay_managers); + +struct omap_overlay_manager *omap_dss_get_overlay_manager(int num) +{ + int i = 0; + struct omap_overlay_manager *mgr; + + list_for_each_entry(mgr, &manager_list, list) { + if (i++ == num) + return mgr; + } + + return NULL; +} +EXPORT_SYMBOL(omap_dss_get_overlay_manager); +
diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
new file mode 100644
index 0000000..c7cb81b
--- /dev/null
+++ b/drivers/video/omap2/dss/overlay.c@@ -0,0 +1,673 @@ +/* + * linux/drivers/video/omap2/dss/overlay.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "OVERLAY" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/sysfs.h> +#include <linux/kobject.h> +#include <linux/platform_device.h> +#include <linux/delay.h> + +#include <mach/display.h> + +#include "dss.h" + +static int num_overlays; +static struct list_head overlay_list; + +static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name); +} + +static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", + ovl->manager ? ovl->manager->name : "<none>"); +} + +static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf, + size_t size) +{ + int i, r; + struct omap_overlay_manager *mgr = NULL; + struct omap_overlay_manager *old_mgr; + int len = size; + + if (buf[size-1] == '\n') + --len; + + if (len > 0) { + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + mgr = omap_dss_get_overlay_manager(i); + + if (strncmp(buf, mgr->name, len) == 0) + break; + + mgr = NULL; + } + } + + if (len > 0 && mgr == NULL) + return -EINVAL; + + if (mgr) + DSSDBG("manager %s found\n", mgr->name); + + if (mgr == ovl->manager) + return size; + + old_mgr = ovl->manager; + + /* detach old manager */ + if (old_mgr) { + r = ovl->unset_manager(ovl); + if (r) { + DSSERR("detach failed\n"); + return r; + } + + r = old_mgr->apply(old_mgr); + if (r) + return r; + } + + if (mgr) { + r = ovl->set_manager(ovl, mgr); + if (r) { + DSSERR("Failed to attach overlay\n"); + return r; + } + + r = mgr->apply(mgr); + if (r) + return r; + } + + return size; +} + +static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d,%d\n", + ovl->info.width, ovl->info.height); +} + +static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.screen_width); +} + +static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d,%d\n", + ovl->info.pos_x, ovl->info.pos_y); +} + +static ssize_t overlay_position_store(struct omap_overlay *ovl, + const char *buf, size_t size) +{ + int r; + char *last; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + info.pos_x = simple_strtoul(buf, &last, 10); + ++last; + if (last - buf >= size) + return -EINVAL; + + info.pos_y = simple_strtoul(last, &last, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d,%d\n", + ovl->info.out_width, ovl->info.out_height); +} + +static ssize_t overlay_output_size_store(struct omap_overlay *ovl, + const char *buf, size_t size) +{ + int r; + char *last; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + info.out_width = simple_strtoul(buf, &last, 10); + ++last; + if (last - buf >= size) + return -EINVAL; + + info.out_height = simple_strtoul(last, &last, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.enabled); +} + +static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf, + size_t size) +{ + int r; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + info.enabled = simple_strtoul(buf, NULL, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", + ovl->info.global_alpha); +} + +static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, + const char *buf, size_t size) +{ + int r; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + /* Video1 plane does not support global alpha + * to always make it 255 completely opaque + */ + if (ovl->id == OMAP_DSS_VIDEO1) + info.global_alpha = 255; + else + info.global_alpha = simple_strtoul(buf, NULL, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +struct overlay_attribute { + struct attribute attr; + ssize_t (*show)(struct omap_overlay *, char *); + ssize_t (*store)(struct omap_overlay *, const char *, size_t); +}; + +#define OVERLAY_ATTR(_name, _mode, _show, _store) \ + struct overlay_attribute overlay_attr_##_name = \ + __ATTR(_name, _mode, _show, _store) + +static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL); +static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR, + overlay_manager_show, overlay_manager_store); +static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL); +static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL); +static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR, + overlay_position_show, overlay_position_store); +static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR, + overlay_output_size_show, overlay_output_size_store); +static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR, + overlay_enabled_show, overlay_enabled_store); +static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR, + overlay_global_alpha_show, overlay_global_alpha_store); + +static struct attribute *overlay_sysfs_attrs[] = { + &overlay_attr_name.attr, + &overlay_attr_manager.attr, + &overlay_attr_input_size.attr, + &overlay_attr_screen_width.attr, + &overlay_attr_position.attr, + &overlay_attr_output_size.attr, + &overlay_attr_enabled.attr, + &overlay_attr_global_alpha.attr, + NULL +}; + +static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct omap_overlay *overlay; + struct overlay_attribute *overlay_attr; + + overlay = container_of(kobj, struct omap_overlay, kobj); + overlay_attr = container_of(attr, struct overlay_attribute, attr); + + if (!overlay_attr->show) + return -ENOENT; + + return overlay_attr->show(overlay, buf); +} + +static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t size) +{ + struct omap_overlay *overlay; + struct overlay_attribute *overlay_attr; + + overlay = container_of(kobj, struct omap_overlay, kobj); + overlay_attr = container_of(attr, struct overlay_attribute, attr); + + if (!overlay_attr->store) + return -ENOENT; + + return overlay_attr->store(overlay, buf, size); +} + +static struct sysfs_ops overlay_sysfs_ops = { + .show = overlay_attr_show, + .store = overlay_attr_store, +}; + +static struct kobj_type overlay_ktype = { + .sysfs_ops = &overlay_sysfs_ops, + .default_attrs = overlay_sysfs_attrs, +}; + +/* Check if overlay parameters are compatible with display */ +int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev) +{ + struct omap_overlay_info *info; + u16 outw, outh; + u16 dw, dh; + + if (!dssdev) + return 0; + + if (!ovl->info.enabled) + return 0; + + info = &ovl->info; + + if (info->paddr == 0) { + DSSDBG("check_overlay failed: paddr 0\n"); + return -EINVAL; + } + + dssdev->get_resolution(dssdev, &dw, &dh); + + DSSDBG("check_overlay %d: (%d,%d %dx%d -> %dx%d) disp (%dx%d)\n", + ovl->id, + info->pos_x, info->pos_y, + info->width, info->height, + info->out_width, info->out_height, + dw, dh); + + if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { + outw = info->width; + outh = info->height; + } else { + if (info->out_width == 0) + outw = info->width; + else + outw = info->out_width; + + if (info->out_height == 0) + outh = info->height; + else + outh = info->out_height; + } + + if (dw < info->pos_x + outw) { + DSSDBG("check_overlay failed 1: %d < %d + %d\n", + dw, info->pos_x, outw); + return -EINVAL; + } + + if (dh < info->pos_y + outh) { + DSSDBG("check_overlay failed 2: %d < %d + %d\n", + dh, info->pos_y, outh); + return -EINVAL; + } + + if ((ovl->supported_modes & info->color_mode) == 0) { + DSSERR("overlay doesn't support mode %d\n", info->color_mode); + return -EINVAL; + } + + return 0; +} + +static int dss_ovl_set_overlay_info(struct omap_overlay *ovl, + struct omap_overlay_info *info) +{ + int r; + struct omap_overlay_info old_info; + + old_info = ovl->info; + ovl->info = *info; + + if (ovl->manager) { + r = dss_check_overlay(ovl, ovl->manager->device); + if (r) { + ovl->info = old_info; + return r; + } + } + + ovl->info_dirty = true; + + return 0; +} + +static void dss_ovl_get_overlay_info(struct omap_overlay *ovl, + struct omap_overlay_info *info) +{ + *info = ovl->info; +} + +static int dss_ovl_wait_for_go(struct omap_overlay *ovl) +{ + return dss_mgr_wait_for_go_ovl(ovl); +} + +static int omap_dss_set_manager(struct omap_overlay *ovl, + struct omap_overlay_manager *mgr) +{ + if (!mgr) + return -EINVAL; + + if (ovl->manager) { + DSSERR("overlay '%s' already has a manager '%s'\n", + ovl->name, ovl->manager->name); + return -EINVAL; + } + + if (ovl->info.enabled) { + DSSERR("overlay has to be disabled to change the manager\n"); + return -EINVAL; + } + + ovl->manager = mgr; + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + /* XXX: on manual update display, in auto update mode, a bug happens + * here. When an overlay is first enabled on LCD, then it's disabled, + * and the manager is changed to TV, we sometimes get SYNC_LOST_DIGIT + * errors. Waiting before changing the channel_out fixes it. I'm + * guessing that the overlay is still somehow being used for the LCD, + * but I don't understand how or why. */ + msleep(40); + dispc_set_channel_out(ovl->id, mgr->id); + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + return 0; +} + +static int omap_dss_unset_manager(struct omap_overlay *ovl) +{ + int r; + + if (!ovl->manager) { + DSSERR("failed to detach overlay: manager not set\n"); + return -EINVAL; + } + + if (ovl->info.enabled) { + DSSERR("overlay has to be disabled to unset the manager\n"); + return -EINVAL; + } + + r = ovl->wait_for_go(ovl); + if (r) + return r; + + ovl->manager = NULL; + + return 0; +} + +int omap_dss_get_num_overlays(void) +{ + return num_overlays; +} +EXPORT_SYMBOL(omap_dss_get_num_overlays); + +struct omap_overlay *omap_dss_get_overlay(int num) +{ + int i = 0; + struct omap_overlay *ovl; + + list_for_each_entry(ovl, &overlay_list, list) { + if (i++ == num) + return ovl; + } + + return NULL; +} +EXPORT_SYMBOL(omap_dss_get_overlay); + +static void omap_dss_add_overlay(struct omap_overlay *overlay) +{ + ++num_overlays; + list_add_tail(&overlay->list, &overlay_list); +} + +static struct omap_overlay *dispc_overlays[3]; + +void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr) +{ + mgr->num_overlays = 3; + mgr->overlays = dispc_overlays; +} + +#ifdef L4_EXAMPLE +static struct omap_overlay *l4_overlays[1]; +void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr) +{ + mgr->num_overlays = 1; + mgr->overlays = l4_overlays; +} +#endif + +void dss_init_overlays(struct platform_device *pdev) +{ + int i, r; + + INIT_LIST_HEAD(&overlay_list); + + num_overlays = 0; + + for (i = 0; i < 3; ++i) { + struct omap_overlay *ovl; + ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); + + BUG_ON(ovl == NULL); + + switch (i) { + case 0: + ovl->name = "gfx"; + ovl->id = OMAP_DSS_GFX; + ovl->supported_modes = OMAP_DSS_COLOR_GFX_OMAP3; + ovl->caps = OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; + break; + case 1: + ovl->name = "vid1"; + ovl->id = OMAP_DSS_VIDEO1; + ovl->supported_modes = OMAP_DSS_COLOR_VID_OMAP3; + ovl->caps = OMAP_DSS_OVL_CAP_SCALE | + OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; + break; + case 2: + ovl->name = "vid2"; + ovl->id = OMAP_DSS_VIDEO2; + ovl->supported_modes = OMAP_DSS_COLOR_VID_OMAP3; + ovl->caps = OMAP_DSS_OVL_CAP_SCALE | + OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; + break; + } + + ovl->set_manager = &omap_dss_set_manager; + ovl->unset_manager = &omap_dss_unset_manager; + ovl->set_overlay_info = &dss_ovl_set_overlay_info; + ovl->get_overlay_info = &dss_ovl_get_overlay_info; + ovl->wait_for_go = &dss_ovl_wait_for_go; + + omap_dss_add_overlay(ovl); + + r = kobject_init_and_add(&ovl->kobj, &overlay_ktype, + &pdev->dev.kobj, "overlay%d", i); + + if (r) { + DSSERR("failed to create sysfs file\n"); + continue; + } + + dispc_overlays[i] = ovl; + } + +#ifdef L4_EXAMPLE + { + struct omap_overlay *ovl; + ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); + + BUG_ON(ovl == NULL); + + ovl->name = "l4"; + ovl->supported_modes = OMAP_DSS_COLOR_RGB24U; + + ovl->set_manager = &omap_dss_set_manager; + ovl->unset_manager = &omap_dss_unset_manager; + ovl->set_overlay_info = &dss_ovl_set_overlay_info; + ovl->get_overlay_info = &dss_ovl_get_overlay_info; + + omap_dss_add_overlay(ovl); + + r = kobject_init_and_add(&ovl->kobj, &overlay_ktype, + &pdev->dev.kobj, "overlayl4"); + + if (r) + DSSERR("failed to create sysfs file\n"); + + l4_overlays[0] = ovl; + } +#endif +} + +/* connect overlays to the new device, if not already connected. if force + * selected, connect always. */ +void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) +{ + int i; + struct omap_overlay_manager *lcd_mgr; + struct omap_overlay_manager *tv_mgr; + struct omap_overlay_manager *mgr = NULL; + + lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD); + tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV); + + if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) { + if (!lcd_mgr->device || force) { + if (lcd_mgr->device) + lcd_mgr->unset_device(lcd_mgr); + lcd_mgr->set_device(lcd_mgr, dssdev); + mgr = lcd_mgr; + } + } + + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { + if (!tv_mgr->device || force) { + if (tv_mgr->device) + tv_mgr->unset_device(tv_mgr); + tv_mgr->set_device(tv_mgr, dssdev); + mgr = tv_mgr; + } + } + + if (mgr) { + for (i = 0; i < 3; i++) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + if (!ovl->manager || force) { + if (ovl->manager) + omap_dss_unset_manager(ovl); + omap_dss_set_manager(ovl, mgr); + } + } + } +} + +void dss_uninit_overlays(struct platform_device *pdev) +{ + struct omap_overlay *ovl; + + while (!list_empty(&overlay_list)) { + ovl = list_first_entry(&overlay_list, + struct omap_overlay, list); + list_del(&ovl->list); + kobject_del(&ovl->kobj); + kobject_put(&ovl->kobj); + kfree(ovl); + } + + num_overlays = 0; +} +
--
1.6.4
------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now. http://p.sf.net/sfu/bobj-july