summaryrefslogtreecommitdiff
path: root/recipes/linux/linux-omap-pm/dss2/0108-DSS2-DSI-Rewrite-of-the-DSI-update-and-cmd-queue.patch
diff options
context:
space:
mode:
authorKoen Kooi <koen@openembedded.org>2009-07-30 11:19:10 +0200
committerKoen Kooi <koen@openembedded.org>2009-07-30 11:19:10 +0200
commitf2e6ca6bfa79d166ee72224bbb95ca1a71125d43 (patch)
treeb2facdc46191d38b9917a7d390c44f668f19183f /recipes/linux/linux-omap-pm/dss2/0108-DSS2-DSI-Rewrite-of-the-DSI-update-and-cmd-queue.patch
parent0501fbe7b6f99eddc8ee9b0d3dab9ebac4722166 (diff)
linux-omap-pm git: add patch for fix musb oops and dss2 patches
Diffstat (limited to 'recipes/linux/linux-omap-pm/dss2/0108-DSS2-DSI-Rewrite-of-the-DSI-update-and-cmd-queue.patch')
-rw-r--r--recipes/linux/linux-omap-pm/dss2/0108-DSS2-DSI-Rewrite-of-the-DSI-update-and-cmd-queue.patch1860
1 files changed, 1860 insertions, 0 deletions
diff --git a/recipes/linux/linux-omap-pm/dss2/0108-DSS2-DSI-Rewrite-of-the-DSI-update-and-cmd-queue.patch b/recipes/linux/linux-omap-pm/dss2/0108-DSS2-DSI-Rewrite-of-the-DSI-update-and-cmd-queue.patch
new file mode 100644
index 0000000000..e872bfdda9
--- /dev/null
+++ b/recipes/linux/linux-omap-pm/dss2/0108-DSS2-DSI-Rewrite-of-the-DSI-update-and-cmd-queue.patch
@@ -0,0 +1,1860 @@
+From 218fd1516751829b239e4c64f71291d291c6bc2c Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+Date: Thu, 11 Jun 2009 09:11:45 +0300
+Subject: [PATCH 108/146] DSS2: DSI: Rewrite of the DSI update and cmd queue
+
+---
+ drivers/video/omap2/dss/dsi.c | 1157 +++++++++++--------------------------
+ drivers/video/omap2/dss/dss.h | 3 +
+ drivers/video/omap2/dss/manager.c | 45 ++-
+ drivers/video/omap2/dss/overlay.c | 65 ++-
+ 4 files changed, 419 insertions(+), 851 deletions(-)
+
+diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
+index b0294b8..f98da6f 100644
+--- a/drivers/video/omap2/dss/dsi.c
++++ b/drivers/video/omap2/dss/dsi.c
+@@ -26,12 +26,12 @@
+ #include <linux/err.h>
+ #include <linux/interrupt.h>
+ #include <linux/delay.h>
+-#include <linux/workqueue.h>
+ #include <linux/mutex.h>
+ #include <linux/seq_file.h>
+-#include <linux/kfifo.h>
+ #include <linux/platform_device.h>
+ #include <linux/regulator/consumer.h>
++#include <linux/kthread.h>
++#include <linux/wait.h>
+
+ #include <mach/board.h>
+ #include <mach/display.h>
+@@ -192,60 +192,10 @@ enum fifo_size {
+ DSI_FIFO_SIZE_128 = 4,
+ };
+
+-#define DSI_CMD_FIFO_LEN 16
+-
+-struct dsi_cmd_update {
+- int bytespp;
+- u16 x;
+- u16 y;
+- u16 w;
+- u16 h;
+-};
+-
+-struct dsi_cmd_mem_read {
+- void *buf;
+- size_t size;
+- u16 x;
+- u16 y;
+- u16 w;
+- u16 h;
+- size_t *ret_size;
+- struct completion *completion;
+-};
+-
+-struct dsi_cmd_test {
+- int test_num;
+- int *result;
+- struct completion *completion;
+-};
+-
+-enum dsi_cmd {
+- DSI_CMD_UPDATE,
+- DSI_CMD_AUTOUPDATE,
+- DSI_CMD_SYNC,
+- DSI_CMD_MEM_READ,
+- DSI_CMD_TEST,
+- DSI_CMD_SET_TE,
+- DSI_CMD_SET_UPDATE_MODE,
+- DSI_CMD_SET_ROTATE,
+- DSI_CMD_SET_MIRROR,
+-};
+-
+-struct dsi_cmd_item {
+- struct omap_dss_device *dssdev;
+-
+- enum dsi_cmd cmd;
+-
+- union {
+- struct dsi_cmd_update r;
+- struct completion *sync;
+- struct dsi_cmd_mem_read mem_read;
+- struct dsi_cmd_test test;
+- int te;
+- enum omap_dss_update_mode update_mode;
+- int rotate;
+- int mirror;
+- } u;
++struct dsi_update_region {
++ bool dirty;
++ u16 x, y, w, h;
++ struct omap_dss_device *device;
+ };
+
+ static struct
+@@ -272,30 +222,24 @@ static struct
+
+ struct completion bta_completion;
+
+- struct work_struct framedone_work;
+- struct work_struct process_work;
+- struct delayed_work framedone_timeout_work;
+- struct workqueue_struct *workqueue;
++ struct task_struct *thread;
++ wait_queue_head_t waitqueue;
++
++ spinlock_t update_lock;
++ bool framedone_received;
++ struct dsi_update_region update_region;
++ struct dsi_update_region active_update_region;
++ struct completion update_completion;
+
+ enum omap_dss_update_mode user_update_mode;
+- enum omap_dss_update_mode target_update_mode;
+ enum omap_dss_update_mode update_mode;
+- bool use_te;
++ bool te_enabled;
+ bool use_ext_te;
+- int framedone_scheduled; /* helps to catch strange framedone bugs */
+
+ unsigned long cache_req_pck;
+ unsigned long cache_clk_freq;
+ struct dsi_clock_info cache_cinfo;
+
+- struct kfifo *cmd_fifo;
+- spinlock_t cmd_lock;
+- struct completion cmd_done;
+- atomic_t cmd_fifo_full;
+- atomic_t cmd_pending;
+-
+- bool autoupdate_setup;
+-
+ u32 errors;
+ spinlock_t errors_lock;
+ #ifdef DEBUG
+@@ -303,14 +247,7 @@ static struct
+ ktime_t perf_start_time;
+ ktime_t perf_start_time_auto;
+ int perf_measure_frames;
+-
+- struct {
+- int x, y, w, h;
+- int bytespp;
+- } update_region;
+-
+ #endif
+- int debug_process;
+ int debug_read;
+ int debug_write;
+ } dsi;
+@@ -320,11 +257,6 @@ static unsigned int dsi_perf;
+ module_param_named(dsi_perf, dsi_perf, bool, 0644);
+ #endif
+
+-static void dsi_process_cmd_fifo(struct work_struct *work);
+-static void dsi_push_update(struct omap_dss_device *dssdev,
+- int x, int y, int w, int h);
+-static void dsi_push_autoupdate(struct omap_dss_device *dssdev);
+-
+ static inline void dsi_write_reg(const struct dsi_reg idx, u32 val)
+ {
+ __raw_writel(val, dsi.base + idx.idx);
+@@ -370,23 +302,23 @@ static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum,
+ }
+
+ #ifdef DEBUG
+-static void perf_mark_setup(void)
++static void dsi_perf_mark_setup(void)
+ {
+ dsi.perf_setup_time = ktime_get();
+ }
+
+-static void perf_mark_start(void)
++static void dsi_perf_mark_start(void)
+ {
+ dsi.perf_start_time = ktime_get();
+ }
+
+-static void perf_mark_start_auto(void)
++static void dsi_perf_mark_start_auto(void)
+ {
+ dsi.perf_measure_frames = 0;
+ dsi.perf_start_time_auto = ktime_get();
+ }
+
+-static void perf_show(const char *name)
++static void dsi_perf_show(const char *name)
+ {
+ ktime_t t, setup_time, trans_time;
+ u32 total_bytes;
+@@ -412,9 +344,9 @@ static void perf_show(const char *name)
+
+ total_us = setup_us + trans_us;
+
+- total_bytes = dsi.update_region.w *
+- dsi.update_region.h *
+- dsi.update_region.bytespp;
++ total_bytes = dsi.active_update_region.w *
++ dsi.active_update_region.h *
++ dsi.active_update_region.device->ctrl.pixel_size / 8;
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) {
+ static u32 s_total_trans_us, s_total_setup_us;
+@@ -465,7 +397,7 @@ static void perf_show(const char *name)
+ s_total_trans_us = 0;
+ s_min_trans_us = 0xffffffff;
+ s_max_trans_us = 0;
+- perf_mark_start_auto();
++ dsi_perf_mark_start_auto();
+ } else {
+ printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), "
+ "%u bytes, %u kbytes/sec\n",
+@@ -479,9 +411,10 @@ static void perf_show(const char *name)
+ }
+ }
+ #else
+-#define perf_mark_setup()
+-#define perf_mark_start()
+-#define perf_show(x)
++#define dsi_perf_mark_setup()
++#define dsi_perf_mark_start()
++#define dsi_perf_mark_start_auto()
++#define dsi_perf_show(x)
+ #endif
+
+ static void print_irq_status(u32 status)
+@@ -2464,7 +2397,7 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev,
+ int max_data_per_packet;
+ int max_pixels_per_packet;
+ int pixels_left;
+- int bytespp = 3;
++ int bytespp = dssdev->ctrl.pixel_size / 8;
+ int scr_width;
+ u32 __iomem *data;
+ int start_offset;
+@@ -2508,26 +2441,12 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev,
+
+ DSSDBG("max_pixels_per_packet %d\n", max_pixels_per_packet);
+
+- dsi_bus_lock();
+-
+- dssdev->driver->setup_update(dssdev, x, y, w, h);
+-
+ pixels_left = w * h;
+
+ DSSDBG("total pixels %d\n", pixels_left);
+
+ data += start_offset;
+
+-#ifdef DEBUG
+- dsi.update_region.x = x;
+- dsi.update_region.y = y;
+- dsi.update_region.w = w;
+- dsi.update_region.h = h;
+- dsi.update_region.bytespp = bytespp;
+-#endif
+-
+- perf_mark_start();
+-
+ while (pixels_left > 0) {
+ /* 0x2c = write_memory_start */
+ /* 0x3c = write_memory_continue */
+@@ -2546,7 +2465,6 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev,
+ DSSERR("fifo stalls overflow, pixels left %d\n",
+ pixels_left);
+ dsi_if_enable(0);
+- dsi_bus_unlock();
+ return -EIO;
+ }
+ }
+@@ -2559,7 +2477,6 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev,
+ DSSERR("fifo stalls overflow, pixels left %d\n",
+ pixels_left);
+ dsi_if_enable(0);
+- dsi_bus_unlock();
+ return -EIO;
+ }
+ }
+@@ -2570,7 +2487,6 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev,
+ DSSERR("fifo stalls overflow, pixels left %d\n",
+ pixels_left);
+ dsi_if_enable(0);
+- dsi_bus_unlock();
+ return -EIO;
+ }
+ }
+@@ -2601,64 +2517,21 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev,
+ DSI_FLUSH(0);
+ }
+
+- perf_show("L4");
+-
+- dsi_bus_unlock();
+-
+ return 0;
+ }
+
+-static void dsi_setup_update_dispc(struct omap_dss_device *dssdev,
+- u16 x, u16 y, u16 w, u16 h)
+-{
+- DSSDBG("dsi_setup_update_dispc(%d,%d %dx%d)\n",
+- x, y, w, h);
+-
+-#ifdef DEBUG
+- dsi.update_region.x = x;
+- dsi.update_region.y = y;
+- dsi.update_region.w = w;
+- dsi.update_region.h = h;
+- dsi.update_region.bytespp = 3; /* XXX */
+-#endif
+-
+- dispc_setup_partial_planes(dssdev, &x, &y, &w, &h);
+-
+- dispc_set_lcd_size(w, h);
+-}
+-
+-static void dsi_setup_autoupdate_dispc(struct omap_dss_device *dssdev)
+-{
+- u16 w, h;
+-
+- dssdev->get_resolution(dssdev, &w, &h);
+-
+-#ifdef DEBUG
+- dsi.update_region.x = 0;
+- dsi.update_region.y = 0;
+- dsi.update_region.w = w;
+- dsi.update_region.h = h;
+- dsi.update_region.bytespp = 3; /* XXX */
+-#endif
+-
+- /* the overlay settings may not have been applied, if we were in manual
+- * mode earlier, so do it here */
+- dssdev->manager->apply(dssdev->manager);
+-
+- dispc_set_lcd_size(w, h);
+-
+- dsi.autoupdate_setup = 0;
+-}
+-
+ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
+ u16 x, u16 y, u16 w, u16 h)
+ {
+- int bytespp = 3;
++ int bytespp = dssdev->ctrl.pixel_size / 8;
+ int len;
+ int total_len;
+ int packet_payload;
+ int packet_len;
+ u32 l;
++ bool use_te_trigger;
++
++ use_te_trigger = dsi.te_enabled && !dsi.use_ext_te;
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL)
+ DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n",
+@@ -2677,24 +2550,15 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
+ if (len % packet_payload)
+ total_len += (len % packet_payload) + 1;
+
+- dsi_bus_lock();
+-
+- dssdev->driver->setup_update(dssdev, x, y, w, h);
+-
+- if (dsi.use_ext_te && dssdev->wait_for_te)
+- dssdev->wait_for_te(dssdev);
+-
+ if (0)
+ dsi_vc_print_status(1);
+
+- perf_mark_start();
+-
+ l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */
+ dsi_write_reg(DSI_VC_TE(1), l);
+
+ dsi_vc_write_long_header(1, DSI_DT_DCS_LONG_WRITE, packet_len, 0);
+
+- if (dsi.use_te)
++ if (use_te_trigger)
+ l = FLD_MOD(l, 1, 30, 30); /* TE_EN */
+ else
+ l = FLD_MOD(l, 1, 31, 31); /* TE_START */
+@@ -2702,62 +2566,81 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
+
+ dispc_disable_sidle();
+
+- queue_delayed_work(dsi.workqueue, &dsi.framedone_timeout_work,
+- msecs_to_jiffies(1000));
+-
+ dispc_enable_lcd_out(1);
+
+- if (dsi.use_te)
++ if (use_te_trigger)
+ dsi_vc_send_bta(1);
+ }
+
+-static void framedone_timeout_callback(struct work_struct *work)
++static void dsi_framedone_irq_callback(void *data, u32 mask)
+ {
+- DSSERR("framedone timeout\n");
+-
+- dispc_enable_lcd_out(0);
++ dispc_enable_sidle();
+
+- /* XXX TODO: cancel the transfer properly */
++ dsi.framedone_received = true;
++ wake_up(&dsi.waitqueue);
++}
+
+- dsi_bus_unlock();
++static void dsi_set_update_region(struct omap_dss_device *dssdev,
++ u16 x, u16 y, u16 w, u16 h)
++{
++ spin_lock(&dsi.update_lock);
++ if (dsi.update_region.dirty) {
++ dsi.update_region.x = min(x, dsi.update_region.x);
++ dsi.update_region.y = min(y, dsi.update_region.y);
++ dsi.update_region.w = max(w, dsi.update_region.w);
++ dsi.update_region.h = max(h, dsi.update_region.h);
++ } else {
++ dsi.update_region.x = x;
++ dsi.update_region.y = y;
++ dsi.update_region.w = w;
++ dsi.update_region.h = h;
++ }
+
+- /* Schedule, so that other threads that want dsi-bus-lock can get it.
+- * Otherwise with autoupdate we may be holding it all the time */
+- schedule();
++ dsi.update_region.device = dssdev;
++ dsi.update_region.dirty = true;
+
+- /* XXX check that fifo is not full. otherwise we would sleep and never
+- * get to process_cmd_fifo below */
+- /* We check for target_update_mode, not update_mode. No reason to push
+- * new updates if we're turning auto update off */
+- if (dsi.target_update_mode == OMAP_DSS_UPDATE_AUTO)
+- dsi_push_autoupdate(dsi.vc[1].dssdev);
++ spin_unlock(&dsi.update_lock);
+
+- atomic_set(&dsi.cmd_pending, 0);
+- dsi_process_cmd_fifo(NULL);
+ }
+
+-static void framedone_callback(void *data, u32 mask)
++static void dsi_start_auto_update(struct omap_dss_device *dssdev)
+ {
+- if (dsi.framedone_scheduled) {
+- DSSERR("Framedone already scheduled. Bogus FRAMEDONE IRQ?\n");
+- return;
+- }
++ u16 w, h;
++
++ DSSDBG("starting auto update\n");
+
+- cancel_delayed_work(&dsi.framedone_timeout_work);
++ /* In automatic mode the overlay settings are applied like on DPI/SDI.
++ * The overlay settings may not have been applied, if we were in manual
++ * mode earlier, so do it here */
++ dssdev->manager->apply(dssdev->manager);
+
+- dispc_enable_sidle();
++ dssdev->get_resolution(dssdev, &w, &h);
+
+- dsi.framedone_scheduled = 1;
++ dsi_set_update_region(dssdev, 0, 0, w, h);
+
+- /* We get FRAMEDONE when DISPC has finished sending pixels and turns
+- * itself off. However, DSI still has the pixels in its buffers, and
+- * is sending the data. Thus we have to wait until we can do a new
+- * transfer or turn the clocks off. We do that in a separate work
+- * func. */
+- queue_work(dsi.workqueue, &dsi.framedone_work);
++ dsi_perf_mark_start_auto();
++
++ wake_up(&dsi.waitqueue);
+ }
+
+-static void framedone_worker(struct work_struct *work)
++static int dsi_set_te(struct omap_dss_device *dssdev, bool enable)
++{
++ dssdev->driver->enable_te(dssdev, enable);
++
++ if (!dsi.use_ext_te) {
++ if (enable) {
++ /* disable LP_RX_TO, so that we can receive TE. Time
++ * to wait for TE is longer than the timer allows */
++ REG_FLD_MOD(DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */
++ } else {
++ REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
++ }
++ }
++
++ return 0;
++}
++
++static void dsi_handle_framedone(void)
+ {
+ u32 l;
+ unsigned long tmo;
+@@ -2765,17 +2648,21 @@ static void framedone_worker(struct work_struct *work)
+
+ l = REG_GET(DSI_VC_TE(1), 23, 0); /* TE_SIZE */
+
+- /* There shouldn't be much stuff in DSI buffers, if any, so we'll
+- * just busyloop */
++ /* We get FRAMEDONE when DISPC has finished sending pixels and turns
++ * itself off. However, DSI still has the pixels in its buffers, and is
++ * sending the data. Thus we have to wait until we can do a new
++ * transfer or turn the clocks off. There shouldn't be much stuff in
++ * DSI buffers, if any, so we'll just busyloop */
+ if (l > 0) {
+ tmo = jiffies + msecs_to_jiffies(50);
+ while (REG_GET(DSI_VC_TE(1), 23, 0) > 0) { /* TE_SIZE */
+ i++;
+ if (time_after(jiffies, tmo)) {
+- DSSERR("timeout waiting TE_SIZE to zero\n");
++ DSSERR("timeout waiting TE_SIZE to zero: %u\n",
++ REG_GET(DSI_VC_TE(1), 23, 0));
+ break;
+ }
+- cpu_relax();
++ schedule();
+ }
+ }
+
+@@ -2785,8 +2672,6 @@ static void framedone_worker(struct work_struct *work)
+ if (REG_GET(DSI_VC_TE(1), 31, 31))
+ DSSERR("TE_START not zero\n");
+
+- perf_show("DISPC");
+-
+ if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL)
+ DSSDBG("FRAMEDONE\n");
+
+@@ -2801,543 +2686,145 @@ static void framedone_worker(struct work_struct *work)
+ #ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC
+ dispc_fake_vsync_irq();
+ #endif
+- dsi.framedone_scheduled = 0;
+-
+- dsi_bus_unlock();
+-
+- /* Schedule, so that other threads that want dsi-bus-lock can get it.
+- * Otherwise with autoupdate we may be holding it all the time */
+- schedule();
+-
+- /* XXX check that fifo is not full. otherwise we would sleep and never
+- * get to process_cmd_fifo below */
+- /* We check for target_update_mode, not update_mode. No reason to push
+- * new updates if we're turning auto update off */
+- if (dsi.target_update_mode == OMAP_DSS_UPDATE_AUTO)
+- dsi_push_autoupdate(dsi.vc[1].dssdev);
+-
+- atomic_set(&dsi.cmd_pending, 0);
+- dsi_process_cmd_fifo(NULL);
+ }
+
+-static void dsi_start_auto_update(struct omap_dss_device *dssdev)
+-{
+- DSSDBG("starting auto update\n");
+-
+- dsi.autoupdate_setup = 1;
+-
+- dsi_push_autoupdate(dssdev);
+-
+- perf_mark_start_auto();
+-}
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-/* FIFO functions */
+-
+-static void dsi_signal_fifo_waiters(void)
++static int dsi_update_thread(void *data)
+ {
+- if (atomic_read(&dsi.cmd_fifo_full) > 0) {
+- DSSDBG("SIGNALING: Fifo not full for waiter!\n");
+- complete(&dsi.cmd_done);
+- atomic_dec(&dsi.cmd_fifo_full);
+- }
+-}
++ unsigned long timeout;
++ struct omap_dss_device *device;
++ u16 x, y, w, h;
+
+-/* returns 1 for async op, and 0 for sync op */
+-static int dsi_do_update(struct omap_dss_device *dssdev,
+- struct dsi_cmd_update *upd)
+-{
+- int r;
+- u16 x = upd->x, y = upd->y, w = upd->w, h = upd->h;
+- u16 dw, dh;
+-
+- if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED)
+- return 0;
+-
+- if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+- return 0;
+-
+- dssdev->get_resolution(dssdev, &dw, &dh);
+- if (x > dw || y > dh)
+- return 0;
+-
+- if (x + w > dw)
+- w = dw - x;
+-
+- if (y + h > dh)
+- h = dh - y;
+-
+- DSSDBGF("%d,%d %dx%d", x, y, w, h);
+-
+- perf_mark_setup();
+-
+- if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+- dsi_setup_update_dispc(dssdev, x, y, w, h);
+- dsi_update_screen_dispc(dssdev, x, y, w, h);
+- return 1;
+- } else {
+- r = dsi_update_screen_l4(dssdev, x, y, w, h);
+- if (r)
+- DSSERR("L4 update failed\n");
+- return 0;
+- }
+-}
+-
+-/* returns 1 for async op, and 0 for sync op */
+-static int dsi_do_autoupdate(struct omap_dss_device *dssdev)
+-{
+- int r;
+- u16 w, h;
+-
+- if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED)
+- return 0;
+-
+- if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+- return 0;
+-
+- dssdev->get_resolution(dssdev, &w, &h);
+-
+- perf_mark_setup();
+-
+- if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+- if (dsi.autoupdate_setup)
+- dsi_setup_autoupdate_dispc(dssdev);
+- dsi_update_screen_dispc(dssdev, 0, 0, w, h);
+- return 1;
+- } else {
+- r = dsi_update_screen_l4(dssdev, 0, 0, w, h);
+- if (r)
+- DSSERR("L4 update failed\n");
+- return 0;
+- }
+-}
+-
+-static void dsi_do_cmd_mem_read(struct omap_dss_device *dssdev,
+- struct dsi_cmd_mem_read *mem_read)
+-{
+- int r;
+-
+- dsi_bus_lock();
+-
+- r = dssdev->driver->memory_read(dssdev,
+- mem_read->buf,
+- mem_read->size,
+- mem_read->x,
+- mem_read->y,
+- mem_read->w,
+- mem_read->h);
+-
+- dsi_bus_unlock();
+-
+- *mem_read->ret_size = (size_t)r;
+- complete(mem_read->completion);
+-}
+-
+-static void dsi_do_cmd_test(struct omap_dss_device *dssdev,
+- struct dsi_cmd_test *test)
+-{
+- int r = 0;
+-
+- DSSDBGF("");
+-
+- if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+- return;
+-
+- dsi_bus_lock();
+-
+- /* run test first in low speed mode */
+- dsi_vc_enable_hs(0, 0);
+-
+- if (dssdev->driver->run_test) {
+- r = dssdev->driver->run_test(dssdev, test->test_num);
+- if (r)
+- goto end;
+- }
+-
+- /* then in high speed */
+- dsi_vc_enable_hs(0, 1);
+-
+- if (dssdev->driver->run_test) {
+- r = dssdev->driver->run_test(dssdev, test->test_num);
+- if (r)
+- goto end;
+- }
+-
+-end:
+- dsi_vc_enable_hs(0, 1);
+-
+- dsi_bus_unlock();
+-
+- *test->result = r;
+- complete(test->completion);
+-
+- DSSDBG("test end\n");
+-}
+-
+-static void dsi_do_cmd_set_te(struct omap_dss_device *dssdev, bool enable)
+-{
+- if (!dssdev->phy.dsi.ext_te)
+- dsi.use_te = enable;
+- else
+- dsi.use_ext_te = enable;
+-
+- if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+- return;
+-
+- dsi_bus_lock();
+- dssdev->driver->enable_te(dssdev, enable);
+- dsi_bus_unlock();
+-
+- if (!dssdev->phy.dsi.ext_te) {
+- if (enable) {
+- /* disable LP_RX_TO, so that we can receive TE. Time
+- * to wait for TE is longer than the timer allows */
+- REG_FLD_MOD(DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */
+- } else {
+- REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
+- }
+- }
+-}
+-
+-static void dsi_do_cmd_set_update_mode(struct omap_dss_device *dssdev,
+- enum omap_dss_update_mode mode)
+-{
+- dsi.update_mode = mode;
+-
+- if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+- return;
+-
+- if (mode == OMAP_DSS_UPDATE_AUTO)
+- dsi_start_auto_update(dssdev);
+-}
+-
+-static void dsi_process_cmd_fifo(struct work_struct *work)
+-{
+- int len;
+- struct dsi_cmd_item p;
+- unsigned long flags;
+- struct omap_dss_device *dssdev;
+- int exit = 0;
+-
+- if (dsi.debug_process)
+- DSSDBGF("");
+-
+- if (atomic_cmpxchg(&dsi.cmd_pending, 0, 1) == 1) {
+- if (dsi.debug_process)
+- DSSDBG("cmd pending, skip process\n");
+- return;
+- }
+-
+- while (!exit) {
+- spin_lock_irqsave(dsi.cmd_fifo->lock, flags);
+-
+- len = __kfifo_get(dsi.cmd_fifo, (unsigned char *)&p,
+- sizeof(p));
+- if (len == 0) {
+- if (dsi.debug_process)
+- DSSDBG("nothing more in fifo, atomic clear\n");
+- atomic_set(&dsi.cmd_pending, 0);
+- spin_unlock_irqrestore(dsi.cmd_fifo->lock, flags);
+- break;
+- }
+-
+- spin_unlock_irqrestore(dsi.cmd_fifo->lock, flags);
+-
+- BUG_ON(len != sizeof(p));
+-
+- dssdev = p.dssdev;
+-
+- if (dsi.debug_process)
+- DSSDBG("processing cmd %d\n", p.cmd);
+-
+- switch (p.cmd) {
+- case DSI_CMD_UPDATE:
+- if (dsi_do_update(dssdev, &p.u.r)) {
+- if (dsi.debug_process)
+- DSSDBG("async update\n");
+- exit = 1;
+- } else {
+- if (dsi.debug_process)
+- DSSDBG("sync update\n");
+- }
+- break;
+-
+- case DSI_CMD_AUTOUPDATE:
+- if (dsi_do_autoupdate(dssdev)) {
+- if (dsi.debug_process)
+- DSSDBG("async autoupdate\n");
+- exit = 1;
+- } else {
+- if (dsi.debug_process)
+- DSSDBG("sync autoupdate\n");
+- }
+- break;
+-
+- case DSI_CMD_SYNC:
+- if (dsi.debug_process)
+- DSSDBG("Signaling SYNC done!\n");
+- complete(p.u.sync);
+- break;
+-
+- case DSI_CMD_MEM_READ:
+- dsi_do_cmd_mem_read(dssdev, &p.u.mem_read);
+- break;
++ while (1) {
++ bool sched;
+
+- case DSI_CMD_TEST:
+- dsi_do_cmd_test(dssdev, &p.u.test);
+- break;
++ wait_event_interruptible(dsi.waitqueue,
++ dsi.update_mode == OMAP_DSS_UPDATE_AUTO ||
++ (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL &&
++ dsi.update_region.dirty == true) ||
++ kthread_should_stop());
+
+- case DSI_CMD_SET_TE:
+- dsi_do_cmd_set_te(dssdev, p.u.te);
++ if (kthread_should_stop())
+ break;
+
+- case DSI_CMD_SET_UPDATE_MODE:
+- dsi_do_cmd_set_update_mode(dssdev, p.u.update_mode);
+- break;
+-
+- case DSI_CMD_SET_ROTATE:
+- dsi_bus_lock();
+- dssdev->driver->set_rotate(dssdev, p.u.rotate);
+- if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO)
+- dsi.autoupdate_setup = 1;
+- dsi_bus_unlock();
+- break;
++ dsi_bus_lock();
+
+- case DSI_CMD_SET_MIRROR:
+- dsi_bus_lock();
+- dssdev->driver->set_mirror(dssdev, p.u.mirror);
++ if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED ||
++ kthread_should_stop()) {
+ dsi_bus_unlock();
+ break;
+-
+- default:
+- BUG();
+ }
+- }
+-
+- if (dsi.debug_process)
+- DSSDBG("exit dsi_process_cmd_fifo\n");
+-
+- dsi_signal_fifo_waiters();
+-}
+-
+-static void dsi_push_cmd(struct dsi_cmd_item *p)
+-{
+- int ret;
+
+- if (dsi.debug_process)
+- DSSDBGF("");
++ dsi_perf_mark_setup();
+
+- while (1) {
+- unsigned long flags;
+- unsigned avail, used;
+-
+- spin_lock_irqsave(dsi.cmd_fifo->lock, flags);
+- used = __kfifo_len(dsi.cmd_fifo) / sizeof(struct dsi_cmd_item);
+- avail = DSI_CMD_FIFO_LEN - used;
+-
+- if (dsi.debug_process)
+- DSSDBG("%u/%u items left in fifo\n", avail, used);
+-
+- if (avail == 0) {
+- if (dsi.debug_process)
+- DSSDBG("cmd fifo full, waiting...\n");
+- spin_unlock_irqrestore(dsi.cmd_fifo->lock, flags);
+- atomic_inc(&dsi.cmd_fifo_full);
+- wait_for_completion(&dsi.cmd_done);
+- if (dsi.debug_process)
+- DSSDBG("cmd fifo not full, woke up\n");
+- continue;
++ if (dsi.update_region.dirty) {
++ spin_lock(&dsi.update_lock);
++ dsi.active_update_region = dsi.update_region;
++ dsi.update_region.dirty = false;
++ spin_unlock(&dsi.update_lock);
+ }
+
+- ret = __kfifo_put(dsi.cmd_fifo, (unsigned char *)p,
+- sizeof(*p));
+-
+- spin_unlock_irqrestore(dsi.cmd_fifo->lock, flags);
+-
+- BUG_ON(ret != sizeof(*p));
+-
+- break;
+- }
+-
+- queue_work(dsi.workqueue, &dsi.process_work);
+-}
+-
+-static void dsi_push_update(struct omap_dss_device *dssdev,
+- int x, int y, int w, int h)
+-{
+- struct dsi_cmd_item p;
+-
+- p.dssdev = dssdev;
+- p.cmd = DSI_CMD_UPDATE;
+-
+- p.u.r.x = x;
+- p.u.r.y = y;
+- p.u.r.w = w;
+- p.u.r.h = h;
+-
+- DSSDBG("pushing UPDATE %d,%d %dx%d\n", x, y, w, h);
+-
+- dsi_push_cmd(&p);
+-}
+-
+-static void dsi_push_autoupdate(struct omap_dss_device *dssdev)
+-{
+- struct dsi_cmd_item p;
+-
+- p.dssdev = dssdev;
+- p.cmd = DSI_CMD_AUTOUPDATE;
+-
+- dsi_push_cmd(&p);
+-}
+-
+-static void dsi_push_sync(struct omap_dss_device *dssdev,
+- struct completion *sync_comp)
+-{
+- struct dsi_cmd_item p;
+-
+- p.dssdev = dssdev;
+- p.cmd = DSI_CMD_SYNC;
+- p.u.sync = sync_comp;
+-
+- DSSDBG("pushing SYNC\n");
+-
+- dsi_push_cmd(&p);
+-}
+-
+-static void dsi_push_mem_read(struct omap_dss_device *dssdev,
+- struct dsi_cmd_mem_read *mem_read)
+-{
+- struct dsi_cmd_item p;
+-
+- p.dssdev = dssdev;
+- p.cmd = DSI_CMD_MEM_READ;
+- p.u.mem_read = *mem_read;
++ device = dsi.active_update_region.device;
++ x = dsi.active_update_region.x;
++ y = dsi.active_update_region.y;
++ w = dsi.active_update_region.w;
++ h = dsi.active_update_region.h;
+
+- DSSDBG("pushing MEM_READ\n");
+-
+- dsi_push_cmd(&p);
+-}
+-
+-static void dsi_push_test(struct omap_dss_device *dssdev, int test_num,
+- int *result, struct completion *completion)
+-{
+- struct dsi_cmd_item p;
+-
+- p.dssdev = dssdev;
+- p.cmd = DSI_CMD_TEST;
+- p.u.test.test_num = test_num;
+- p.u.test.result = result;
+- p.u.test.completion = completion;
+-
+- DSSDBG("pushing TEST\n");
+-
+- dsi_push_cmd(&p);
+-}
++ if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+
+-static void dsi_push_set_te(struct omap_dss_device *dssdev, bool enable)
+-{
+- struct dsi_cmd_item p;
+-
+- p.dssdev = dssdev;
+- p.cmd = DSI_CMD_SET_TE;
+- p.u.te = enable;
+-
+- DSSDBG("pushing SET_TE\n");
+-
+- dsi_push_cmd(&p);
+-}
+-
+-static void dsi_push_set_update_mode(struct omap_dss_device *dssdev,
+- enum omap_dss_update_mode mode)
+-{
+- struct dsi_cmd_item p;
+-
+- p.dssdev = dssdev;
+- p.cmd = DSI_CMD_SET_UPDATE_MODE;
+- p.u.update_mode = mode;
+-
+- DSSDBG("pushing SET_UPDATE_MODE\n");
+-
+- dsi_push_cmd(&p);
+-}
++ if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL) {
++ dispc_setup_partial_planes(device,
++ &x, &y, &w, &h);
++#if 1
++ /* XXX there seems to be a bug in this driver
++ * or OMAP hardware. Some updates with certain
++ * widths and x coordinates fail. These widths
++ * are always odd, so "fix" it here for now */
++ if (w & 1) {
++ u16 dw, dh;
++ device->get_resolution(device, &dw, &dh);
++ if (x + w == dw)
++ x &= ~1;
++ ++w;
++
++ dispc_setup_partial_planes(device,
++ &x, &y, &w, &h);
++ }
++#endif
++ }
+
+-static void dsi_push_set_rotate(struct omap_dss_device *dssdev, int rotate)
+-{
+- struct dsi_cmd_item p;
++ dispc_set_lcd_size(w, h);
++ }
+
+- p.dssdev = dssdev;
+- p.cmd = DSI_CMD_SET_ROTATE;
+- p.u.rotate = rotate;
++ /* XXX We don't need to send the update area coords to the
++ * panel every time. But for some reason TE doesn't work if we
++ * don't send at least a BTA here... */
++#if 0
++ if (dsi.active_update_region.dirty) {
++ dsi.active_update_region.dirty = false;
++ device->driver->setup_update(device, x, y, w, h);
++ }
++#else
++ device->driver->setup_update(device, x, y, w, h);
++#endif
+
+- DSSDBG("pushing SET_ROTATE\n");
++ if (dsi.te_enabled && dsi.use_ext_te && device->wait_for_te)
++ device->wait_for_te(device);
+
+- dsi_push_cmd(&p);
+-}
++ dsi_perf_mark_start();
+
+-static void dsi_push_set_mirror(struct omap_dss_device *dssdev, int mirror)
+-{
+- struct dsi_cmd_item p;
++ if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
++ dsi_update_screen_dispc(device, x, y, w, h);
+
+- p.dssdev = dssdev;
+- p.cmd = DSI_CMD_SET_MIRROR;
+- p.u.mirror = mirror;
++ /* wait for framedone */
++ timeout = msecs_to_jiffies(500);
++ timeout = wait_event_timeout(dsi.waitqueue,
++ dsi.framedone_received == true,
++ timeout);
+
+- DSSDBG("pushing SET_MIRROR\n");
++ dsi.framedone_received = false;
+
+- dsi_push_cmd(&p);
+-}
++ if (timeout == 0) {
++ DSSERR("framedone timeout\n");
++ DSSERR("failed update %d,%d %dx%d\n",
++ x, y, w, h);
+
+-static int dsi_wait_sync(struct omap_dss_device *dssdev)
+-{
+- long wait = msecs_to_jiffies(2000);
+- struct completion compl;
++ dispc_enable_sidle();
++ dispc_enable_lcd_out(0);
++ } else {
++ dsi_handle_framedone();
++ dsi_perf_show("DISPC");
++ }
++ } else {
++ dsi_update_screen_l4(device, x, y, w, h);
++ dsi_perf_show("L4");
++ }
+
+- DSSDBGF("");
++ sched = atomic_read(&dsi.bus_lock.count) < 0;
+
+- init_completion(&compl);
+- dsi_push_sync(dssdev, &compl);
++ complete_all(&dsi.update_completion);
+
+- DSSDBG("Waiting for SYNC to happen...\n");
+- wait = wait_for_completion_timeout(&compl, wait);
+- DSSDBG("Released from SYNC\n");
++ dsi_bus_unlock();
+
+- if (wait == 0) {
+- DSSERR("timeout waiting sync\n");
+- return -ETIME;
++ /* XXX We need to give others chance to get the bus lock. Is
++ * there a better way for this? */
++ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO && sched)
++ schedule_timeout_interruptible(1);
+ }
+
++ DSSDBG("update thread exiting\n");
++
+ return 0;
+ }
+
+
+
+-
+-
+-
+-
+-
+-
+-
+-
+-
+ /* Display funcs */
+
+ static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
+ {
+ int r;
+
+- r = omap_dispc_register_isr(framedone_callback, NULL,
++ r = omap_dispc_register_isr(dsi_framedone_irq_callback, NULL,
+ DISPC_IRQ_FRAMEDONE);
+ if (r) {
+ DSSERR("can't get FRAMEDONE irq\n");
+@@ -3369,7 +2856,7 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
+
+ static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev)
+ {
+- omap_dispc_unregister_isr(framedone_callback, NULL,
++ omap_dispc_unregister_isr(dsi_framedone_irq_callback, NULL,
+ DISPC_IRQ_FRAMEDONE);
+ }
+
+@@ -3380,8 +2867,6 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
+
+ _dsi_print_reset_status();
+
+- dsi_bus_lock();
+-
+ r = dsi_pll_init(1, 0);
+ if (r)
+ goto err0;
+@@ -3427,8 +2912,6 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
+ /* enable high-speed after initial config */
+ dsi_vc_enable_hs(0, 1);
+
+- dsi_bus_unlock();
+-
+ return 0;
+ err3:
+ dsi_if_enable(0);
+@@ -3437,16 +2920,13 @@ err2:
+ err1:
+ dsi_pll_uninit();
+ err0:
+- dsi_bus_unlock();
+ return r;
+ }
+
+ static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev)
+ {
+- dsi_bus_lock();
+ if (dssdev->driver->disable)
+ dssdev->driver->disable(dssdev);
+- dsi_bus_unlock();
+
+ dsi_complexio_uninit();
+ dsi_pll_uninit();
+@@ -3475,6 +2955,7 @@ static int dsi_display_enable(struct omap_dss_device *dssdev)
+ DSSDBG("dsi_display_enable\n");
+
+ mutex_lock(&dsi.lock);
++ dsi_bus_lock();
+
+ r = omap_dss_start_device(dssdev);
+ if (r) {
+@@ -3507,15 +2988,17 @@ static int dsi_display_enable(struct omap_dss_device *dssdev)
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+- if (dsi.use_te || dsi.use_ext_te)
+- dsi_push_set_te(dssdev, 1);
++ dsi.use_ext_te = dssdev->phy.dsi.ext_te;
++ dsi_set_te(dssdev, dsi.te_enabled);
+
+- dsi_push_set_update_mode(dssdev, dsi.user_update_mode);
+- dsi.target_update_mode = dsi.user_update_mode;
++ dsi.update_mode = dsi.user_update_mode;
++ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO)
++ dsi_start_auto_update(dssdev);
+
++ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+
+- return dsi_wait_sync(dssdev);
++ return 0;
+
+ err3:
+ dsi_display_uninit_dispc(dssdev);
+@@ -3525,6 +3008,7 @@ err2:
+ err1:
+ omap_dss_stop_device(dssdev);
+ err0:
++ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+ DSSDBG("dsi_display_enable FAILED\n");
+ return r;
+@@ -3535,18 +3019,13 @@ static void dsi_display_disable(struct omap_dss_device *dssdev)
+ DSSDBG("dsi_display_disable\n");
+
+ mutex_lock(&dsi.lock);
++ dsi_bus_lock();
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
+ dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
+ goto end;
+
+- if (dsi.target_update_mode != OMAP_DSS_UPDATE_DISABLED) {
+- dsi_push_set_update_mode(dssdev, OMAP_DSS_UPDATE_DISABLED);
+- dsi.target_update_mode = OMAP_DSS_UPDATE_DISABLED;
+- }
+-
+- dsi_wait_sync(dssdev);
+-
++ dsi.update_mode = OMAP_DSS_UPDATE_DISABLED;
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+
+ dsi_display_uninit_dispc(dssdev);
+@@ -3558,6 +3037,7 @@ static void dsi_display_disable(struct omap_dss_device *dssdev)
+
+ omap_dss_stop_device(dssdev);
+ end:
++ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+ }
+
+@@ -3566,18 +3046,13 @@ static int dsi_display_suspend(struct omap_dss_device *dssdev)
+ DSSDBG("dsi_display_suspend\n");
+
+ mutex_lock(&dsi.lock);
++ dsi_bus_lock();
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
+ dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
+ goto end;
+
+- if (dsi.target_update_mode != OMAP_DSS_UPDATE_DISABLED) {
+- dsi_push_set_update_mode(dssdev, OMAP_DSS_UPDATE_DISABLED);
+- dsi.target_update_mode = OMAP_DSS_UPDATE_DISABLED;
+- }
+-
+- dsi_wait_sync(dssdev);
+-
++ dsi.update_mode = OMAP_DSS_UPDATE_DISABLED;
+ dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+
+ dsi_display_uninit_dispc(dssdev);
+@@ -3587,6 +3062,7 @@ static int dsi_display_suspend(struct omap_dss_device *dssdev)
+ enable_clocks(0);
+ dsi_enable_pll_clock(0);
+ end:
++ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+
+ return 0;
+@@ -3599,6 +3075,7 @@ static int dsi_display_resume(struct omap_dss_device *dssdev)
+ DSSDBG("dsi_display_resume\n");
+
+ mutex_lock(&dsi.lock);
++ dsi_bus_lock();
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
+ DSSERR("dssdev not suspended\n");
+@@ -3625,15 +3102,16 @@ static int dsi_display_resume(struct omap_dss_device *dssdev)
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+- if (dsi.use_te || dsi.use_ext_te)
+- dsi_push_set_te(dssdev, 1);
++ dsi_set_te(dssdev, dsi.te_enabled);
+
+- dsi_push_set_update_mode(dssdev, dsi.user_update_mode);
+- dsi.target_update_mode = dsi.user_update_mode;
++ dsi.update_mode = dsi.user_update_mode;
++ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO)
++ dsi_start_auto_update(dssdev);
+
++ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+
+- return dsi_wait_sync(dssdev);
++ return 0;
+
+ err2:
+ dsi_display_uninit_dispc(dssdev);
+@@ -3641,6 +3119,7 @@ err1:
+ enable_clocks(0);
+ dsi_enable_pll_clock(0);
+ err0:
++ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+ DSSDBG("dsi_display_resume FAILED\n");
+ return r;
+@@ -3649,26 +3128,68 @@ err0:
+ static int dsi_display_update(struct omap_dss_device *dssdev,
+ u16 x, u16 y, u16 w, u16 h)
+ {
++ int r = 0;
++ u16 dw, dh;
++
+ DSSDBG("dsi_display_update(%d,%d %dx%d)\n", x, y, w, h);
+
++ mutex_lock(&dsi.lock);
++
++ if (dsi.update_mode != OMAP_DSS_UPDATE_MANUAL)
++ goto end;
++
++ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
++ goto end;
++
++ dssdev->get_resolution(dssdev, &dw, &dh);
++
++ if (x > dw || y > dh)
++ goto end;
++
++ if (x + w > dw)
++ w = dw - x;
++
++ if (y + h > dh)
++ h = dh - y;
++
+ if (w == 0 || h == 0)
+- return 0;
++ goto end;
+
+- mutex_lock(&dsi.lock);
++ dsi_set_update_region(dssdev, x, y, w, h);
+
+- if (dsi.target_update_mode == OMAP_DSS_UPDATE_MANUAL)
+- dsi_push_update(dssdev, x, y, w, h);
+- /* XXX else return error? */
++ wake_up(&dsi.waitqueue);
+
++end:
+ mutex_unlock(&dsi.lock);
+
+- return 0;
++ return r;
+ }
+
+ static int dsi_display_sync(struct omap_dss_device *dssdev)
+ {
+- DSSDBGF("");
+- return dsi_wait_sync(dssdev);
++ bool wait;
++
++ DSSDBG("dsi_display_sync()\n");
++
++ mutex_lock(&dsi.lock);
++ dsi_bus_lock();
++
++ if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL &&
++ dsi.update_region.dirty) {
++ INIT_COMPLETION(dsi.update_completion);
++ wait = true;
++ } else {
++ wait = false;
++ }
++
++ dsi_bus_unlock();
++ mutex_unlock(&dsi.lock);
++
++ if (wait)
++ wait_for_completion_interruptible(&dsi.update_completion);
++
++ DSSDBG("dsi_display_sync() done\n");
++ return 0;
+ }
+
+ static int dsi_display_set_update_mode(struct omap_dss_device *dssdev,
+@@ -3677,17 +3198,21 @@ static int dsi_display_set_update_mode(struct omap_dss_device *dssdev,
+ DSSDBGF("%d", mode);
+
+ mutex_lock(&dsi.lock);
++ dsi_bus_lock();
+
+- if (dsi.target_update_mode != mode) {
+- dsi_push_set_update_mode(dssdev, mode);
+-
+- dsi.target_update_mode = mode;
++ if (dsi.update_mode != mode) {
+ dsi.user_update_mode = mode;
++ dsi.update_mode = mode;
++
++ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE &&
++ mode == OMAP_DSS_UPDATE_AUTO)
++ dsi_start_auto_update(dssdev);
+ }
+
++ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+
+- return dsi_wait_sync(dssdev);
++ return 0;
+ }
+
+ static enum omap_dss_update_mode dsi_display_get_update_mode(
+@@ -3696,6 +3221,7 @@ static enum omap_dss_update_mode dsi_display_get_update_mode(
+ return dsi.update_mode;
+ }
+
++
+ static int dsi_display_enable_te(struct omap_dss_device *dssdev, bool enable)
+ {
+ DSSDBGF("%d", enable);
+@@ -3703,28 +3229,45 @@ static int dsi_display_enable_te(struct omap_dss_device *dssdev, bool enable)
+ if (!dssdev->driver->enable_te)
+ return -ENOENT;
+
+- dsi_push_set_te(dssdev, enable);
++ dsi_bus_lock();
++
++ dsi.te_enabled = enable;
++
++ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
++ goto end;
+
+- return dsi_wait_sync(dssdev);
++ dsi_set_te(dssdev, enable);
++end:
++ dsi_bus_unlock();
++
++ return 0;
+ }
+
+ static int dsi_display_get_te(struct omap_dss_device *dssdev)
+ {
+- return dsi.use_te | dsi.use_ext_te;
++ return dsi.te_enabled;
+ }
+
+-
+-
+ static int dsi_display_set_rotate(struct omap_dss_device *dssdev, u8 rotate)
+ {
++
+ DSSDBGF("%d", rotate);
+
+ if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
+ return -EINVAL;
+
+- dsi_push_set_rotate(dssdev, rotate);
++ dsi_bus_lock();
++ dssdev->driver->set_rotate(dssdev, rotate);
++ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) {
++ u16 w, h;
++ /* the display dimensions may have changed, so set a new
++ * update region */
++ dssdev->get_resolution(dssdev, &w, &h);
++ dsi_set_update_region(dssdev, 0, 0, w, h);
++ }
++ dsi_bus_unlock();
+
+- return dsi_wait_sync(dssdev);
++ return 0;
+ }
+
+ static u8 dsi_display_get_rotate(struct omap_dss_device *dssdev)
+@@ -3742,9 +3285,11 @@ static int dsi_display_set_mirror(struct omap_dss_device *dssdev, bool mirror)
+ if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
+ return -EINVAL;
+
+- dsi_push_set_mirror(dssdev, mirror);
++ dsi_bus_lock();
++ dssdev->driver->set_mirror(dssdev, mirror);
++ dsi_bus_unlock();
+
+- return dsi_wait_sync(dssdev);
++ return 0;
+ }
+
+ static bool dsi_display_get_mirror(struct omap_dss_device *dssdev)
+@@ -3757,39 +3302,46 @@ static bool dsi_display_get_mirror(struct omap_dss_device *dssdev)
+
+ static int dsi_display_run_test(struct omap_dss_device *dssdev, int test_num)
+ {
+- long wait = msecs_to_jiffies(60000);
+- struct completion compl;
+- int result;
++ int r;
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return -EIO;
+
+ DSSDBGF("%d", test_num);
+
+- init_completion(&compl);
++ dsi_bus_lock();
+
+- dsi_push_test(dssdev, test_num, &result, &compl);
++ /* run test first in low speed mode */
++ dsi_vc_enable_hs(0, 0);
+
+- DSSDBG("Waiting for SYNC to happen...\n");
+- wait = wait_for_completion_timeout(&compl, wait);
+- DSSDBG("Released from SYNC\n");
++ if (dssdev->driver->run_test) {
++ r = dssdev->driver->run_test(dssdev, test_num);
++ if (r)
++ goto end;
++ }
+
+- if (wait == 0) {
+- DSSERR("timeout waiting test sync\n");
+- return -ETIME;
++ /* then in high speed */
++ dsi_vc_enable_hs(0, 1);
++
++ if (dssdev->driver->run_test) {
++ r = dssdev->driver->run_test(dssdev, test_num);
++ if (r)
++ goto end;
+ }
+
+- return result;
++end:
++ dsi_vc_enable_hs(0, 1);
++
++ dsi_bus_unlock();
++
++ return r;
+ }
+
+ static int dsi_display_memory_read(struct omap_dss_device *dssdev,
+ void *buf, size_t size,
+ u16 x, u16 y, u16 w, u16 h)
+ {
+- long wait = msecs_to_jiffies(60000);
+- struct completion compl;
+- struct dsi_cmd_mem_read mem_read;
+- size_t ret_size;
++ int r;
+
+ DSSDBGF("");
+
+@@ -3799,29 +3351,14 @@ static int dsi_display_memory_read(struct omap_dss_device *dssdev,
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return -EIO;
+
+- init_completion(&compl);
+-
+- mem_read.x = x;
+- mem_read.y = y;
+- mem_read.w = w;
+- mem_read.h = h;
+- mem_read.buf = buf;
+- mem_read.size = size;
+- mem_read.ret_size = &ret_size;
+- mem_read.completion = &compl;
+-
+- dsi_push_mem_read(dssdev, &mem_read);
++ dsi_bus_lock();
+
+- DSSDBG("Waiting for SYNC to happen...\n");
+- wait = wait_for_completion_timeout(&compl, wait);
+- DSSDBG("Released from SYNC\n");
++ r = dssdev->driver->memory_read(dssdev, buf, size,
++ x, y, w, h);
+
+- if (wait == 0) {
+- DSSERR("timeout waiting mem read sync\n");
+- return -ETIME;
+- }
++ dsi_bus_unlock();
+
+- return ret_size;
++ return r;
+ }
+
+ static void dsi_configure_overlay(struct omap_overlay *ovl)
+@@ -3879,28 +3416,23 @@ int dsi_init(struct platform_device *pdev)
+ spin_lock_init(&dsi.errors_lock);
+ dsi.errors = 0;
+
+- spin_lock_init(&dsi.cmd_lock);
+- dsi.cmd_fifo = kfifo_alloc(
+- DSI_CMD_FIFO_LEN * sizeof(struct dsi_cmd_item),
+- GFP_KERNEL,
+- &dsi.cmd_lock);
+-
+- init_completion(&dsi.cmd_done);
+- atomic_set(&dsi.cmd_fifo_full, 0);
+- atomic_set(&dsi.cmd_pending, 0);
++ /* XXX fail properly */
+
+ init_completion(&dsi.bta_completion);
++ init_completion(&dsi.update_completion);
+
+- dsi.workqueue = create_singlethread_workqueue("dsi");
+- INIT_WORK(&dsi.framedone_work, framedone_worker);
+- INIT_WORK(&dsi.process_work, dsi_process_cmd_fifo);
+- INIT_DELAYED_WORK(&dsi.framedone_timeout_work,
+- framedone_timeout_callback);
++ dsi.thread = kthread_create(dsi_update_thread, NULL, "dsi");
++ if (IS_ERR(dsi.thread)) {
++ DSSERR("cannot create kthread\n");
++ return PTR_ERR(dsi.thread);
++ }
++ init_waitqueue_head(&dsi.waitqueue);
++ spin_lock_init(&dsi.update_lock);
+
+ mutex_init(&dsi.lock);
+ mutex_init(&dsi.bus_lock);
+
+- dsi.target_update_mode = OMAP_DSS_UPDATE_DISABLED;
++ dsi.update_mode = OMAP_DSS_UPDATE_DISABLED;
+ dsi.user_update_mode = OMAP_DSS_UPDATE_DISABLED;
+
+ dsi.base = ioremap(DSI_BASE, DSI_SZ_REGS);
+@@ -3924,20 +3456,19 @@ int dsi_init(struct platform_device *pdev)
+
+ enable_clocks(0);
+
++ wake_up_process(dsi.thread);
++
+ return 0;
+ }
+
+ void dsi_exit(void)
+ {
+- flush_workqueue(dsi.workqueue);
+- destroy_workqueue(dsi.workqueue);
++ kthread_stop(dsi.thread);
+
+ regulator_put(dsi.vdds_dsi_reg);
+
+ iounmap(dsi.base);
+
+- kfifo_free(dsi.cmd_fifo);
+-
+ DSSDBG("omap_dsi_exit\n");
+ }
+
+diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
+index 1e40830..36f401b 100644
+--- a/drivers/video/omap2/dss/dss.h
++++ b/drivers/video/omap2/dss/dss.h
+@@ -185,6 +185,9 @@ 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 */
+diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
+index 798867c..eeca3f9 100644
+--- a/drivers/video/omap2/dss/manager.c
++++ b/drivers/video/omap2/dss/manager.c
+@@ -635,6 +635,42 @@ int dss_init_overlay_managers(struct platform_device *pdev)
+ }
+ }
+
++#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;
+ }
+
+@@ -674,12 +710,3 @@ struct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
+ }
+ EXPORT_SYMBOL(omap_dss_get_overlay_manager);
+
+-#ifdef L4_EXAMPLE
+-static int ovl_mgr_apply_l4(struct omap_overlay_manager *mgr)
+-{
+- DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name);
+-
+- return 0;
+-}
+-#endif
+-
+diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
+index 31385f3..9f883aa 100644
+--- a/drivers/video/omap2/dss/overlay.c
++++ b/drivers/video/omap2/dss/overlay.c
+@@ -477,6 +477,15 @@ void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr)
+ 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;
+@@ -534,6 +543,33 @@ void dss_init_overlays(struct platform_device *pdev)
+
+ 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
+@@ -577,35 +613,6 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)
+ }
+ }
+ }
+-#ifdef L4_EXAMPLE
+- /* setup L4 overlay as an example */
+- {
+- static struct omap_overlay ovl = {
+- .name = "l4-ovl",
+- .supported_modes = OMAP_DSS_COLOR_RGB24U,
+- .set_manager = &omap_dss_set_manager,
+- .unset_manager = &omap_dss_unset_manager,
+- .setup_input = &omap_dss_setup_overlay_input,
+- .setup_output = &omap_dss_setup_overlay_output,
+- .enable = &omap_dss_enable_overlay,
+- };
+-
+- static struct omap_overlay_manager mgr = {
+- .name = "l4",
+- .num_overlays = 1,
+- .overlays = &ovl,
+- .set_display = &omap_dss_set_display,
+- .unset_display = &omap_dss_unset_display,
+- .apply = &ovl_mgr_apply_l4,
+- .supported_displays =
+- OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI,
+- };
+-
+- omap_dss_add_overlay(&ovl);
+- omap_dss_add_overlay_manager(&mgr);
+- omap_dss_set_manager(&ovl, &mgr);
+- }
+-#endif
+ }
+
+ void dss_uninit_overlays(struct platform_device *pdev)
+--
+1.6.2.4
+