OMAP: DSS2: Display Subsystem Driver core
authorTomi Valkeinen <tomi.valkeinen@nokia.com>
Tue, 3 Nov 2009 09:23:50 +0000 (11:23 +0200)
committerTomi Valkeinen <tomi.valkeinen@nokia.com>
Wed, 9 Dec 2009 10:04:34 +0000 (12:04 +0200)
The core files of DSS2. DSS2 commits are split a bit artificially to
make the individual commits smaller, and DSS2 doesn't compile properly
without the rest of the core commits. This shouldn't be a problem, as no
configuration uses DSS2 yet.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com>
arch/arm/plat-omap/include/plat/display.h [new file with mode: 0644]
drivers/video/omap2/Kconfig
drivers/video/omap2/Makefile
drivers/video/omap2/dss/Kconfig [new file with mode: 0644]
drivers/video/omap2/dss/Makefile [new file with mode: 0644]
drivers/video/omap2/dss/core.c [new file with mode: 0644]
drivers/video/omap2/dss/dss.c [new file with mode: 0644]
drivers/video/omap2/dss/dss.h [new file with mode: 0644]

diff --git a/arch/arm/plat-omap/include/plat/display.h b/arch/arm/plat-omap/include/plat/display.h
new file mode 100644 (file)
index 0000000..c66e464
--- /dev/null
@@ -0,0 +1,575 @@
+/*
+ * 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_OMAP2 =
+               OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 |
+               OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 |
+               OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 |
+               OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P,
+
+       OMAP_DSS_COLOR_VID_OMAP2 =
+               OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
+               OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 |
+               OMAP_DSS_COLOR_UYVY,
+
+       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_VID1_OMAP3 =
+               OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 |
+               OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P |
+               OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY,
+
+       OMAP_DSS_COLOR_VID2_OMAP3 =
+               OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 |
+               OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
+               OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 |
+               OMAP_DSS_COLOR_UYVY | OMAP_DSS_COLOR_ARGB32 |
+               OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32,
+};
+
+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,
+       OMAP_DSS_DISPLAY_CAP_TEAR_ELIM          = 1 << 1,
+};
+
+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,
+};
+
+/* clockwise rotation angle */
+enum omap_dss_rotation_angle {
+       OMAP_DSS_ROT_0   = 0,
+       OMAP_DSS_ROT_90  = 1,
+       OMAP_DSS_ROT_180 = 2,
+       OMAP_DSS_ROT_270 = 3,
+};
+
+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;
+               } sdi;
+
+               struct {
+                       u8 clk_lane;
+                       u8 clk_pol;
+                       u8 data1_lane;
+                       u8 data1_pol;
+                       u8 data2_lane;
+                       u8 data2_pol;
+
+                       struct {
+                               u16 regn;
+                               u16 regm;
+                               u16 regm3;
+                               u16 regm4;
+
+                               u16 lp_clk_div;
+
+                               u16 lck_div;
+                               u16 pck_div;
+                       } div;
+
+                       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
index ac8b65057a01aab90acdd72c036721a255697d49..55b4c4265f5794a1116e3d1de38ffbdfbd1b92ab 100644 (file)
@@ -3,3 +3,5 @@ config OMAP2_VRAM
 
 config OMAP2_VRFB
        bool
+
+source "drivers/video/omap2/dss/Kconfig"
index aa3751b3dc907be008b1041bf888b134b24b82c8..ee0644f9d3c1eae5b6791c5df9a8632b12ea735c 100644 (file)
@@ -1,2 +1,4 @@
 obj-$(CONFIG_OMAP2_VRAM) += vram.o
 obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
+
+obj-y += dss/
diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig
new file mode 100644 (file)
index 0000000..71d8dec
--- /dev/null
@@ -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 (file)
index 0000000..980c72c
--- /dev/null
@@ -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 (file)
index 0000000..29497a0
--- /dev/null
@@ -0,0 +1,919 @@
+/*
+ * 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 <plat/display.h>
+#include <plat/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 */
+static void core_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, "- CORE -\n");
+
+       seq_printf(s, "internal clk count\t\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)
+{
+       core_dump_clocks(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");
+       dss_disable_all_devices();
+}
+
+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/dss.c b/drivers/video/omap2/dss/dss.c
new file mode 100644 (file)
index 0000000..9b05ee6
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * 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 <linux/clk.h>
+
+#include <plat/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;
+
+       struct clk      *dpll4_m4_ck;
+
+       unsigned long   cache_req_pck;
+       unsigned long   cache_prate;
+       struct dss_clock_info cache_dss_cinfo;
+       struct dispc_clock_info cache_dispc_cinfo;
+
+       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);
+}
+
+int dss_sdi_enable(void)
+{
+       unsigned long timeout;
+
+       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 */
+       timeout = jiffies + msecs_to_jiffies(500);
+       while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) {
+               if (time_after_eq(jiffies, timeout)) {
+                       DSSERR("PLL lock request timed out\n");
+                       goto err1;
+               }
+       }
+
+       /* Clearing PLL_GO bit */
+       REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28);
+
+       /* Waiting for PLL to lock */
+       timeout = jiffies + msecs_to_jiffies(500);
+       while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) {
+               if (time_after_eq(jiffies, timeout)) {
+                       DSSERR("PLL lock timed out\n");
+                       goto err1;
+               }
+       }
+
+       dispc_lcd_enable_signal(1);
+
+       /* Waiting for SDI reset to complete */
+       timeout = jiffies + msecs_to_jiffies(500);
+       while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) {
+               if (time_after_eq(jiffies, timeout)) {
+                       DSSERR("SDI reset timed out\n");
+                       goto err2;
+               }
+       }
+
+       return 0;
+
+ err2:
+       dispc_lcd_enable_signal(0);
+ err1:
+       /* Reset SDI PLL */
+       REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
+
+       dispc_pck_free_enable(0);
+
+       return -ETIMEDOUT;
+}
+
+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_clocks(struct seq_file *s)
+{
+       unsigned long dpll4_ck_rate;
+       unsigned long dpll4_m4_ck_rate;
+
+       dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+       dpll4_m4_ck_rate = clk_get_rate(dss.dpll4_m4_ck);
+
+       seq_printf(s, "- DSS -\n");
+
+       seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate);
+
+       seq_printf(s, "dss1_alwon_fclk = %lu / %lu * 2 = %lu\n",
+                       dpll4_ck_rate,
+                       dpll4_ck_rate / dpll4_m4_ck_rate,
+                       dss_clk_get_rate(DSS_CLK_FCK1));
+
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+}
+
+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);
+}
+
+/* calculate clock rates using dividers in cinfo */
+int dss_calc_clock_rates(struct dss_clock_info *cinfo)
+{
+       unsigned long prate;
+
+       if (cinfo->fck_div > 16 || cinfo->fck_div == 0)
+               return -EINVAL;
+
+       prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+
+       cinfo->fck = prate / cinfo->fck_div;
+
+       return 0;
+}
+
+int dss_set_clock_div(struct dss_clock_info *cinfo)
+{
+       unsigned long prate;
+       int r;
+
+       if (cpu_is_omap34xx()) {
+               prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+               DSSDBG("dpll4_m4 = %ld\n", prate);
+
+               r = clk_set_rate(dss.dpll4_m4_ck, prate / cinfo->fck_div);
+               if (r)
+                       return r;
+       }
+
+       DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div);
+
+       return 0;
+}
+
+int dss_get_clock_div(struct dss_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(dss.dpll4_m4_ck));
+               cinfo->fck_div = prate / (cinfo->fck / 2);
+       } else {
+               cinfo->fck_div = 0;
+       }
+
+       return 0;
+}
+
+unsigned long dss_get_dpll4_rate(void)
+{
+       if (cpu_is_omap34xx())
+               return clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+       else
+               return 0;
+}
+
+int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
+               struct dss_clock_info *dss_cinfo,
+               struct dispc_clock_info *dispc_cinfo)
+{
+       unsigned long prate;
+       struct dss_clock_info best_dss;
+       struct dispc_clock_info best_dispc;
+
+       unsigned long fck;
+
+       u16 fck_div;
+
+       int match = 0;
+       int min_fck_per_pck;
+
+       prate = dss_get_dpll4_rate();
+
+       fck = dss_clk_get_rate(DSS_CLK_FCK1);
+       if (req_pck == dss.cache_req_pck &&
+                       ((cpu_is_omap34xx() && prate == dss.cache_prate) ||
+                        dss.cache_dss_cinfo.fck == fck)) {
+               DSSDBG("dispc clock info found from cache.\n");
+               *dss_cinfo = dss.cache_dss_cinfo;
+               *dispc_cinfo = dss.cache_dispc_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(&best_dss, 0, sizeof(best_dss));
+       memset(&best_dispc, 0, sizeof(best_dispc));
+
+       if (cpu_is_omap24xx()) {
+               struct dispc_clock_info cur_dispc;
+               /* XXX can we change the clock on omap2? */
+               fck = dss_clk_get_rate(DSS_CLK_FCK1);
+               fck_div = 1;
+
+               dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
+               match = 1;
+
+               best_dss.fck = fck;
+               best_dss.fck_div = fck_div;
+
+               best_dispc = cur_dispc;
+
+               goto found;
+       } else if (cpu_is_omap34xx()) {
+               for (fck_div = 16; fck_div > 0; --fck_div) {
+                       struct dispc_clock_info cur_dispc;
+
+                       fck = prate / fck_div * 2;
+
+                       if (fck > DISPC_MAX_FCK)
+                               continue;
+
+                       if (min_fck_per_pck &&
+                                       fck < req_pck * min_fck_per_pck)
+                               continue;
+
+                       match = 1;
+
+                       dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
+
+                       if (abs(cur_dispc.pck - req_pck) <
+                                       abs(best_dispc.pck - req_pck)) {
+
+                               best_dss.fck = fck;
+                               best_dss.fck_div = fck_div;
+
+                               best_dispc = cur_dispc;
+
+                               if (cur_dispc.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 (dss_cinfo)
+               *dss_cinfo = best_dss;
+       if (dispc_cinfo)
+               *dispc_cinfo = best_dispc;
+
+       dss.cache_req_pck = req_pck;
+       dss.cache_prate = prate;
+       dss.cache_dss_cinfo = best_dss;
+       dss.cache_dispc_cinfo = best_dispc;
+
+       return 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;
+       }
+
+       if (cpu_is_omap34xx()) {
+               dss.dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck");
+               if (IS_ERR(dss.dpll4_m4_ck)) {
+                       DSSERR("Failed to get dpll4_m4_ck\n");
+                       r = PTR_ERR(dss.dpll4_m4_ck);
+                       goto fail2;
+               }
+       }
+
+       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;
+
+fail2:
+       free_irq(INT_24XX_DSS_IRQ, NULL);
+fail1:
+       iounmap(dss.base);
+fail0:
+       return r;
+}
+
+void dss_exit(void)
+{
+       if (cpu_is_omap34xx())
+               clk_put(dss.dpll4_m4_ck);
+
+       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 (file)
index 0000000..8da5ac4
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * 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 dss_clock_info {
+       /* rates that we get with dividers below */
+       unsigned long fck;
+
+       /* dividers */
+       u16 fck_div;
+};
+
+struct dispc_clock_info {
+       /* rates that we get with dividers below */
+       unsigned long lck;
+       unsigned long pck;
+
+       /* dividers */
+       u16 lck_div;
+       u16 pck_div;
+};
+
+struct dsi_clock_info {
+       /* rates that we get with dividers below */
+       unsigned long fint;
+       unsigned long clkin4ddr;
+       unsigned long clkin;
+       unsigned long dsi1_pll_fclk;
+       unsigned long dsi2_pll_fclk;
+
+       unsigned long lp_clk;
+
+       /* dividers */
+       u16 regn;
+       u16 regm;
+       u16 regm3;
+       u16 regm4;
+
+       u16 lp_clk_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_disable_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);
+int 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);
+
+unsigned long dss_get_dpll4_rate(void);
+int dss_calc_clock_rates(struct dss_clock_info *cinfo);
+int dss_set_clock_div(struct dss_clock_info *cinfo);
+int dss_get_clock_div(struct dss_clock_info *cinfo);
+int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
+               struct dss_clock_info *dss_cinfo,
+               struct dispc_clock_info *dispc_cinfo);
+
+/* 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);
+int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo);
+int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck,
+               struct dsi_clock_info *cinfo,
+               struct dispc_clock_info *dispc_cinfo);
+int dsi_pll_init(struct omap_dss_device *dssdev, 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 dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
+               struct dispc_clock_info *cinfo);
+int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
+               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);
+
+
+/* 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