Merge branch 'for-linus' of git://gitorious.org/linux-omap-dss2/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 8 Aug 2010 17:02:59 +0000 (10:02 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 8 Aug 2010 17:02:59 +0000 (10:02 -0700)
* 'for-linus' of git://gitorious.org/linux-omap-dss2/linux: (64 commits)
  OMAP: DSS2: OMAPFB: add support for FBIO_WAITFORVSYNC
  OMAP: DSS2: Replace strncmp() with sysfs_streq() in overlay_manager_store()
  OMAP: DSS2: Fix error path in omap_dsi_update()
  OMAP: DSS2: TDO35S: fix video signaling
  OMAP: DSS2: OMAPFB: Fix invalid bpp for PAL and NTSC modes
  OMAP: DSS2: OMAPFB: Fix probe error path
  OMAP3EVM: Replace vdvi regulator supply with vdds_dsi
  OMAP: DSS2: Remove extra return statement
  OMAP: DSS2: adjust YUV overlay width to be even
  OMAP: DSS2: OMAPFB: Fix sysfs mirror input check
  OMAP: DSS2: OMAPFB: Remove redundant color register range check
  OMAP: DSS2: OMAPFB: Remove redundant rotate range check
  OMAP: DSS2: OMAPFB: Check fb2display() return value
  OMAP: DSS2: Taal: Optimize enable_te, rotate, mirror when value unchanged
  OMAP: DSS2: DSI: detect unsupported update requests
  OMAP: DSS2: DSI: increase FIFO low threshold
  OMAP: DSS2: DSI: Add error IRQ mask for DSI complexIO
  OMAP: DSS2: DSI: Remove BTA after set_max_rx_packet_size
  OMAP: DSS2: change manual update scaling setup
  OMAP: DSS2: DSI: use BTA to end the frame transfer
  ...

18 files changed:
arch/arm/mach-omap2/board-omap3evm.c
arch/arm/plat-omap/include/plat/display.h
arch/arm/plat-omap/include/plat/nokia-dsi-panel.h [new file with mode: 0644]
drivers/video/omap2/displays/panel-taal.c
drivers/video/omap2/displays/panel-toppoly-tdo35s.c
drivers/video/omap2/dss/dispc.c
drivers/video/omap2/dss/display.c
drivers/video/omap2/dss/dsi.c
drivers/video/omap2/dss/dss.c
drivers/video/omap2/dss/dss.h
drivers/video/omap2/dss/manager.c
drivers/video/omap2/dss/overlay.c
drivers/video/omap2/dss/rfbi.c
drivers/video/omap2/omapfb/omapfb-ioctl.c
drivers/video/omap2/omapfb/omapfb-main.c
drivers/video/omap2/omapfb/omapfb-sysfs.c
drivers/video/omap2/omapfb/omapfb.h
include/linux/omapfb.h

index 6494dbdfc391567bfe3d1c209de0d4d2ad34c5b5..f76d9c0a47a1e83c8220977d75908c181899d53c 100644 (file)
@@ -514,14 +514,11 @@ static struct regulator_init_data omap3_evm_vdac = {
 };
 
 /* VPLL2 for digital video outputs */
-static struct regulator_consumer_supply omap3_evm_vpll2_supply = {
-       .supply         = "vdvi",
-       .dev            = &omap3_evm_lcd_device.dev,
-};
+static struct regulator_consumer_supply omap3_evm_vpll2_supply =
+       REGULATOR_SUPPLY("vdds_dsi", "omapdss");
 
 static struct regulator_init_data omap3_evm_vpll2 = {
        .constraints = {
-               .name                   = "VDVI",
                .min_uV                 = 1800000,
                .max_uV                 = 1800000,
                .apply_uV               = true,
index 1c529ce9dc1149c710d87fe7871aecd00e7cdb0b..8bd15bdb4132c1532fdd0704c88af5206b7f6e15 100644 (file)
@@ -238,7 +238,7 @@ int dsi_vc_dcs_write_1(int channel, u8 dcs_cmd, u8 param);
 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_dcs_read_1(int channel, u8 dcs_cmd, u8 *data);
-int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u16 *data);
+int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u8 *data1, u8 *data2);
 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);
@@ -277,8 +277,8 @@ struct omap_video_timings {
  * 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;
+extern const struct omap_video_timings omap_dss_pal_timings;
+extern const struct omap_video_timings omap_dss_ntsc_timings;
 #endif
 
 struct omap_overlay_info {
@@ -560,7 +560,8 @@ void omapdss_dsi_vc_enable_hs(int channel, bool enable);
 int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable);
 
 int omap_dsi_prepare_update(struct omap_dss_device *dssdev,
-                                   u16 *x, u16 *y, u16 *w, u16 *h);
+                                   u16 *x, u16 *y, u16 *w, u16 *h,
+                                   bool enlarge_update_area);
 int omap_dsi_update(struct omap_dss_device *dssdev,
                int channel,
                u16 x, u16 y, u16 w, u16 h,
diff --git a/arch/arm/plat-omap/include/plat/nokia-dsi-panel.h b/arch/arm/plat-omap/include/plat/nokia-dsi-panel.h
new file mode 100644 (file)
index 0000000..01ab657
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef __ARCH_ARM_PLAT_OMAP_NOKIA_DSI_PANEL_H
+#define __ARCH_ARM_PLAT_OMAP_NOKIA_DSI_PANEL_H
+
+#include "display.h"
+
+/**
+ * struct nokia_dsi_panel_data - Nokia DSI panel driver configuration
+ * @name: panel name
+ * @use_ext_te: use external TE
+ * @ext_te_gpio: external TE GPIO
+ * @use_esd_check: perform ESD checks
+ * @max_backlight_level: maximum backlight level
+ * @set_backlight: pointer to backlight set function
+ * @get_backlight: pointer to backlight get function
+ */
+struct nokia_dsi_panel_data {
+       const char *name;
+
+       int reset_gpio;
+
+       bool use_ext_te;
+       int ext_te_gpio;
+
+       bool use_esd_check;
+
+       int max_backlight_level;
+       int (*set_backlight)(struct omap_dss_device *dssdev, int level);
+       int (*get_backlight)(struct omap_dss_device *dssdev);
+};
+
+#endif /* __ARCH_ARM_PLAT_OMAP_NOKIA_DSI_PANEL_H */
index aaf5d308a046d6db9d42708964e8f717247ea0dd..e1c765d1141998b9584a3f38e00819c0b5c77300 100644 (file)
 #include <linux/fb.h>
 #include <linux/interrupt.h>
 #include <linux/gpio.h>
-#include <linux/completion.h>
 #include <linux/workqueue.h>
 #include <linux/slab.h>
+#include <linux/regulator/consumer.h>
 #include <linux/mutex.h>
 
 #include <plat/display.h>
+#include <plat/nokia-dsi-panel.h>
 
 /* DSI Virtual channel. Hardcoded for now. */
 #define TCH 0
 #define DCS_GET_ID2            0xdb
 #define DCS_GET_ID3            0xdc
 
-/* #define TAAL_USE_ESD_CHECK */
 #define TAAL_ESD_CHECK_PERIOD  msecs_to_jiffies(5000)
 
+static irqreturn_t taal_te_isr(int irq, void *data);
+static void taal_te_timeout_work_callback(struct work_struct *work);
 static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable);
 
+struct panel_regulator {
+       struct regulator *regulator;
+       const char *name;
+       int min_uV;
+       int max_uV;
+};
+
+static void free_regulators(struct panel_regulator *regulators, int n)
+{
+       int i;
+
+       for (i = 0; i < n; i++) {
+               /* disable/put in reverse order */
+               regulator_disable(regulators[n - i - 1].regulator);
+               regulator_put(regulators[n - i - 1].regulator);
+       }
+}
+
+static int init_regulators(struct omap_dss_device *dssdev,
+                       struct panel_regulator *regulators, int n)
+{
+       int r, i, v;
+
+       for (i = 0; i < n; i++) {
+               struct regulator *reg;
+
+               reg = regulator_get(&dssdev->dev, regulators[i].name);
+               if (IS_ERR(reg)) {
+                       dev_err(&dssdev->dev, "failed to get regulator %s\n",
+                               regulators[i].name);
+                       r = PTR_ERR(reg);
+                       goto err;
+               }
+
+               /* FIXME: better handling of fixed vs. variable regulators */
+               v = regulator_get_voltage(reg);
+               if (v < regulators[i].min_uV || v > regulators[i].max_uV) {
+                       r = regulator_set_voltage(reg, regulators[i].min_uV,
+                                               regulators[i].max_uV);
+                       if (r) {
+                               dev_err(&dssdev->dev,
+                                       "failed to set regulator %s voltage\n",
+                                       regulators[i].name);
+                               regulator_put(reg);
+                               goto err;
+                       }
+               }
+
+               r = regulator_enable(reg);
+               if (r) {
+                       dev_err(&dssdev->dev, "failed to enable regulator %s\n",
+                               regulators[i].name);
+                       regulator_put(reg);
+                       goto err;
+               }
+
+               regulators[i].regulator = reg;
+       }
+
+       return 0;
+
+err:
+       free_regulators(regulators, i);
+
+       return r;
+}
+
+/**
+ * struct panel_config - panel configuration
+ * @name: panel name
+ * @type: panel type
+ * @timings: panel resolution
+ * @sleep: various panel specific delays, passed to msleep() if non-zero
+ * @reset_sequence: reset sequence timings, passed to udelay() if non-zero
+ * @regulators: array of panel regulators
+ * @num_regulators: number of regulators in the array
+ */
+struct panel_config {
+       const char *name;
+       int type;
+
+       struct omap_video_timings timings;
+
+       struct {
+               unsigned int sleep_in;
+               unsigned int sleep_out;
+               unsigned int hw_reset;
+               unsigned int enable_te;
+       } sleep;
+
+       struct {
+               unsigned int high;
+               unsigned int low;
+       } reset_sequence;
+
+       struct panel_regulator *regulators;
+       int num_regulators;
+};
+
+enum {
+       PANEL_TAAL,
+};
+
+static struct panel_config panel_configs[] = {
+       {
+               .name           = "taal",
+               .type           = PANEL_TAAL,
+               .timings        = {
+                       .x_res          = 864,
+                       .y_res          = 480,
+               },
+               .sleep          = {
+                       .sleep_in       = 5,
+                       .sleep_out      = 5,
+                       .hw_reset       = 5,
+                       .enable_te      = 100, /* possible panel bug */
+               },
+               .reset_sequence = {
+                       .high           = 10,
+                       .low            = 10,
+               },
+       },
+};
+
 struct taal_data {
        struct mutex lock;
 
@@ -84,8 +210,15 @@ struct taal_data {
        bool mirror;
 
        bool te_enabled;
-       bool use_ext_te;
-       struct completion te_completion;
+
+       atomic_t do_update;
+       struct {
+               u16 x;
+               u16 y;
+               u16 w;
+               u16 h;
+       } update_region;
+       struct delayed_work te_timeout_work;
 
        bool use_dsi_bl;
 
@@ -96,8 +229,16 @@ struct taal_data {
 
        struct workqueue_struct *esd_wq;
        struct delayed_work esd_work;
+
+       struct panel_config *panel_config;
 };
 
+static inline struct nokia_dsi_panel_data
+*get_panel_data(const struct omap_dss_device *dssdev)
+{
+       return (struct nokia_dsi_panel_data *) dssdev->data;
+}
+
 static void taal_esd_work(struct work_struct *work);
 
 static void hw_guard_start(struct taal_data *td, int guard_msec)
@@ -159,7 +300,8 @@ static int taal_sleep_in(struct taal_data *td)
 
        hw_guard_start(td, 120);
 
-       msleep(5);
+       if (td->panel_config->sleep.sleep_in)
+               msleep(td->panel_config->sleep.sleep_in);
 
        return 0;
 }
@@ -176,7 +318,8 @@ static int taal_sleep_out(struct taal_data *td)
 
        hw_guard_start(td, 120);
 
-       msleep(5);
+       if (td->panel_config->sleep.sleep_out)
+               msleep(td->panel_config->sleep.sleep_out);
 
        return 0;
 }
@@ -279,6 +422,7 @@ static int taal_bl_update_status(struct backlight_device *dev)
 {
        struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
        struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
        int r;
        int level;
 
@@ -290,24 +434,26 @@ static int taal_bl_update_status(struct backlight_device *dev)
 
        dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
 
+       mutex_lock(&td->lock);
+
        if (td->use_dsi_bl) {
                if (td->enabled) {
                        dsi_bus_lock();
                        r = taal_dcs_write_1(DCS_BRIGHTNESS, level);
                        dsi_bus_unlock();
-                       if (r)
-                               return r;
+               } else {
+                       r = 0;
                }
        } else {
-               if (!dssdev->set_backlight)
-                       return -EINVAL;
-
-               r = dssdev->set_backlight(dssdev, level);
-               if (r)
-                       return r;
+               if (!panel_data->set_backlight)
+                       r = -EINVAL;
+               else
+                       r = panel_data->set_backlight(dssdev, level);
        }
 
-       return 0;
+       mutex_unlock(&td->lock);
+
+       return r;
 }
 
 static int taal_bl_get_intensity(struct backlight_device *dev)
@@ -344,16 +490,6 @@ static void taal_get_resolution(struct omap_dss_device *dssdev,
        }
 }
 
-static irqreturn_t taal_te_isr(int irq, void *data)
-{
-       struct omap_dss_device *dssdev = data;
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
-
-       complete_all(&td->te_completion);
-
-       return IRQ_HANDLED;
-}
-
 static ssize_t taal_num_errors_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
@@ -362,6 +498,8 @@ static ssize_t taal_num_errors_show(struct device *dev,
        u8 errors;
        int r;
 
+       mutex_lock(&td->lock);
+
        if (td->enabled) {
                dsi_bus_lock();
                r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors);
@@ -370,6 +508,8 @@ static ssize_t taal_num_errors_show(struct device *dev,
                r = -ENODEV;
        }
 
+       mutex_unlock(&td->lock);
+
        if (r)
                return r;
 
@@ -384,6 +524,8 @@ static ssize_t taal_hw_revision_show(struct device *dev,
        u8 id1, id2, id3;
        int r;
 
+       mutex_lock(&td->lock);
+
        if (td->enabled) {
                dsi_bus_lock();
                r = taal_get_id(&id1, &id2, &id3);
@@ -392,6 +534,8 @@ static ssize_t taal_hw_revision_show(struct device *dev,
                r = -ENODEV;
        }
 
+       mutex_unlock(&td->lock);
+
        if (r)
                return r;
 
@@ -441,6 +585,8 @@ static ssize_t store_cabc_mode(struct device *dev,
        if (i == ARRAY_SIZE(cabc_modes))
                return -EINVAL;
 
+       mutex_lock(&td->lock);
+
        if (td->enabled) {
                dsi_bus_lock();
                if (!td->cabc_broken)
@@ -450,6 +596,8 @@ static ssize_t store_cabc_mode(struct device *dev,
 
        td->cabc_mode = i;
 
+       mutex_unlock(&td->lock);
+
        return count;
 }
 
@@ -488,47 +636,93 @@ static struct attribute_group taal_attr_group = {
        .attrs = taal_attrs,
 };
 
+static void taal_hw_reset(struct omap_dss_device *dssdev)
+{
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
+
+       if (panel_data->reset_gpio == -1)
+               return;
+
+       gpio_set_value(panel_data->reset_gpio, 1);
+       if (td->panel_config->reset_sequence.high)
+               udelay(td->panel_config->reset_sequence.high);
+       /* reset the panel */
+       gpio_set_value(panel_data->reset_gpio, 0);
+       /* assert reset */
+       if (td->panel_config->reset_sequence.low)
+               udelay(td->panel_config->reset_sequence.low);
+       gpio_set_value(panel_data->reset_gpio, 1);
+       /* wait after releasing reset */
+       if (td->panel_config->sleep.hw_reset)
+               msleep(td->panel_config->sleep.hw_reset);
+}
+
 static int taal_probe(struct omap_dss_device *dssdev)
 {
        struct backlight_properties props;
        struct taal_data *td;
        struct backlight_device *bldev;
-       int r;
-
-       const struct omap_video_timings taal_panel_timings = {
-               .x_res          = 864,
-               .y_res          = 480,
-       };
+       struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
+       struct panel_config *panel_config = NULL;
+       int r, i;
 
        dev_dbg(&dssdev->dev, "probe\n");
 
+       if (!panel_data || !panel_data->name) {
+               r = -EINVAL;
+               goto err;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(panel_configs); i++) {
+               if (strcmp(panel_data->name, panel_configs[i].name) == 0) {
+                       panel_config = &panel_configs[i];
+                       break;
+               }
+       }
+
+       if (!panel_config) {
+               r = -EINVAL;
+               goto err;
+       }
+
        dssdev->panel.config = OMAP_DSS_LCD_TFT;
-       dssdev->panel.timings = taal_panel_timings;
+       dssdev->panel.timings = panel_config->timings;
        dssdev->ctrl.pixel_size = 24;
 
        td = kzalloc(sizeof(*td), GFP_KERNEL);
        if (!td) {
                r = -ENOMEM;
-               goto err0;
+               goto err;
        }
        td->dssdev = dssdev;
+       td->panel_config = panel_config;
 
        mutex_init(&td->lock);
 
+       atomic_set(&td->do_update, 0);
+
+       r = init_regulators(dssdev, panel_config->regulators,
+                       panel_config->num_regulators);
+       if (r)
+               goto err_reg;
+
        td->esd_wq = create_singlethread_workqueue("taal_esd");
        if (td->esd_wq == NULL) {
                dev_err(&dssdev->dev, "can't create ESD workqueue\n");
                r = -ENOMEM;
-               goto err1;
+               goto err_wq;
        }
        INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work);
 
        dev_set_drvdata(&dssdev->dev, td);
 
+       taal_hw_reset(dssdev);
+
        /* if no platform set_backlight() defined, presume DSI backlight
         * control */
        memset(&props, 0, sizeof(struct backlight_properties));
-       if (!dssdev->set_backlight)
+       if (!panel_data->set_backlight)
                td->use_dsi_bl = true;
 
        if (td->use_dsi_bl)
@@ -539,7 +733,7 @@ static int taal_probe(struct omap_dss_device *dssdev)
                                          &taal_bl_ops, &props);
        if (IS_ERR(bldev)) {
                r = PTR_ERR(bldev);
-               goto err2;
+               goto err_bl;
        }
 
        td->bldev = bldev;
@@ -553,13 +747,13 @@ static int taal_probe(struct omap_dss_device *dssdev)
 
        taal_bl_update_status(bldev);
 
-       if (dssdev->phy.dsi.ext_te) {
-               int gpio = dssdev->phy.dsi.ext_te_gpio;
+       if (panel_data->use_ext_te) {
+               int gpio = panel_data->ext_te_gpio;
 
                r = gpio_request(gpio, "taal irq");
                if (r) {
                        dev_err(&dssdev->dev, "GPIO request failed\n");
-                       goto err3;
+                       goto err_gpio;
                }
 
                gpio_direction_input(gpio);
@@ -571,49 +765,52 @@ static int taal_probe(struct omap_dss_device *dssdev)
                if (r) {
                        dev_err(&dssdev->dev, "IRQ request failed\n");
                        gpio_free(gpio);
-                       goto err3;
+                       goto err_irq;
                }
 
-               init_completion(&td->te_completion);
+               INIT_DELAYED_WORK_DEFERRABLE(&td->te_timeout_work,
+                                       taal_te_timeout_work_callback);
 
-               td->use_ext_te = true;
+               dev_dbg(&dssdev->dev, "Using GPIO TE\n");
        }
 
        r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
        if (r) {
                dev_err(&dssdev->dev, "failed to create sysfs files\n");
-               goto err4;
+               goto err_sysfs;
        }
 
        return 0;
-err4:
-       if (td->use_ext_te) {
-               int gpio = dssdev->phy.dsi.ext_te_gpio;
-               free_irq(gpio_to_irq(gpio), dssdev);
-               gpio_free(gpio);
-       }
-err3:
+err_sysfs:
+       if (panel_data->use_ext_te)
+               free_irq(gpio_to_irq(panel_data->ext_te_gpio), dssdev);
+err_irq:
+       if (panel_data->use_ext_te)
+               gpio_free(panel_data->ext_te_gpio);
+err_gpio:
        backlight_device_unregister(bldev);
-err2:
-       cancel_delayed_work_sync(&td->esd_work);
+err_bl:
        destroy_workqueue(td->esd_wq);
-err1:
+err_wq:
+       free_regulators(panel_config->regulators, panel_config->num_regulators);
+err_reg:
        kfree(td);
-err0:
+err:
        return r;
 }
 
 static void taal_remove(struct omap_dss_device *dssdev)
 {
        struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
        struct backlight_device *bldev;
 
        dev_dbg(&dssdev->dev, "remove\n");
 
        sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
 
-       if (td->use_ext_te) {
-               int gpio = dssdev->phy.dsi.ext_te_gpio;
+       if (panel_data->use_ext_te) {
+               int gpio = panel_data->ext_te_gpio;
                free_irq(gpio_to_irq(gpio), dssdev);
                gpio_free(gpio);
        }
@@ -623,9 +820,15 @@ static void taal_remove(struct omap_dss_device *dssdev)
        taal_bl_update_status(bldev);
        backlight_device_unregister(bldev);
 
-       cancel_delayed_work_sync(&td->esd_work);
+       cancel_delayed_work(&td->esd_work);
        destroy_workqueue(td->esd_wq);
 
+       /* reset, to be sure that the panel is in a valid state */
+       taal_hw_reset(dssdev);
+
+       free_regulators(td->panel_config->regulators,
+                       td->panel_config->num_regulators);
+
        kfree(td);
 }
 
@@ -635,23 +838,14 @@ static int taal_power_on(struct omap_dss_device *dssdev)
        u8 id1, id2, id3;
        int r;
 
-       if (dssdev->platform_enable) {
-               r = dssdev->platform_enable(dssdev);
-               if (r)
-                       return r;
-       }
-
-       /* it seems we have to wait a bit until taal is ready */
-       msleep(5);
-
-       dsi_bus_lock();
-
        r = omapdss_dsi_display_enable(dssdev);
        if (r) {
                dev_err(&dssdev->dev, "failed to enable DSI\n");
                goto err0;
        }
 
+       taal_hw_reset(dssdev);
+
        omapdss_dsi_vc_enable_hs(TCH, false);
 
        r = taal_sleep_out(td);
@@ -662,34 +856,47 @@ static int taal_power_on(struct omap_dss_device *dssdev)
        if (r)
                goto err;
 
-       /* on early revisions CABC is broken */
-       if (id2 == 0x00 || id2 == 0xff || id2 == 0x81)
+       /* on early Taal revisions CABC is broken */
+       if (td->panel_config->type == PANEL_TAAL &&
+               (id2 == 0x00 || id2 == 0xff || id2 == 0x81))
                td->cabc_broken = true;
 
-       taal_dcs_write_1(DCS_BRIGHTNESS, 0xff);
-       taal_dcs_write_1(DCS_CTRL_DISPLAY, (1<<2) | (1<<5)); /* BL | BCTRL */
+       r = taal_dcs_write_1(DCS_BRIGHTNESS, 0xff);
+       if (r)
+               goto err;
+
+       r = taal_dcs_write_1(DCS_CTRL_DISPLAY,
+                       (1<<2) | (1<<5));       /* BL | BCTRL */
+       if (r)
+               goto err;
+
+       r = taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */
+       if (r)
+               goto err;
 
-       taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */
+       r = taal_set_addr_mode(td->rotate, td->mirror);
+       if (r)
+               goto err;
 
-       taal_set_addr_mode(td->rotate, td->mirror);
-       if (!td->cabc_broken)
-               taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode);
+       if (!td->cabc_broken) {
+               r = taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode);
+               if (r)
+                       goto err;
+       }
 
-       taal_dcs_write_0(DCS_DISPLAY_ON);
+       r = taal_dcs_write_0(DCS_DISPLAY_ON);
+       if (r)
+               goto err;
 
        r = _taal_enable_te(dssdev, td->te_enabled);
        if (r)
                goto err;
 
-#ifdef TAAL_USE_ESD_CHECK
-       queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
-#endif
-
        td->enabled = 1;
 
        if (!td->intro_printed) {
-               dev_info(&dssdev->dev, "revision %02x.%02x.%02x\n",
-                               id1, id2, id3);
+               dev_info(&dssdev->dev, "%s panel revision %02x.%02x.%02x\n",
+                       td->panel_config->name, id1, id2, id3);
                if (td->cabc_broken)
                        dev_info(&dssdev->dev,
                                        "old Taal version, CABC disabled\n");
@@ -698,46 +905,44 @@ static int taal_power_on(struct omap_dss_device *dssdev)
 
        omapdss_dsi_vc_enable_hs(TCH, true);
 
-       dsi_bus_unlock();
-
        return 0;
 err:
+       dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
+
+       taal_hw_reset(dssdev);
+
        omapdss_dsi_display_disable(dssdev);
 err0:
-       dsi_bus_unlock();
-       if (dssdev->platform_disable)
-               dssdev->platform_disable(dssdev);
-
        return r;
 }
 
 static void taal_power_off(struct omap_dss_device *dssdev)
 {
        struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       int r;
 
-       dsi_bus_lock();
-
-       cancel_delayed_work(&td->esd_work);
-
-       taal_dcs_write_0(DCS_DISPLAY_OFF);
-       taal_sleep_in(td);
+       r = taal_dcs_write_0(DCS_DISPLAY_OFF);
+       if (!r) {
+               r = taal_sleep_in(td);
+               /* HACK: wait a bit so that the message goes through */
+               msleep(10);
+       }
 
-       /* wait a bit so that the message goes through */
-       msleep(10);
+       if (r) {
+               dev_err(&dssdev->dev,
+                               "error disabling panel, issuing HW reset\n");
+               taal_hw_reset(dssdev);
+       }
 
        omapdss_dsi_display_disable(dssdev);
 
-       if (dssdev->platform_disable)
-               dssdev->platform_disable(dssdev);
-
        td->enabled = 0;
-
-       dsi_bus_unlock();
 }
 
 static int taal_enable(struct omap_dss_device *dssdev)
 {
        struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
        int r;
 
        dev_dbg(&dssdev->dev, "enable\n");
@@ -749,10 +954,19 @@ static int taal_enable(struct omap_dss_device *dssdev)
                goto err;
        }
 
+       dsi_bus_lock();
+
        r = taal_power_on(dssdev);
+
+       dsi_bus_unlock();
+
        if (r)
                goto err;
 
+       if (panel_data->use_esd_check)
+               queue_delayed_work(td->esd_wq, &td->esd_work,
+                               TAAL_ESD_CHECK_PERIOD);
+
        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 
        mutex_unlock(&td->lock);
@@ -772,9 +986,15 @@ static void taal_disable(struct omap_dss_device *dssdev)
 
        mutex_lock(&td->lock);
 
+       cancel_delayed_work(&td->esd_work);
+
+       dsi_bus_lock();
+
        if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
                taal_power_off(dssdev);
 
+       dsi_bus_unlock();
+
        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 
        mutex_unlock(&td->lock);
@@ -794,7 +1014,14 @@ static int taal_suspend(struct omap_dss_device *dssdev)
                goto err;
        }
 
+       cancel_delayed_work(&td->esd_work);
+
+       dsi_bus_lock();
+
        taal_power_off(dssdev);
+
+       dsi_bus_unlock();
+
        dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
 
        mutex_unlock(&td->lock);
@@ -808,6 +1035,7 @@ err:
 static int taal_resume(struct omap_dss_device *dssdev)
 {
        struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
        int r;
 
        dev_dbg(&dssdev->dev, "resume\n");
@@ -819,8 +1047,20 @@ static int taal_resume(struct omap_dss_device *dssdev)
                goto err;
        }
 
+       dsi_bus_lock();
+
        r = taal_power_on(dssdev);
-       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       dsi_bus_unlock();
+
+       if (r) {
+               dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+       } else {
+               dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+               if (panel_data->use_esd_check)
+                       queue_delayed_work(td->esd_wq, &td->esd_work,
+                                       TAAL_ESD_CHECK_PERIOD);
+       }
 
        mutex_unlock(&td->lock);
 
@@ -837,10 +1077,52 @@ static void taal_framedone_cb(int err, void *data)
        dsi_bus_unlock();
 }
 
+static irqreturn_t taal_te_isr(int irq, void *data)
+{
+       struct omap_dss_device *dssdev = data;
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       int old;
+       int r;
+
+       old = atomic_cmpxchg(&td->do_update, 1, 0);
+
+       if (old) {
+               cancel_delayed_work(&td->te_timeout_work);
+
+               r = omap_dsi_update(dssdev, TCH,
+                               td->update_region.x,
+                               td->update_region.y,
+                               td->update_region.w,
+                               td->update_region.h,
+                               taal_framedone_cb, dssdev);
+               if (r)
+                       goto err;
+       }
+
+       return IRQ_HANDLED;
+err:
+       dev_err(&dssdev->dev, "start update failed\n");
+       dsi_bus_unlock();
+       return IRQ_HANDLED;
+}
+
+static void taal_te_timeout_work_callback(struct work_struct *work)
+{
+       struct taal_data *td = container_of(work, struct taal_data,
+                                       te_timeout_work.work);
+       struct omap_dss_device *dssdev = td->dssdev;
+
+       dev_err(&dssdev->dev, "TE not received for 250ms!\n");
+
+       atomic_set(&td->do_update, 0);
+       dsi_bus_unlock();
+}
+
 static int taal_update(struct omap_dss_device *dssdev,
                                    u16 x, u16 y, u16 w, u16 h)
 {
        struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
        int r;
 
        dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
@@ -853,7 +1135,7 @@ static int taal_update(struct omap_dss_device *dssdev,
                goto err;
        }
 
-       r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h);
+       r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h, true);
        if (r)
                goto err;
 
@@ -861,10 +1143,21 @@ static int taal_update(struct omap_dss_device *dssdev,
        if (r)
                goto err;
 
-       r = omap_dsi_update(dssdev, TCH, x, y, w, h,
-                       taal_framedone_cb, dssdev);
-       if (r)
-               goto err;
+       if (td->te_enabled && panel_data->use_ext_te) {
+               td->update_region.x = x;
+               td->update_region.y = y;
+               td->update_region.w = w;
+               td->update_region.h = h;
+               barrier();
+               schedule_delayed_work(&td->te_timeout_work,
+                               msecs_to_jiffies(250));
+               atomic_set(&td->do_update, 1);
+       } else {
+               r = omap_dsi_update(dssdev, TCH, x, y, w, h,
+                               taal_framedone_cb, dssdev);
+               if (r)
+                       goto err;
+       }
 
        /* note: no bus_unlock here. unlock is in framedone_cb */
        mutex_unlock(&td->lock);
@@ -894,20 +1187,19 @@ static int taal_sync(struct omap_dss_device *dssdev)
 static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
 {
        struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
        int r;
 
-       td->te_enabled = enable;
-
        if (enable)
                r = taal_dcs_write_1(DCS_TEAR_ON, 0);
        else
                r = taal_dcs_write_0(DCS_TEAR_OFF);
 
-       omapdss_dsi_enable_te(dssdev, enable);
+       if (!panel_data->use_ext_te)
+               omapdss_dsi_enable_te(dssdev, enable);
 
-       /* XXX for some reason, DSI TE breaks if we don't wait here.
-        * Panel bug? Needs more studying */
-       msleep(100);
+       if (td->panel_config->sleep.enable_te)
+               msleep(td->panel_config->sleep.enable_te);
 
        return r;
 }
@@ -918,10 +1210,26 @@ static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
        int r;
 
        mutex_lock(&td->lock);
+
+       if (td->te_enabled == enable)
+               goto end;
+
        dsi_bus_lock();
 
-       r = _taal_enable_te(dssdev, enable);
+       if (td->enabled) {
+               r = _taal_enable_te(dssdev, enable);
+               if (r)
+                       goto err;
+       }
+
+       td->te_enabled = enable;
+
+       dsi_bus_unlock();
+end:
+       mutex_unlock(&td->lock);
 
+       return 0;
+err:
        dsi_bus_unlock();
        mutex_unlock(&td->lock);
 
@@ -948,6 +1256,10 @@ static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
        dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
 
        mutex_lock(&td->lock);
+
+       if (td->rotate == rotate)
+               goto end;
+
        dsi_bus_lock();
 
        if (td->enabled) {
@@ -959,6 +1271,7 @@ static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
        td->rotate = rotate;
 
        dsi_bus_unlock();
+end:
        mutex_unlock(&td->lock);
        return 0;
 err:
@@ -987,6 +1300,10 @@ static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
        dev_dbg(&dssdev->dev, "mirror %d\n", enable);
 
        mutex_lock(&td->lock);
+
+       if (td->mirror == enable)
+               goto end;
+
        dsi_bus_lock();
        if (td->enabled) {
                r = taal_set_addr_mode(td->rotate, enable);
@@ -997,6 +1314,7 @@ static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
        td->mirror = enable;
 
        dsi_bus_unlock();
+end:
        mutex_unlock(&td->lock);
        return 0;
 err:
@@ -1024,23 +1342,30 @@ static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
        int r;
 
        mutex_lock(&td->lock);
+
+       if (!td->enabled) {
+               r = -ENODEV;
+               goto err1;
+       }
+
        dsi_bus_lock();
 
        r = taal_dcs_read_1(DCS_GET_ID1, &id1);
        if (r)
-               goto err;
+               goto err2;
        r = taal_dcs_read_1(DCS_GET_ID2, &id2);
        if (r)
-               goto err;
+               goto err2;
        r = taal_dcs_read_1(DCS_GET_ID3, &id3);
        if (r)
-               goto err;
+               goto err2;
 
        dsi_bus_unlock();
        mutex_unlock(&td->lock);
        return 0;
-err:
+err2:
        dsi_bus_unlock();
+err1:
        mutex_unlock(&td->lock);
        return r;
 }
@@ -1128,6 +1453,7 @@ static void taal_esd_work(struct work_struct *work)
        struct taal_data *td = container_of(work, struct taal_data,
                        esd_work.work);
        struct omap_dss_device *dssdev = td->dssdev;
+       struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
        u8 state1, state2;
        int r;
 
@@ -1168,7 +1494,7 @@ static void taal_esd_work(struct work_struct *work)
        }
        /* Self-diagnostics result is also shown on TE GPIO line. We need
         * to re-enable TE after self diagnostics */
-       if (td->use_ext_te && td->te_enabled) {
+       if (td->te_enabled && panel_data->use_ext_te) {
                r = taal_dcs_write_1(DCS_TEAR_ON, 0);
                if (r)
                        goto err;
@@ -1184,6 +1510,7 @@ err:
        dev_err(&dssdev->dev, "performing LCD reset\n");
 
        taal_power_off(dssdev);
+       taal_hw_reset(dssdev);
        taal_power_on(dssdev);
 
        dsi_bus_unlock();
index fa434ca6e4b7fc001dbe416ad91e85b63ae9b47b..e320e67d06f3e80cec9bd8f5008d7eb7a07b3b9c 100644 (file)
@@ -73,8 +73,12 @@ static void toppoly_tdo_panel_power_off(struct omap_dss_device *dssdev)
 
 static int toppoly_tdo_panel_probe(struct omap_dss_device *dssdev)
 {
-       dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-               OMAP_DSS_LCD_IHS;
+       dssdev->panel.config = OMAP_DSS_LCD_TFT |
+                              OMAP_DSS_LCD_IVS |
+                              OMAP_DSS_LCD_IHS |
+                              OMAP_DSS_LCD_IPC |
+                              OMAP_DSS_LCD_ONOFF;
+
        dssdev->panel.timings = toppoly_tdo_panel_timings;
 
        return 0;
index e777e352dbcd8f4050a202323fadfebe2828c4d7..5ecdc000409429f385657c06271426296019e157 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/seq_file.h>
 #include <linux/delay.h>
 #include <linux/workqueue.h>
+#include <linux/hardirq.h>
 
 #include <plat/sram.h>
 #include <plat/clock.h>
@@ -335,7 +336,7 @@ void dispc_save_context(void)
 void dispc_restore_context(void)
 {
        RR(SYSCONFIG);
-       RR(IRQENABLE);
+       /*RR(IRQENABLE);*/
        /*RR(CONTROL);*/
        RR(CONFIG);
        RR(DEFAULT_COLOR0);
@@ -472,6 +473,15 @@ void dispc_restore_context(void)
 
        /* enable last, because LCD & DIGIT enable are here */
        RR(CONTROL);
+
+       /* clear spurious SYNC_LOST_DIGIT interrupts */
+       dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT);
+
+       /*
+        * enable last so IRQs won't trigger before
+        * the context is fully restored
+        */
+       RR(IRQENABLE);
 }
 
 #undef SR
@@ -3019,7 +3029,7 @@ void dispc_fake_vsync_irq(void)
        u32 irqstatus = DISPC_IRQ_VSYNC;
        int i;
 
-       local_irq_disable();
+       WARN_ON(!in_interrupt());
 
        for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
                struct omap_dispc_isr_data *isr_data;
@@ -3031,8 +3041,6 @@ void dispc_fake_vsync_irq(void)
                if (isr_data->mask & irqstatus)
                        isr_data->isr(isr_data->arg, irqstatus);
        }
-
-       local_irq_enable();
 }
 #endif
 
index ef8c8529dda23fba9a97fc5c81970a3d8fe5ca06..22dd7a474f79bd8fae54987dd66f01ac554fb8e6 100644 (file)
@@ -82,6 +82,9 @@ static ssize_t display_upd_mode_store(struct device *dev,
        int val, r;
        enum omap_dss_update_mode mode;
 
+       if (!dssdev->driver->set_update_mode)
+               return -EINVAL;
+
        val = simple_strtoul(buf, NULL, 10);
 
        switch (val) {
@@ -343,7 +346,6 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
        case OMAP_DISPLAY_TYPE_VENC:
        case OMAP_DISPLAY_TYPE_SDI:
                return 24;
-               return 24;
        default:
                BUG();
        }
index 3af207b2bde385989125ad2639b567dc74e01fa2..b3fa3a7db911f40042dacc86fa025b42f6eabc1c 100644 (file)
@@ -165,6 +165,14 @@ struct dsi_reg { u16 idx; };
 #define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25)
 #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30)
 #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31)
+#define DSI_CIO_IRQ_ERROR_MASK \
+       (DSI_CIO_IRQ_ERRSYNCESC1 | DSI_CIO_IRQ_ERRSYNCESC2 | \
+        DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \
+        DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRCONTROL1 | \
+        DSI_CIO_IRQ_ERRCONTROL2 | DSI_CIO_IRQ_ERRCONTROL3 | \
+        DSI_CIO_IRQ_ERRCONTENTIONLP0_1 | DSI_CIO_IRQ_ERRCONTENTIONLP1_1 | \
+        DSI_CIO_IRQ_ERRCONTENTIONLP0_2 | DSI_CIO_IRQ_ERRCONTENTIONLP1_2 | \
+        DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3)
 
 #define DSI_DT_DCS_SHORT_WRITE_0       0x05
 #define DSI_DT_DCS_SHORT_WRITE_1       0x15
@@ -232,13 +240,15 @@ static struct
        unsigned pll_locked;
 
        struct completion bta_completion;
+       void (*bta_callback)(void);
 
        int update_channel;
        struct dsi_update_region update_region;
 
        bool te_enabled;
 
-       struct work_struct framedone_work;
+       struct workqueue_struct *workqueue;
+
        void (*framedone_callback)(int, void *);
        void *framedone_data;
 
@@ -509,9 +519,13 @@ void dsi_irq_handler(void)
                dss_collect_irq_stats(vcstatus, dsi.irq_stats.vc_irqs[i]);
 #endif
 
-               if (vcstatus & DSI_VC_IRQ_BTA)
+               if (vcstatus & DSI_VC_IRQ_BTA) {
                        complete(&dsi.bta_completion);
 
+                       if (dsi.bta_callback)
+                               dsi.bta_callback();
+               }
+
                if (vcstatus & DSI_VC_IRQ_ERROR_MASK) {
                        DSSERR("DSI VC(%d) error, vc irqstatus %x\n",
                                       i, vcstatus);
@@ -536,8 +550,12 @@ void dsi_irq_handler(void)
                /* flush posted write */
                dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS);
 
-               DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus);
-               print_irq_status_cio(ciostatus);
+               if (ciostatus & DSI_CIO_IRQ_ERROR_MASK) {
+                       DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus);
+                       print_irq_status_cio(ciostatus);
+               } else if (debug_irq) {
+                       print_irq_status_cio(ciostatus);
+               }
        }
 
        dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK);
@@ -584,11 +602,8 @@ static void _dsi_initialize_irq(void)
        for (i = 0; i < 4; ++i)
                dsi_write_reg(DSI_VC_IRQENABLE(i), l);
 
-       /* XXX zonda responds incorrectly, causing control error:
-          Exit from LP-ESC mode to LP11 uses wrong transition states on the
-          data lines LP0 and LN0. */
-       dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE,
-                       -1 & (~DSI_CIO_IRQ_ERRCONTROL2));
+       l = DSI_CIO_IRQ_ERROR_MASK;
+       dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, l);
 }
 
 static u32 dsi_get_errors(void)
@@ -1098,6 +1113,7 @@ int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk,
        if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) {
                DSSERR("PLL not coming out of reset.\n");
                r = -ENODEV;
+               dispc_pck_free_enable(0);
                goto err1;
        }
 
@@ -1740,42 +1756,52 @@ static void dsi_vc_initial_config(int channel)
        dsi.vc[channel].mode = DSI_VC_MODE_L4;
 }
 
-static void dsi_vc_config_l4(int channel)
+static int dsi_vc_config_l4(int channel)
 {
        if (dsi.vc[channel].mode == DSI_VC_MODE_L4)
-               return;
+               return 0;
 
        DSSDBGF("%d", channel);
 
        dsi_vc_enable(channel, 0);
 
-       if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */
+       /* VC_BUSY */
+       if (wait_for_bit_change(DSI_VC_CTRL(channel), 15, 0) != 0) {
                DSSERR("vc(%d) busy when trying to config for L4\n", channel);
+               return -EIO;
+       }
 
        REG_FLD_MOD(DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */
 
        dsi_vc_enable(channel, 1);
 
        dsi.vc[channel].mode = DSI_VC_MODE_L4;
+
+       return 0;
 }
 
-static void dsi_vc_config_vp(int channel)
+static int dsi_vc_config_vp(int channel)
 {
        if (dsi.vc[channel].mode == DSI_VC_MODE_VP)
-               return;
+               return 0;
 
        DSSDBGF("%d", channel);
 
        dsi_vc_enable(channel, 0);
 
-       if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */
+       /* VC_BUSY */
+       if (wait_for_bit_change(DSI_VC_CTRL(channel), 15, 0) != 0) {
                DSSERR("vc(%d) busy when trying to config for VP\n", channel);
+               return -EIO;
+       }
 
        REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 1, 1); /* SOURCE, 1 = video port */
 
        dsi_vc_enable(channel, 1);
 
        dsi.vc[channel].mode = DSI_VC_MODE_VP;
+
+       return 0;
 }
 
 
@@ -1854,19 +1880,19 @@ static u16 dsi_vc_flush_receive_data(int channel)
                u32 val;
                u8 dt;
                val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
-               DSSDBG("\trawval %#08x\n", val);
+               DSSERR("\trawval %#08x\n", val);
                dt = FLD_GET(val, 5, 0);
                if (dt == DSI_DT_RX_ACK_WITH_ERR) {
                        u16 err = FLD_GET(val, 23, 8);
                        dsi_show_rx_ack_with_err(err);
                } else if (dt == DSI_DT_RX_SHORT_READ_1) {
-                       DSSDBG("\tDCS short response, 1 byte: %#x\n",
+                       DSSERR("\tDCS short response, 1 byte: %#x\n",
                                        FLD_GET(val, 23, 8));
                } else if (dt == DSI_DT_RX_SHORT_READ_2) {
-                       DSSDBG("\tDCS short response, 2 byte: %#x\n",
+                       DSSERR("\tDCS short response, 2 byte: %#x\n",
                                        FLD_GET(val, 23, 8));
                } else if (dt == DSI_DT_RX_DCS_LONG_READ) {
-                       DSSDBG("\tDCS long response, len %d\n",
+                       DSSERR("\tDCS long response, len %d\n",
                                        FLD_GET(val, 23, 8));
                        dsi_vc_flush_long_data(channel);
                } else {
@@ -2087,6 +2113,13 @@ int dsi_vc_dcs_write(int channel, u8 *data, int len)
        if (r)
                goto err;
 
+       if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {    /* RX_FIFO_NOT_EMPTY */
+               DSSERR("rx fifo not empty after write, dumping data:\n");
+               dsi_vc_flush_receive_data(channel);
+               r = -EIO;
+               goto err;
+       }
+
        return 0;
 err:
        DSSERR("dsi_vc_dcs_write(ch %d, cmd 0x%02x, len %d) failed\n",
@@ -2233,11 +2266,12 @@ int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data)
 }
 EXPORT_SYMBOL(dsi_vc_dcs_read_1);
 
-int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u16 *data)
+int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u8 *data1, u8 *data2)
 {
+       u8 buf[2];
        int r;
 
-       r = dsi_vc_dcs_read(channel, dcs_cmd, (u8 *)data, 2);
+       r = dsi_vc_dcs_read(channel, dcs_cmd, buf, 2);
 
        if (r < 0)
                return r;
@@ -2245,231 +2279,122 @@ int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u16 *data)
        if (r != 2)
                return -EIO;
 
+       *data1 = buf[0];
+       *data2 = buf[1];
+
        return 0;
 }
 EXPORT_SYMBOL(dsi_vc_dcs_read_2);
 
 int dsi_vc_set_max_rx_packet_size(int channel, u16 len)
 {
-       int r;
-       r = dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE,
+       return dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE,
                        len, 0);
-
-       if (r)
-               return r;
-
-       r = dsi_vc_send_bta_sync(channel);
-
-       return r;
 }
 EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size);
 
-static void dsi_set_lp_rx_timeout(unsigned long ns)
+static void dsi_set_lp_rx_timeout(unsigned ticks, bool x4, bool x16)
 {
-       u32 r;
-       unsigned x4, x16;
        unsigned long fck;
-       unsigned long ticks;
+       unsigned long total_ticks;
+       u32 r;
 
-       /* ticks in DSI_FCK */
+       BUG_ON(ticks > 0x1fff);
 
+       /* ticks in DSI_FCK */
        fck = dsi_fclk_rate();
-       ticks = (fck / 1000 / 1000) * ns / 1000;
-       x4 = 0;
-       x16 = 0;
-
-       if (ticks > 0x1fff) {
-               ticks = (fck / 1000 / 1000) * ns / 1000 / 4;
-               x4 = 1;
-               x16 = 0;
-       }
-
-       if (ticks > 0x1fff) {
-               ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
-               x4 = 0;
-               x16 = 1;
-       }
-
-       if (ticks > 0x1fff) {
-               ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16);
-               x4 = 1;
-               x16 = 1;
-       }
-
-       if (ticks > 0x1fff) {
-               DSSWARN("LP_TX_TO over limit, setting it to max\n");
-               ticks = 0x1fff;
-               x4 = 1;
-               x16 = 1;
-       }
 
        r = dsi_read_reg(DSI_TIMING2);
        r = FLD_MOD(r, 1, 15, 15);      /* LP_RX_TO */
-       r = FLD_MOD(r, x16, 14, 14);    /* LP_RX_TO_X16 */
-       r = FLD_MOD(r, x4, 13, 13);     /* LP_RX_TO_X4 */
+       r = FLD_MOD(r, x16 ? 1 : 0, 14, 14);    /* LP_RX_TO_X16 */
+       r = FLD_MOD(r, x4 ? 1 : 0, 13, 13);     /* LP_RX_TO_X4 */
        r = FLD_MOD(r, ticks, 12, 0);   /* LP_RX_COUNTER */
        dsi_write_reg(DSI_TIMING2, r);
 
-       DSSDBG("LP_RX_TO %lu ns (%#lx ticks%s%s)\n",
-                       (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
-                       (fck / 1000 / 1000),
-                       ticks, x4 ? " x4" : "", x16 ? " x16" : "");
+       total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
+
+       DSSDBG("LP_RX_TO %lu ticks (%#x%s%s) = %lu ns\n",
+                       total_ticks,
+                       ticks, x4 ? " x4" : "", x16 ? " x16" : "",
+                       (total_ticks * 1000) / (fck / 1000 / 1000));
 }
 
-static void dsi_set_ta_timeout(unsigned long ns)
+static void dsi_set_ta_timeout(unsigned ticks, bool x8, bool x16)
 {
-       u32 r;
-       unsigned x8, x16;
        unsigned long fck;
-       unsigned long ticks;
+       unsigned long total_ticks;
+       u32 r;
+
+       BUG_ON(ticks > 0x1fff);
 
        /* ticks in DSI_FCK */
        fck = dsi_fclk_rate();
-       ticks = (fck / 1000 / 1000) * ns / 1000;
-       x8 = 0;
-       x16 = 0;
-
-       if (ticks > 0x1fff) {
-               ticks = (fck / 1000 / 1000) * ns / 1000 / 8;
-               x8 = 1;
-               x16 = 0;
-       }
-
-       if (ticks > 0x1fff) {
-               ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
-               x8 = 0;
-               x16 = 1;
-       }
-
-       if (ticks > 0x1fff) {
-               ticks = (fck / 1000 / 1000) * ns / 1000 / (8 * 16);
-               x8 = 1;
-               x16 = 1;
-       }
-
-       if (ticks > 0x1fff) {
-               DSSWARN("TA_TO over limit, setting it to max\n");
-               ticks = 0x1fff;
-               x8 = 1;
-               x16 = 1;
-       }
 
        r = dsi_read_reg(DSI_TIMING1);
        r = FLD_MOD(r, 1, 31, 31);      /* TA_TO */
-       r = FLD_MOD(r, x16, 30, 30);    /* TA_TO_X16 */
-       r = FLD_MOD(r, x8, 29, 29);     /* TA_TO_X8 */
+       r = FLD_MOD(r, x16 ? 1 : 0, 30, 30);    /* TA_TO_X16 */
+       r = FLD_MOD(r, x8 ? 1 : 0, 29, 29);     /* TA_TO_X8 */
        r = FLD_MOD(r, ticks, 28, 16);  /* TA_TO_COUNTER */
        dsi_write_reg(DSI_TIMING1, r);
 
-       DSSDBG("TA_TO %lu ns (%#lx ticks%s%s)\n",
-                       (ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1) * 1000) /
-                       (fck / 1000 / 1000),
-                       ticks, x8 ? " x8" : "", x16 ? " x16" : "");
+       total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1);
+
+       DSSDBG("TA_TO %lu ticks (%#x%s%s) = %lu ns\n",
+                       total_ticks,
+                       ticks, x8 ? " x8" : "", x16 ? " x16" : "",
+                       (total_ticks * 1000) / (fck / 1000 / 1000));
 }
 
-static void dsi_set_stop_state_counter(unsigned long ns)
+static void dsi_set_stop_state_counter(unsigned ticks, bool x4, bool x16)
 {
-       u32 r;
-       unsigned x4, x16;
        unsigned long fck;
-       unsigned long ticks;
+       unsigned long total_ticks;
+       u32 r;
 
-       /* ticks in DSI_FCK */
+       BUG_ON(ticks > 0x1fff);
 
+       /* ticks in DSI_FCK */
        fck = dsi_fclk_rate();
-       ticks = (fck / 1000 / 1000) * ns / 1000;
-       x4 = 0;
-       x16 = 0;
-
-       if (ticks > 0x1fff) {
-               ticks = (fck / 1000 / 1000) * ns / 1000 / 4;
-               x4 = 1;
-               x16 = 0;
-       }
-
-       if (ticks > 0x1fff) {
-               ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
-               x4 = 0;
-               x16 = 1;
-       }
-
-       if (ticks > 0x1fff) {
-               ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16);
-               x4 = 1;
-               x16 = 1;
-       }
-
-       if (ticks > 0x1fff) {
-               DSSWARN("STOP_STATE_COUNTER_IO over limit, "
-                               "setting it to max\n");
-               ticks = 0x1fff;
-               x4 = 1;
-               x16 = 1;
-       }
 
        r = dsi_read_reg(DSI_TIMING1);
        r = FLD_MOD(r, 1, 15, 15);      /* FORCE_TX_STOP_MODE_IO */
-       r = FLD_MOD(r, x16, 14, 14);    /* STOP_STATE_X16_IO */
-       r = FLD_MOD(r, x4, 13, 13);     /* STOP_STATE_X4_IO */
+       r = FLD_MOD(r, x16 ? 1 : 0, 14, 14);    /* STOP_STATE_X16_IO */
+       r = FLD_MOD(r, x4 ? 1 : 0, 13, 13);     /* STOP_STATE_X4_IO */
        r = FLD_MOD(r, ticks, 12, 0);   /* STOP_STATE_COUNTER_IO */
        dsi_write_reg(DSI_TIMING1, r);
 
-       DSSDBG("STOP_STATE_COUNTER %lu ns (%#lx ticks%s%s)\n",
-                       (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
-                       (fck / 1000 / 1000),
-                       ticks, x4 ? " x4" : "", x16 ? " x16" : "");
+       total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
+
+       DSSDBG("STOP_STATE_COUNTER %lu ticks (%#x%s%s) = %lu ns\n",
+                       total_ticks,
+                       ticks, x4 ? " x4" : "", x16 ? " x16" : "",
+                       (total_ticks * 1000) / (fck / 1000 / 1000));
 }
 
-static void dsi_set_hs_tx_timeout(unsigned long ns)
+static void dsi_set_hs_tx_timeout(unsigned ticks, bool x4, bool x16)
 {
-       u32 r;
-       unsigned x4, x16;
        unsigned long fck;
-       unsigned long ticks;
+       unsigned long total_ticks;
+       u32 r;
 
-       /* ticks in TxByteClkHS */
+       BUG_ON(ticks > 0x1fff);
 
+       /* ticks in TxByteClkHS */
        fck = dsi_get_txbyteclkhs();
-       ticks = (fck / 1000 / 1000) * ns / 1000;
-       x4 = 0;
-       x16 = 0;
-
-       if (ticks > 0x1fff) {
-               ticks = (fck / 1000 / 1000) * ns / 1000 / 4;
-               x4 = 1;
-               x16 = 0;
-       }
-
-       if (ticks > 0x1fff) {
-               ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
-               x4 = 0;
-               x16 = 1;
-       }
-
-       if (ticks > 0x1fff) {
-               ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16);
-               x4 = 1;
-               x16 = 1;
-       }
-
-       if (ticks > 0x1fff) {
-               DSSWARN("HS_TX_TO over limit, setting it to max\n");
-               ticks = 0x1fff;
-               x4 = 1;
-               x16 = 1;
-       }
 
        r = dsi_read_reg(DSI_TIMING2);
        r = FLD_MOD(r, 1, 31, 31);      /* HS_TX_TO */
-       r = FLD_MOD(r, x16, 30, 30);    /* HS_TX_TO_X16 */
-       r = FLD_MOD(r, x4, 29, 29);     /* HS_TX_TO_X8 (4 really) */
+       r = FLD_MOD(r, x16 ? 1 : 0, 30, 30);    /* HS_TX_TO_X16 */
+       r = FLD_MOD(r, x4 ? 1 : 0, 29, 29);     /* HS_TX_TO_X8 (4 really) */
        r = FLD_MOD(r, ticks, 28, 16);  /* HS_TX_TO_COUNTER */
        dsi_write_reg(DSI_TIMING2, r);
 
-       DSSDBG("HS_TX_TO %lu ns (%#lx ticks%s%s)\n",
-                       (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
-                       (fck / 1000 / 1000),
-                       ticks, x4 ? " x4" : "", x16 ? " x16" : "");
+       total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
+
+       DSSDBG("HS_TX_TO %lu ticks (%#x%s%s) = %lu ns\n",
+                       total_ticks,
+                       ticks, x4 ? " x4" : "", x16 ? " x16" : "",
+                       (total_ticks * 1000) / (fck / 1000 / 1000));
 }
 static int dsi_proto_config(struct omap_dss_device *dssdev)
 {
@@ -2487,10 +2412,10 @@ static int dsi_proto_config(struct omap_dss_device *dssdev)
                        DSI_FIFO_SIZE_32);
 
        /* XXX what values for the timeouts? */
-       dsi_set_stop_state_counter(1000);
-       dsi_set_ta_timeout(6400000);
-       dsi_set_lp_rx_timeout(48000);
-       dsi_set_hs_tx_timeout(1000000);
+       dsi_set_stop_state_counter(0x1000, false, false);
+       dsi_set_ta_timeout(0x1fff, true, true);
+       dsi_set_lp_rx_timeout(0x1fff, true, true);
+       dsi_set_hs_tx_timeout(0x1fff, true, true);
 
        switch (dssdev->ctrl.pixel_size) {
        case 16:
@@ -2759,6 +2684,7 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
        unsigned packet_payload;
        unsigned packet_len;
        u32 l;
+       int r;
        const unsigned channel = dsi.update_channel;
        /* line buffer is 1024 x 24bits */
        /* XXX: for some reason using full buffer size causes considerable TX
@@ -2809,8 +2735,9 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
 
        dsi_perf_mark_start();
 
-       schedule_delayed_work(&dsi.framedone_timeout_work,
+       r = queue_delayed_work(dsi.workqueue, &dsi.framedone_timeout_work,
                        msecs_to_jiffies(250));
+       BUG_ON(r == 0);
 
        dss_start_update(dssdev);
 
@@ -2834,62 +2761,70 @@ static void dsi_te_timeout(unsigned long arg)
 }
 #endif
 
-static void dsi_framedone_timeout_work_callback(struct work_struct *work)
+static void dsi_handle_framedone(int error)
 {
-       int r;
        const int channel = dsi.update_channel;
 
-       DSSERR("Framedone not received for 250ms!\n");
+       cancel_delayed_work(&dsi.framedone_timeout_work);
+
+       dsi_vc_disable_bta_irq(channel);
 
        /* SIDLEMODE back to smart-idle */
        dispc_enable_sidle();
 
+       dsi.bta_callback = NULL;
+
        if (dsi.te_enabled) {
                /* enable LP_RX_TO again after the TE */
                REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
        }
 
-       /* Send BTA after the frame. We need this for the TE to work, as TE
-        * trigger is only sent for BTAs without preceding packet. Thus we need
-        * to BTA after the pixel packets so that next BTA will cause TE
-        * trigger.
-        *
-        * This is not needed when TE is not in use, but we do it anyway to
-        * make sure that the transfer has been completed. It would be more
-        * optimal, but more complex, to wait only just before starting next
-        * transfer. */
-       r = dsi_vc_send_bta_sync(channel);
-       if (r)
-               DSSERR("BTA after framedone failed\n");
-
        /* RX_FIFO_NOT_EMPTY */
        if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {
                DSSERR("Received error during frame transfer:\n");
                dsi_vc_flush_receive_data(channel);
+               if (!error)
+                       error = -EIO;
        }
 
-       dsi.framedone_callback(-ETIMEDOUT, dsi.framedone_data);
+       dsi.framedone_callback(error, dsi.framedone_data);
+
+       if (!error)
+               dsi_perf_show("DISPC");
 }
 
-static void dsi_framedone_irq_callback(void *data, u32 mask)
+static void dsi_framedone_timeout_work_callback(struct work_struct *work)
 {
-       /* Note: 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.
-        */
+       /* XXX While extremely unlikely, we could get FRAMEDONE interrupt after
+        * 250ms which would conflict with this timeout work. What should be
+        * done is first cancel the transfer on the HW, and then cancel the
+        * possibly scheduled framedone work. However, cancelling the transfer
+        * on the HW is buggy, and would probably require resetting the whole
+        * DSI */
 
-       /* SIDLEMODE back to smart-idle */
-       dispc_enable_sidle();
+       DSSERR("Framedone not received for 250ms!\n");
 
-       schedule_work(&dsi.framedone_work);
+       dsi_handle_framedone(-ETIMEDOUT);
 }
 
-static void dsi_handle_framedone(void)
+static void dsi_framedone_bta_callback(void)
+{
+       dsi_handle_framedone(0);
+
+#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC
+       dispc_fake_vsync_irq();
+#endif
+}
+
+static void dsi_framedone_irq_callback(void *data, u32 mask)
 {
-       int r;
        const int channel = dsi.update_channel;
+       int r;
 
-       DSSDBG("FRAMEDONE\n");
+       /* Note: 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.
+        */
 
        if (dsi.te_enabled) {
                /* enable LP_RX_TO again after the TE */
@@ -2904,37 +2839,30 @@ static void dsi_handle_framedone(void)
         * This is not needed when TE is not in use, but we do it anyway to
         * make sure that the transfer has been completed. It would be more
         * optimal, but more complex, to wait only just before starting next
-        * transfer. */
-       r = dsi_vc_send_bta_sync(channel);
-       if (r)
-               DSSERR("BTA after framedone failed\n");
-
-       /* RX_FIFO_NOT_EMPTY */
-       if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {
-               DSSERR("Received error during frame transfer:\n");
-               dsi_vc_flush_receive_data(channel);
-       }
-
-#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC
-       dispc_fake_vsync_irq();
-#endif
-}
-
-static void dsi_framedone_work_callback(struct work_struct *work)
-{
-       DSSDBGF();
+        * transfer.
+        *
+        * Also, as there's no interrupt telling when the transfer has been
+        * done and the channel could be reconfigured, the only way is to
+        * busyloop until TE_SIZE is zero. With BTA we can do this
+        * asynchronously.
+        * */
 
-       cancel_delayed_work_sync(&dsi.framedone_timeout_work);
+       dsi.bta_callback = dsi_framedone_bta_callback;
 
-       dsi_handle_framedone();
+       barrier();
 
-       dsi_perf_show("DISPC");
+       dsi_vc_enable_bta_irq(channel);
 
-       dsi.framedone_callback(0, dsi.framedone_data);
+       r = dsi_vc_send_bta(channel);
+       if (r) {
+               DSSERR("BTA after framedone failed\n");
+               dsi_handle_framedone(-EIO);
+       }
 }
 
 int omap_dsi_prepare_update(struct omap_dss_device *dssdev,
-                                   u16 *x, u16 *y, u16 *w, u16 *h)
+                                   u16 *x, u16 *y, u16 *w, u16 *h,
+                                   bool enlarge_update_area)
 {
        u16 dw, dh;
 
@@ -2958,7 +2886,8 @@ int omap_dsi_prepare_update(struct omap_dss_device *dssdev,
        dsi_perf_mark_setup();
 
        if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
-               dss_setup_partial_planes(dssdev, x, y, w, h);
+               dss_setup_partial_planes(dssdev, x, y, w, h,
+                               enlarge_update_area);
                dispc_set_lcd_size(*w, *h);
        }
 
@@ -2973,6 +2902,12 @@ int omap_dsi_update(struct omap_dss_device *dssdev,
 {
        dsi.update_channel = channel;
 
+       /* OMAP DSS cannot send updates of odd widths.
+        * omap_dsi_prepare_update() makes the widths even, but add a BUG_ON
+        * here to make sure we catch erroneous updates. Otherwise we'll only
+        * see rather obscure HW error happening, as DSS halts. */
+       BUG_ON(x % 2 == 1);
+
        if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
                dsi.framedone_callback = callback;
                dsi.framedone_data = data;
@@ -2985,7 +2920,12 @@ int omap_dsi_update(struct omap_dss_device *dssdev,
 
                dsi_update_screen_dispc(dssdev, x, y, w, h);
        } else {
-               dsi_update_screen_l4(dssdev, x, y, w, h);
+               int r;
+
+               r = dsi_update_screen_l4(dssdev, x, y, w, h);
+               if (r)
+                       return r;
+
                dsi_perf_show("L4");
                callback(0, data);
        }
@@ -3048,8 +2988,10 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev)
        cinfo.regm3 = dssdev->phy.dsi.div.regm3;
        cinfo.regm4 = dssdev->phy.dsi.div.regm4;
        r = dsi_calc_clock_rates(&cinfo);
-       if (r)
+       if (r) {
+               DSSERR("Failed to calc dsi clocks\n");
                return r;
+       }
 
        r = dsi_pll_set_clock_div(&cinfo);
        if (r) {
@@ -3147,6 +3089,13 @@ err0:
 
 static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev)
 {
+       /* disable interface */
+       dsi_if_enable(0);
+       dsi_vc_enable(0, 0);
+       dsi_vc_enable(1, 0);
+       dsi_vc_enable(2, 0);
+       dsi_vc_enable(3, 0);
+
        dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK);
        dss_select_dsi_clk_source(DSS_SRC_DSS1_ALWON_FCLK);
        dsi_complexio_uninit();
@@ -3257,7 +3206,7 @@ void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
        burst_size_bytes = 16 * 32 / 8;
 
        *fifo_high = fifo_size - burst_size_bytes;
-       *fifo_low = fifo_size - burst_size_bytes * 8;
+       *fifo_low = fifo_size - burst_size_bytes * 2;
 }
 
 int dsi_init_display(struct omap_dss_device *dssdev)
@@ -3274,6 +3223,18 @@ int dsi_init_display(struct omap_dss_device *dssdev)
        return 0;
 }
 
+void dsi_wait_dsi1_pll_active(void)
+{
+       if (wait_for_bit_change(DSI_PLL_STATUS, 7, 1) != 1)
+               DSSERR("DSI1 PLL clock not active\n");
+}
+
+void dsi_wait_dsi2_pll_active(void)
+{
+       if (wait_for_bit_change(DSI_PLL_STATUS, 8, 1) != 1)
+               DSSERR("DSI2 PLL clock not active\n");
+}
+
 int dsi_init(struct platform_device *pdev)
 {
        u32 rev;
@@ -3292,7 +3253,10 @@ int dsi_init(struct platform_device *pdev)
        mutex_init(&dsi.lock);
        sema_init(&dsi.bus_lock, 1);
 
-       INIT_WORK(&dsi.framedone_work, dsi_framedone_work_callback);
+       dsi.workqueue = create_singlethread_workqueue("dsi");
+       if (dsi.workqueue == NULL)
+               return -ENOMEM;
+
        INIT_DELAYED_WORK_DEFERRABLE(&dsi.framedone_timeout_work,
                        dsi_framedone_timeout_work_callback);
 
@@ -3328,6 +3292,7 @@ int dsi_init(struct platform_device *pdev)
 err2:
        iounmap(dsi.base);
 err1:
+       destroy_workqueue(dsi.workqueue);
        return r;
 }
 
@@ -3335,6 +3300,8 @@ void dsi_exit(void)
 {
        iounmap(dsi.base);
 
+       destroy_workqueue(dsi.workqueue);
+
        DSSDBG("omap_dsi_exit\n");
 }
 
index 24b18258654f09fb64ae0b5887f515893028627e..77c3621c9171867a340ae146039e08aed1cc7ffd 100644 (file)
@@ -265,6 +265,9 @@ void dss_select_dispc_clk_source(enum dss_clk_source clk_src)
 
        b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1;
 
+       if (clk_src == DSS_SRC_DSI1_PLL_FCLK)
+               dsi_wait_dsi1_pll_active();
+
        REG_FLD_MOD(DSS_CONTROL, b, 0, 0);      /* DISPC_CLK_SWITCH */
 
        dss.dispc_clk_source = clk_src;
@@ -279,6 +282,9 @@ void dss_select_dsi_clk_source(enum dss_clk_source clk_src)
 
        b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1;
 
+       if (clk_src == DSS_SRC_DSI2_PLL_FCLK)
+               dsi_wait_dsi2_pll_active();
+
        REG_FLD_MOD(DSS_CONTROL, b, 1, 1);      /* DSI_CLK_SWITCH */
 
        dss.dsi_clk_source = clk_src;
index 786f433fd571ce60a367962d40e1152e3bb89098..5c7940d5f282f3137be4f8451c76a5692609b3a0 100644 (file)
@@ -199,7 +199,8 @@ 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);
+                               u16 *x, u16 *y, u16 *w, u16 *h,
+                               bool enlarge_update_area);
 void dss_start_update(struct omap_dss_device *dssdev);
 
 /* overlay */
@@ -281,6 +282,8 @@ 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);
+void dsi_wait_dsi1_pll_active(void);
+void dsi_wait_dsi2_pll_active(void);
 #else
 static inline int dsi_init(struct platform_device *pdev)
 {
@@ -289,6 +292,12 @@ static inline int dsi_init(struct platform_device *pdev)
 static inline void dsi_exit(void)
 {
 }
+static inline void dsi_wait_dsi1_pll_active(void)
+{
+}
+static inline void dsi_wait_dsi2_pll_active(void)
+{
+}
 #endif
 
 /* DPI */
index 9e1fbe531bf070312ed55ac15560d8e6a1c1ff74..6a649ab5539ea3492c9f85599c93c891581b3dbb 100644 (file)
@@ -440,6 +440,10 @@ struct manager_cache_data {
 
        /* manual update region */
        u16 x, y, w, h;
+
+       /* enlarge the update area if the update area contains scaled
+        * overlays */
+       bool enlarge_update_area;
 };
 
 static struct {
@@ -525,7 +529,7 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
        int i;
        struct omap_dss_device *dssdev = mgr->device;
 
-       if (!dssdev)
+       if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
                return 0;
 
        if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) {
@@ -596,11 +600,14 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
        int r;
        int i;
 
-       if (!ovl->manager || !ovl->manager->device)
+       if (!ovl->manager)
                return 0;
 
        dssdev = ovl->manager->device;
 
+       if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+               return 0;
+
        if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) {
                irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
                channel = OMAP_DSS_CHANNEL_DIGIT;
@@ -718,6 +725,7 @@ static int configure_overlay(enum omap_plane plane)
        u16 x, y, w, h;
        u32 paddr;
        int r;
+       u16 orig_w, orig_h, orig_outw, orig_outh;
 
        DSSDBGF("%d", plane);
 
@@ -738,8 +746,16 @@ static int configure_overlay(enum omap_plane plane)
        outh = c->out_height == 0 ? c->height : c->out_height;
        paddr = c->paddr;
 
+       orig_w = w;
+       orig_h = h;
+       orig_outw = outw;
+       orig_outh = outh;
+
        if (c->manual_update && mc->do_manual_update) {
                unsigned bpp;
+               unsigned scale_x_m = w, scale_x_d = outw;
+               unsigned scale_y_m = h, scale_y_d = outh;
+
                /* If the overlay is outside the update region, disable it */
                if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h,
                                        x, y, outw, outh)) {
@@ -770,38 +786,47 @@ static int configure_overlay(enum omap_plane plane)
                        BUG();
                }
 
-               if (dispc_is_overlay_scaled(c)) {
-                       /* If the overlay is scaled, the update area has
-                        * already been enlarged to cover the whole overlay. We
-                        * only need to adjust x/y here */
-                       x = c->pos_x - mc->x;
-                       y = c->pos_y - mc->y;
+               if (mc->x > c->pos_x) {
+                       x = 0;
+                       outw -= (mc->x - c->pos_x);
+                       paddr += (mc->x - c->pos_x) *
+                               scale_x_m / scale_x_d * bpp / 8;
                } else {
-                       if (mc->x > c->pos_x) {
-                               x = 0;
-                               w -= (mc->x - c->pos_x);
-                               paddr += (mc->x - c->pos_x) * bpp / 8;
-                       } else {
-                               x = c->pos_x - mc->x;
-                       }
-
-                       if (mc->y > c->pos_y) {
-                               y = 0;
-                               h -= (mc->y - c->pos_y);
-                               paddr += (mc->y - c->pos_y) * c->screen_width *
-                                       bpp / 8;
-                       } else {
-                               y = c->pos_y - mc->y;
-                       }
-
-                       if (mc->w < (x+w))
-                               w -= (x+w) - (mc->w);
+                       x = c->pos_x - mc->x;
+               }
 
-                       if (mc->h < (y+h))
-                               h -= (y+h) - (mc->h);
+               if (mc->y > c->pos_y) {
+                       y = 0;
+                       outh -= (mc->y - c->pos_y);
+                       paddr += (mc->y - c->pos_y) *
+                               scale_y_m / scale_y_d *
+                               c->screen_width * bpp / 8;
+               } else {
+                       y = c->pos_y - mc->y;
+               }
 
-                       outw = w;
-                       outh = h;
+               if (mc->w < (x + outw))
+                       outw -= (x + outw) - (mc->w);
+
+               if (mc->h < (y + outh))
+                       outh -= (y + outh) - (mc->h);
+
+               w = w * outw / orig_outw;
+               h = h * outh / orig_outh;
+
+               /* YUV mode overlay's input width has to be even and the
+                * algorithm above may adjust the width to be odd.
+                *
+                * Here we adjust the width if needed, preferring to increase
+                * the width if the original width was bigger.
+                */
+               if ((w & 1) &&
+                               (c->color_mode == OMAP_DSS_COLOR_YUV2 ||
+                                c->color_mode == OMAP_DSS_COLOR_UYVY)) {
+                       if (orig_w > w)
+                               w += 1;
+                       else
+                               w -= 1;
                }
        }
 
@@ -960,7 +985,7 @@ static void make_even(u16 *x, u16 *w)
 /* Configure dispc for partial update. Return possibly modified update
  * area */
 void dss_setup_partial_planes(struct omap_dss_device *dssdev,
-               u16 *xi, u16 *yi, u16 *wi, u16 *hi)
+               u16 *xi, u16 *yi, u16 *wi, u16 *hi, bool enlarge_update_area)
 {
        struct overlay_cache_data *oc;
        struct manager_cache_data *mc;
@@ -969,6 +994,7 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev,
        int i;
        u16 x, y, w, h;
        unsigned long flags;
+       bool area_changed;
 
        x = *xi;
        y = *yi;
@@ -989,73 +1015,91 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev,
 
        spin_lock_irqsave(&dss_cache.lock, flags);
 
-       /* We need to show the whole overlay if it is scaled. So look for
-        * those, and make the update area larger if found.
-        * Also mark the overlay cache dirty */
-       for (i = 0; i < num_ovls; ++i) {
-               unsigned x1, y1, x2, y2;
-               unsigned outw, outh;
+       /*
+        * Execute the outer loop until the inner loop has completed
+        * once without increasing the update area. This will ensure that
+        * all scaled overlays end up completely within the update area.
+        */
+       do {
+               area_changed = false;
 
-               oc = &dss_cache.overlay_cache[i];
+               /* We need to show the whole overlay if it is scaled. So look
+                * for those, and make the update area larger if found.
+                * Also mark the overlay cache dirty */
+               for (i = 0; i < num_ovls; ++i) {
+                       unsigned x1, y1, x2, y2;
+                       unsigned outw, outh;
 
-               if (oc->channel != mgr->id)
-                       continue;
+                       oc = &dss_cache.overlay_cache[i];
 
-               oc->dirty = true;
+                       if (oc->channel != mgr->id)
+                               continue;
 
-               if (!oc->enabled)
-                       continue;
+                       oc->dirty = true;
 
-               if (!dispc_is_overlay_scaled(oc))
-                       continue;
+                       if (!enlarge_update_area)
+                               continue;
 
-               outw = oc->out_width == 0 ? oc->width : oc->out_width;
-               outh = oc->out_height == 0 ? oc->height : oc->out_height;
+                       if (!oc->enabled)
+                               continue;
 
-               /* is the overlay outside the update region? */
-               if (!rectangle_intersects(x, y, w, h,
-                                       oc->pos_x, oc->pos_y,
-                                       outw, outh))
-                       continue;
+                       if (!dispc_is_overlay_scaled(oc))
+                               continue;
 
-               /* if the overlay totally inside the update region? */
-               if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh,
-                                       x, y, w, h))
-                       continue;
+                       outw = oc->out_width == 0 ?
+                               oc->width : oc->out_width;
+                       outh = oc->out_height == 0 ?
+                               oc->height : oc->out_height;
+
+                       /* is the overlay outside the update region? */
+                       if (!rectangle_intersects(x, y, w, h,
+                                               oc->pos_x, oc->pos_y,
+                                               outw, outh))
+                               continue;
 
-               if (x > oc->pos_x)
-                       x1 = oc->pos_x;
-               else
-                       x1 = x;
+                       /* if the overlay totally inside the update region? */
+                       if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh,
+                                               x, y, w, h))
+                               continue;
 
-               if (y > oc->pos_y)
-                       y1 = oc->pos_y;
-               else
-                       y1 = y;
+                       if (x > oc->pos_x)
+                               x1 = oc->pos_x;
+                       else
+                               x1 = x;
 
-               if ((x + w) < (oc->pos_x + outw))
-                       x2 = oc->pos_x + outw;
-               else
-                       x2 = x + w;
+                       if (y > oc->pos_y)
+                               y1 = oc->pos_y;
+                       else
+                               y1 = y;
 
-               if ((y + h) < (oc->pos_y + outh))
-                       y2 = oc->pos_y + outh;
-               else
-                       y2 = y + h;
+                       if ((x + w) < (oc->pos_x + outw))
+                               x2 = oc->pos_x + outw;
+                       else
+                               x2 = x + w;
 
-               x = x1;
-               y = y1;
-               w = x2 - x1;
-               h = y2 - y1;
+                       if ((y + h) < (oc->pos_y + outh))
+                               y2 = oc->pos_y + outh;
+                       else
+                               y2 = y + h;
 
-               make_even(&x, &w);
+                       x = x1;
+                       y = y1;
+                       w = x2 - x1;
+                       h = y2 - y1;
 
-               DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n",
+                       make_even(&x, &w);
+
+                       DSSDBG("changing upd area due to ovl(%d) "
+                              "scaling %d,%d %dx%d\n",
                                i, x, y, w, h);
-       }
+
+                       area_changed = true;
+               }
+       } while (area_changed);
 
        mc = &dss_cache.manager_cache[mgr->id];
        mc->do_manual_update = true;
+       mc->enlarge_update_area = enlarge_update_area;
        mc->x = x;
        mc->y = y;
        mc->w = w;
index 82336583adeffa7333975ba71d3796a604e1981b..244dca81a399a2efd047538846d53080d428bfaf 100644 (file)
@@ -65,7 +65,7 @@ static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,
                for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
                        mgr = omap_dss_get_overlay_manager(i);
 
-                       if (strncmp(buf, mgr->name, len) == 0)
+                       if (sysfs_streq(buf, mgr->name))
                                break;
 
                        mgr = NULL;
index cc23f53cc62d49968055d62359218043b562a674..bbe62464e92df533309f20ea59f4e2dbbe6c6d3e 100644 (file)
@@ -886,7 +886,7 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev,
                return -EINVAL;
 
        if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
-               dss_setup_partial_planes(dssdev, x, y, w, h);
+               dss_setup_partial_planes(dssdev, x, y, w, h, true);
                dispc_set_lcd_size(*w, *h);
        }
 
index 9c7361871d786b4685e7469fa0eb99b5fa16c4b0..6f435450987e7596d1e81963ea53bcc82bcd3f42 100644 (file)
 
 #include "omapfb.h"
 
+static u8 get_mem_idx(struct omapfb_info *ofbi)
+{
+       if (ofbi->id == ofbi->region->id)
+               return 0;
+
+       return OMAPFB_MEM_IDX_ENABLED | ofbi->region->id;
+}
+
+static struct omapfb2_mem_region *get_mem_region(struct omapfb_info *ofbi,
+                                                u8 mem_idx)
+{
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+
+       if (mem_idx & OMAPFB_MEM_IDX_ENABLED)
+               mem_idx &= OMAPFB_MEM_IDX_MASK;
+       else
+               mem_idx = ofbi->id;
+
+       if (mem_idx >= fbdev->num_fbs)
+               return NULL;
+
+       return &fbdev->regions[mem_idx];
+}
+
 static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
 {
        struct omapfb_info *ofbi = FB2OFB(fbi);
        struct omapfb2_device *fbdev = ofbi->fbdev;
        struct omap_overlay *ovl;
-       struct omap_overlay_info info;
+       struct omap_overlay_info old_info;
+       struct omapfb2_mem_region *old_rg, *new_rg;
        int r = 0;
 
        DBG("omapfb_setup_plane\n");
@@ -52,36 +77,106 @@ static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
        /* XXX uses only the first overlay */
        ovl = ofbi->overlays[0];
 
-       if (pi->enabled && !ofbi->region.size) {
+       old_rg = ofbi->region;
+       new_rg = get_mem_region(ofbi, pi->mem_idx);
+       if (!new_rg) {
+               r = -EINVAL;
+               goto out;
+       }
+
+       /* Take the locks in a specific order to keep lockdep happy */
+       if (old_rg->id < new_rg->id) {
+               omapfb_get_mem_region(old_rg);
+               omapfb_get_mem_region(new_rg);
+       } else if (new_rg->id < old_rg->id) {
+               omapfb_get_mem_region(new_rg);
+               omapfb_get_mem_region(old_rg);
+       } else
+               omapfb_get_mem_region(old_rg);
+
+       if (pi->enabled && !new_rg->size) {
                /*
                 * This plane's memory was freed, can't enable it
                 * until it's reallocated.
                 */
                r = -EINVAL;
-               goto out;
+               goto put_mem;
        }
 
-       ovl->get_overlay_info(ovl, &info);
+       ovl->get_overlay_info(ovl, &old_info);
 
-       info.pos_x = pi->pos_x;
-       info.pos_y = pi->pos_y;
-       info.out_width = pi->out_width;
-       info.out_height = pi->out_height;
-       info.enabled = pi->enabled;
+       if (old_rg != new_rg) {
+               ofbi->region = new_rg;
+               set_fb_fix(fbi);
+       }
 
-       r = ovl->set_overlay_info(ovl, &info);
-       if (r)
-               goto out;
+       if (pi->enabled) {
+               struct omap_overlay_info info;
 
-       if (ovl->manager) {
-               r = ovl->manager->apply(ovl->manager);
+               r = omapfb_setup_overlay(fbi, ovl, pi->pos_x, pi->pos_y,
+                       pi->out_width, pi->out_height);
                if (r)
-                       goto out;
+                       goto undo;
+
+               ovl->get_overlay_info(ovl, &info);
+
+               if (!info.enabled) {
+                       info.enabled = pi->enabled;
+                       r = ovl->set_overlay_info(ovl, &info);
+                       if (r)
+                               goto undo;
+               }
+       } else {
+               struct omap_overlay_info info;
+
+               ovl->get_overlay_info(ovl, &info);
+
+               info.enabled = pi->enabled;
+               info.pos_x = pi->pos_x;
+               info.pos_y = pi->pos_y;
+               info.out_width = pi->out_width;
+               info.out_height = pi->out_height;
+
+               r = ovl->set_overlay_info(ovl, &info);
+               if (r)
+                       goto undo;
        }
 
-out:
-       if (r)
-               dev_err(fbdev->dev, "setup_plane failed\n");
+       if (ovl->manager)
+               ovl->manager->apply(ovl->manager);
+
+       /* Release the locks in a specific order to keep lockdep happy */
+       if (old_rg->id > new_rg->id) {
+               omapfb_put_mem_region(old_rg);
+               omapfb_put_mem_region(new_rg);
+       } else if (new_rg->id > old_rg->id) {
+               omapfb_put_mem_region(new_rg);
+               omapfb_put_mem_region(old_rg);
+       } else
+               omapfb_put_mem_region(old_rg);
+
+       return 0;
+
+ undo:
+       if (old_rg != new_rg) {
+               ofbi->region = old_rg;
+               set_fb_fix(fbi);
+       }
+
+       ovl->set_overlay_info(ovl, &old_info);
+ put_mem:
+       /* Release the locks in a specific order to keep lockdep happy */
+       if (old_rg->id > new_rg->id) {
+               omapfb_put_mem_region(old_rg);
+               omapfb_put_mem_region(new_rg);
+       } else if (new_rg->id > old_rg->id) {
+               omapfb_put_mem_region(new_rg);
+               omapfb_put_mem_region(old_rg);
+       } else
+               omapfb_put_mem_region(old_rg);
+ out:
+       dev_err(fbdev->dev, "setup_plane failed\n");
+
        return r;
 }
 
@@ -92,8 +187,8 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
        if (ofbi->num_overlays != 1) {
                memset(pi, 0, sizeof(*pi));
        } else {
-               struct omap_overlay_info *ovli;
                struct omap_overlay *ovl;
+               struct omap_overlay_info *ovli;
 
                ovl = ofbi->overlays[0];
                ovli = &ovl->info;
@@ -103,6 +198,7 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
                pi->enabled = ovli->enabled;
                pi->channel_out = 0; /* xxx */
                pi->mirror = 0;
+               pi->mem_idx = get_mem_idx(ofbi);
                pi->out_width = ovli->out_width;
                pi->out_height = ovli->out_height;
        }
@@ -115,7 +211,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
        struct omapfb_info *ofbi = FB2OFB(fbi);
        struct omapfb2_device *fbdev = ofbi->fbdev;
        struct omapfb2_mem_region *rg;
-       int r, i;
+       int r = 0, i;
        size_t size;
 
        if (mi->type > OMAPFB_MEMTYPE_MAX)
@@ -123,22 +219,44 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
 
        size = PAGE_ALIGN(mi->size);
 
-       rg = &ofbi->region;
+       rg = ofbi->region;
 
-       for (i = 0; i < ofbi->num_overlays; i++) {
-               if (ofbi->overlays[i]->info.enabled)
-                       return -EBUSY;
+       down_write_nested(&rg->lock, rg->id);
+       atomic_inc(&rg->lock_count);
+
+       if (atomic_read(&rg->map_count)) {
+               r = -EBUSY;
+               goto out;
+       }
+
+       for (i = 0; i < fbdev->num_fbs; i++) {
+               struct omapfb_info *ofbi2 = FB2OFB(fbdev->fbs[i]);
+               int j;
+
+               if (ofbi2->region != rg)
+                       continue;
+
+               for (j = 0; j < ofbi2->num_overlays; j++) {
+                       if (ofbi2->overlays[j]->info.enabled) {
+                               r = -EBUSY;
+                               goto out;
+                       }
+               }
        }
 
        if (rg->size != size || rg->type != mi->type) {
                r = omapfb_realloc_fbmem(fbi, size, mi->type);
                if (r) {
                        dev_err(fbdev->dev, "realloc fbmem failed\n");
-                       return r;
+                       goto out;
                }
        }
 
-       return 0;
+ out:
+       atomic_dec(&rg->lock_count);
+       up_write(&rg->lock);
+
+       return r;
 }
 
 static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
@@ -146,12 +264,14 @@ static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
        struct omapfb_info *ofbi = FB2OFB(fbi);
        struct omapfb2_mem_region *rg;
 
-       rg = &ofbi->region;
+       rg = omapfb_get_mem_region(ofbi->region);
        memset(mi, 0, sizeof(*mi));
 
        mi->size = rg->size;
        mi->type = rg->type;
 
+       omapfb_put_mem_region(rg);
+
        return 0;
 }
 
@@ -490,6 +610,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
                struct omapfb_vram_info         vram_info;
                struct omapfb_tearsync_info     tearsync_info;
                struct omapfb_display_info      display_info;
+               u32                             crt;
        } p;
 
        int r = 0;
@@ -648,6 +769,17 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
                        r = -EFAULT;
                break;
 
+       case FBIO_WAITFORVSYNC:
+               if (get_user(p.crt, (__u32 __user *)arg)) {
+                       r = -EFAULT;
+                       break;
+               }
+               if (p.crt != 0) {
+                       r = -ENODEV;
+                       break;
+               }
+               /* FALLTHROUGH */
+
        case OMAPFB_WAITFORVSYNC:
                DBG("ioctl WAITFORVSYNC\n");
                if (!display) {
@@ -738,7 +870,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
                        break;
                }
 
-               if (!display->driver->enable_te) {
+               if (!display || !display->driver->enable_te) {
                        r = -ENODEV;
                        break;
                }
index 4b4506da96da360ad706f0fbc59716dd8013cd8d..04034d410d6dee2e3d806c9a21793923585b7690 100644 (file)
@@ -157,7 +157,7 @@ static void fill_fb(struct fb_info *fbi)
 
 static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot)
 {
-       const struct vrfb *vrfb = &ofbi->region.vrfb;
+       const struct vrfb *vrfb = &ofbi->region->vrfb;
        unsigned offset;
 
        switch (rot) {
@@ -185,27 +185,27 @@ static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot)
 static u32 omapfb_get_region_rot_paddr(const struct omapfb_info *ofbi, int rot)
 {
        if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
-               return ofbi->region.vrfb.paddr[rot]
+               return ofbi->region->vrfb.paddr[rot]
                        + omapfb_get_vrfb_offset(ofbi, rot);
        } else {
-               return ofbi->region.paddr;
+               return ofbi->region->paddr;
        }
 }
 
 static u32 omapfb_get_region_paddr(const struct omapfb_info *ofbi)
 {
        if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
-               return ofbi->region.vrfb.paddr[0];
+               return ofbi->region->vrfb.paddr[0];
        else
-               return ofbi->region.paddr;
+               return ofbi->region->paddr;
 }
 
 static void __iomem *omapfb_get_region_vaddr(const struct omapfb_info *ofbi)
 {
        if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
-               return ofbi->region.vrfb.vaddr[0];
+               return ofbi->region->vrfb.vaddr[0];
        else
-               return ofbi->region.vaddr;
+               return ofbi->region->vaddr;
 }
 
 static struct omapfb_colormode omapfb_colormodes[] = {
@@ -450,7 +450,7 @@ static int check_vrfb_fb_size(unsigned long region_size,
 static int check_fb_size(const struct omapfb_info *ofbi,
                struct fb_var_screeninfo *var)
 {
-       unsigned long max_frame_size = ofbi->region.size;
+       unsigned long max_frame_size = ofbi->region->size;
        int bytespp = var->bits_per_pixel >> 3;
        unsigned long line_size = var->xres_virtual * bytespp;
 
@@ -497,7 +497,7 @@ static int check_fb_size(const struct omapfb_info *ofbi,
 static int setup_vrfb_rotation(struct fb_info *fbi)
 {
        struct omapfb_info *ofbi = FB2OFB(fbi);
-       struct omapfb2_mem_region *rg = &ofbi->region;
+       struct omapfb2_mem_region *rg = ofbi->region;
        struct vrfb *vrfb = &rg->vrfb;
        struct fb_var_screeninfo *var = &fbi->var;
        struct fb_fix_screeninfo *fix = &fbi->fix;
@@ -558,9 +558,9 @@ static int setup_vrfb_rotation(struct fb_info *fbi)
                return r;
 
        /* used by open/write in fbmem.c */
-       fbi->screen_base = ofbi->region.vrfb.vaddr[0];
+       fbi->screen_base = ofbi->region->vrfb.vaddr[0];
 
-       fix->smem_start = ofbi->region.vrfb.paddr[0];
+       fix->smem_start = ofbi->region->vrfb.paddr[0];
 
        switch (var->nonstd) {
        case OMAPFB_COLOR_YUV422:
@@ -599,7 +599,7 @@ void set_fb_fix(struct fb_info *fbi)
        struct fb_fix_screeninfo *fix = &fbi->fix;
        struct fb_var_screeninfo *var = &fbi->var;
        struct omapfb_info *ofbi = FB2OFB(fbi);
-       struct omapfb2_mem_region *rg = &ofbi->region;
+       struct omapfb2_mem_region *rg = ofbi->region;
 
        DBG("set_fb_fix\n");
 
@@ -668,8 +668,7 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
 
        DBG("check_fb_var %d\n", ofbi->id);
 
-       if (ofbi->region.size == 0)
-               return 0;
+       WARN_ON(!atomic_read(&ofbi->region->lock_count));
 
        r = fb_mode_to_dss_mode(var, &mode);
        if (r) {
@@ -684,13 +683,14 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
                }
        }
 
-       if (var->rotate < 0 || var->rotate > 3)
+       if (var->rotate > 3)
                return -EINVAL;
 
        if (check_fb_res_bounds(var))
                return -EINVAL;
 
-       if (check_fb_size(ofbi, var))
+       /* When no memory is allocated ignore the size check */
+       if (ofbi->region->size != 0 && check_fb_size(ofbi, var))
                return -EINVAL;
 
        if (var->xres + var->xoffset > var->xres_virtual)
@@ -822,9 +822,43 @@ static unsigned calc_rotation_offset_vrfb(const struct fb_var_screeninfo *var,
        return offset;
 }
 
+static void omapfb_calc_addr(const struct omapfb_info *ofbi,
+                            const struct fb_var_screeninfo *var,
+                            const struct fb_fix_screeninfo *fix,
+                            int rotation, u32 *paddr, void __iomem **vaddr)
+{
+       u32 data_start_p;
+       void __iomem *data_start_v;
+       int offset;
+
+       if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+               data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation);
+               data_start_v = NULL;
+       } else {
+               data_start_p = omapfb_get_region_paddr(ofbi);
+               data_start_v = omapfb_get_region_vaddr(ofbi);
+       }
+
+       if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
+               offset = calc_rotation_offset_vrfb(var, fix, rotation);
+       else
+               offset = calc_rotation_offset_dma(var, fix, rotation);
+
+       data_start_p += offset;
+       data_start_v += offset;
+
+       if (offset)
+               DBG("offset %d, %d = %d\n",
+                   var->xoffset, var->yoffset, offset);
+
+       DBG("paddr %x, vaddr %p\n", data_start_p, data_start_v);
+
+       *paddr = data_start_p;
+       *vaddr = data_start_v;
+}
 
 /* setup overlay according to the fb */
-static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
+int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
                u16 posx, u16 posy, u16 outw, u16 outh)
 {
        int r = 0;
@@ -832,9 +866,8 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
        struct fb_var_screeninfo *var = &fbi->var;
        struct fb_fix_screeninfo *fix = &fbi->fix;
        enum omap_color_mode mode = 0;
-       int offset;
-       u32 data_start_p;
-       void __iomem *data_start_v;
+       u32 data_start_p = 0;
+       void __iomem *data_start_v = NULL;
        struct omap_overlay_info info;
        int xres, yres;
        int screen_width;
@@ -842,6 +875,8 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
        int rotation = var->rotate;
        int i;
 
+       WARN_ON(!atomic_read(&ofbi->region->lock_count));
+
        for (i = 0; i < ofbi->num_overlays; i++) {
                if (ovl != ofbi->overlays[i])
                        continue;
@@ -861,28 +896,9 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
                yres = var->yres;
        }
 
-
-       if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
-               data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation);
-               data_start_v = NULL;
-       } else {
-               data_start_p = omapfb_get_region_paddr(ofbi);
-               data_start_v = omapfb_get_region_vaddr(ofbi);
-       }
-
-       if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
-               offset = calc_rotation_offset_vrfb(var, fix, rotation);
-       else
-               offset = calc_rotation_offset_dma(var, fix, rotation);
-
-       data_start_p += offset;
-       data_start_v += offset;
-
-       if (offset)
-               DBG("offset %d, %d = %d\n",
-                               var->xoffset, var->yoffset, offset);
-
-       DBG("paddr %x, vaddr %p\n", data_start_p, data_start_v);
+       if (ofbi->region->size)
+               omapfb_calc_addr(ofbi, var, fix, rotation,
+                                &data_start_p, &data_start_v);
 
        r = fb_mode_to_dss_mode(var, &mode);
        if (r) {
@@ -954,12 +970,14 @@ int omapfb_apply_changes(struct fb_info *fbi, int init)
                fill_fb(fbi);
 #endif
 
+       WARN_ON(!atomic_read(&ofbi->region->lock_count));
+
        for (i = 0; i < ofbi->num_overlays; i++) {
                ovl = ofbi->overlays[i];
 
                DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id);
 
-               if (ofbi->region.size == 0) {
+               if (ofbi->region->size == 0) {
                        /* the fb is not available. disable the overlay */
                        omapfb_overlay_enable(ovl, 0);
                        if (!init && ovl->manager)
@@ -1007,36 +1025,48 @@ err:
  * DO NOT MODIFY PAR */
 static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
 {
+       struct omapfb_info *ofbi = FB2OFB(fbi);
        int r;
 
        DBG("check_var(%d)\n", FB2OFB(fbi)->id);
 
+       omapfb_get_mem_region(ofbi->region);
+
        r = check_fb_var(fbi, var);
 
+       omapfb_put_mem_region(ofbi->region);
+
        return r;
 }
 
 /* set the video mode according to info->var */
 static int omapfb_set_par(struct fb_info *fbi)
 {
+       struct omapfb_info *ofbi = FB2OFB(fbi);
        int r;
 
        DBG("set_par(%d)\n", FB2OFB(fbi)->id);
 
+       omapfb_get_mem_region(ofbi->region);
+
        set_fb_fix(fbi);
 
        r = setup_vrfb_rotation(fbi);
        if (r)
-               return r;
+               goto out;
 
        r = omapfb_apply_changes(fbi, 0);
 
+ out:
+       omapfb_put_mem_region(ofbi->region);
+
        return r;
 }
 
 static int omapfb_pan_display(struct fb_var_screeninfo *var,
                struct fb_info *fbi)
 {
+       struct omapfb_info *ofbi = FB2OFB(fbi);
        struct fb_var_screeninfo new_var;
        int r;
 
@@ -1052,23 +1082,31 @@ static int omapfb_pan_display(struct fb_var_screeninfo *var,
 
        fbi->var = new_var;
 
+       omapfb_get_mem_region(ofbi->region);
+
        r = omapfb_apply_changes(fbi, 0);
 
+       omapfb_put_mem_region(ofbi->region);
+
        return r;
 }
 
 static void mmap_user_open(struct vm_area_struct *vma)
 {
-       struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data;
+       struct omapfb2_mem_region *rg = vma->vm_private_data;
 
-       atomic_inc(&ofbi->map_count);
+       omapfb_get_mem_region(rg);
+       atomic_inc(&rg->map_count);
+       omapfb_put_mem_region(rg);
 }
 
 static void mmap_user_close(struct vm_area_struct *vma)
 {
-       struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data;
+       struct omapfb2_mem_region *rg = vma->vm_private_data;
 
-       atomic_dec(&ofbi->map_count);
+       omapfb_get_mem_region(rg);
+       atomic_dec(&rg->map_count);
+       omapfb_put_mem_region(rg);
 }
 
 static struct vm_operations_struct mmap_user_ops = {
@@ -1080,9 +1118,11 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
 {
        struct omapfb_info *ofbi = FB2OFB(fbi);
        struct fb_fix_screeninfo *fix = &fbi->fix;
+       struct omapfb2_mem_region *rg;
        unsigned long off;
        unsigned long start;
        u32 len;
+       int r = -EINVAL;
 
        if (vma->vm_end - vma->vm_start == 0)
                return 0;
@@ -1090,12 +1130,14 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
                return -EINVAL;
        off = vma->vm_pgoff << PAGE_SHIFT;
 
+       rg = omapfb_get_mem_region(ofbi->region);
+
        start = omapfb_get_region_paddr(ofbi);
        len = fix->smem_len;
        if (off >= len)
-               return -EINVAL;
+               goto error;
        if ((vma->vm_end - vma->vm_start + off) > len)
-               return -EINVAL;
+               goto error;
 
        off += start;
 
@@ -1105,13 +1147,25 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
        vma->vm_flags |= VM_IO | VM_RESERVED;
        vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
        vma->vm_ops = &mmap_user_ops;
-       vma->vm_private_data = ofbi;
+       vma->vm_private_data = rg;
        if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
-                            vma->vm_end - vma->vm_start, vma->vm_page_prot))
-               return -EAGAIN;
+                              vma->vm_end - vma->vm_start,
+                              vma->vm_page_prot)) {
+               r = -EAGAIN;
+               goto error;
+       }
+
        /* vm_ops.open won't be called for mmap itself. */
-       atomic_inc(&ofbi->map_count);
+       atomic_inc(&rg->map_count);
+
+       omapfb_put_mem_region(rg);
+
        return 0;
+
+ error:
+       omapfb_put_mem_region(ofbi->region);
+
+       return r;
 }
 
 /* Store a single color palette entry into a pseudo palette or the hardware
@@ -1154,11 +1208,6 @@ static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green,
                if (r != 0)
                        break;
 
-               if (regno < 0) {
-                       r = -EINVAL;
-                       break;
-               }
-
                if (regno < 16) {
                        u16 pal;
                        pal = ((red >> (16 - var->red.length)) <<
@@ -1217,6 +1266,9 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
        int do_update = 0;
        int r = 0;
 
+       if (!display)
+               return -EINVAL;
+
        omapfb_lock(fbdev);
 
        switch (blank) {
@@ -1300,7 +1352,9 @@ static void omapfb_free_fbmem(struct fb_info *fbi)
        struct omapfb2_device *fbdev = ofbi->fbdev;
        struct omapfb2_mem_region *rg;
 
-       rg = &ofbi->region;
+       rg = ofbi->region;
+
+       WARN_ON(atomic_read(&rg->map_count));
 
        if (rg->paddr)
                if (omap_vram_free(rg->paddr, rg->size))
@@ -1355,8 +1409,15 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size,
        void __iomem *vaddr;
        int r;
 
-       rg = &ofbi->region;
-       memset(rg, 0, sizeof(*rg));
+       rg = ofbi->region;
+
+       rg->paddr = 0;
+       rg->vaddr = NULL;
+       memset(&rg->vrfb, 0, sizeof rg->vrfb);
+       rg->size = 0;
+       rg->type = 0;
+       rg->alloc = false;
+       rg->map = false;
 
        size = PAGE_ALIGN(size);
 
@@ -1609,7 +1670,7 @@ static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev)
        for (i = 0; i < fbdev->num_fbs; i++) {
                struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
                struct omapfb2_mem_region *rg;
-               rg = &ofbi->region;
+               rg = ofbi->region;
 
                DBG("region%d phys %08x virt %p size=%lu\n",
                                i,
@@ -1626,7 +1687,7 @@ int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type)
        struct omapfb_info *ofbi = FB2OFB(fbi);
        struct omapfb2_device *fbdev = ofbi->fbdev;
        struct omap_dss_device *display = fb2display(fbi);
-       struct omapfb2_mem_region *rg = &ofbi->region;
+       struct omapfb2_mem_region *rg = ofbi->region;
        unsigned long old_size = rg->size;
        unsigned long old_paddr = rg->paddr;
        int old_type = rg->type;
@@ -1709,7 +1770,7 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)
        fbi->flags = FBINFO_FLAG_DEFAULT;
        fbi->pseudo_palette = fbdev->pseudo_palette;
 
-       if (ofbi->region.size == 0) {
+       if (ofbi->region->size == 0) {
                clear_fb_info(fbi);
                return 0;
        }
@@ -1871,6 +1932,10 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
                ofbi->fbdev = fbdev;
                ofbi->id = i;
 
+               ofbi->region = &fbdev->regions[i];
+               ofbi->region->id = i;
+               init_rwsem(&ofbi->region->lock);
+
                /* assign these early, so that fb alloc can use them */
                ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB :
                        OMAP_DSS_ROT_DMA;
@@ -1900,7 +1965,13 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
 
        /* setup fb_infos */
        for (i = 0; i < fbdev->num_fbs; i++) {
-               r = omapfb_fb_init(fbdev, fbdev->fbs[i]);
+               struct fb_info *fbi = fbdev->fbs[i];
+               struct omapfb_info *ofbi = FB2OFB(fbi);
+
+               omapfb_get_mem_region(ofbi->region);
+               r = omapfb_fb_init(fbdev, fbi);
+               omapfb_put_mem_region(ofbi->region);
+
                if (r) {
                        dev_err(fbdev->dev, "failed to setup fb_info\n");
                        return r;
@@ -1921,20 +1992,19 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
        DBG("framebuffers registered\n");
 
        for (i = 0; i < fbdev->num_fbs; i++) {
-               r = omapfb_apply_changes(fbdev->fbs[i], 1);
+               struct fb_info *fbi = fbdev->fbs[i];
+               struct omapfb_info *ofbi = FB2OFB(fbi);
+
+               omapfb_get_mem_region(ofbi->region);
+               r = omapfb_apply_changes(fbi, 1);
+               omapfb_put_mem_region(ofbi->region);
+
                if (r) {
                        dev_err(fbdev->dev, "failed to change mode\n");
                        return r;
                }
        }
 
-       DBG("create sysfs for fbs\n");
-       r = omapfb_create_sysfs(fbdev);
-       if (r) {
-               dev_err(fbdev->dev, "failed to create sysfs entries\n");
-               return r;
-       }
-
        /* Enable fb0 */
        if (fbdev->num_fbs > 0) {
                struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]);
@@ -1968,11 +2038,11 @@ static int omapfb_mode_to_timings(const char *mode_str,
 #ifdef CONFIG_OMAP2_DSS_VENC
        if (strcmp(mode_str, "pal") == 0) {
                *timings = omap_dss_pal_timings;
-               *bpp = 0;
+               *bpp = 24;
                return 0;
        } else if (strcmp(mode_str, "ntsc") == 0) {
                *timings = omap_dss_ntsc_timings;
-               *bpp = 0;
+               *bpp = 24;
                return 0;
        }
 #endif
@@ -2220,6 +2290,13 @@ static int omapfb_probe(struct platform_device *pdev)
                }
        }
 
+       DBG("create sysfs for fbs\n");
+       r = omapfb_create_sysfs(fbdev);
+       if (r) {
+               dev_err(fbdev->dev, "failed to create sysfs entries\n");
+               goto cleanup;
+       }
+
        return 0;
 
 cleanup:
index 5179219128bd031f5e0303dcfce3e443aefab781..6f9c72cd6bb0e52675375e4cd4c2bd168e175701 100644 (file)
@@ -49,6 +49,7 @@ static ssize_t store_rotate_type(struct device *dev,
 {
        struct fb_info *fbi = dev_get_drvdata(dev);
        struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_mem_region *rg;
        enum omap_dss_rotation_type rot_type;
        int r;
 
@@ -64,9 +65,11 @@ static ssize_t store_rotate_type(struct device *dev,
        if (rot_type == ofbi->rotation_type)
                goto out;
 
-       if (ofbi->region.size) {
+       rg = omapfb_get_mem_region(ofbi->region);
+
+       if (rg->size) {
                r = -EBUSY;
-               goto out;
+               goto put_region;
        }
 
        ofbi->rotation_type = rot_type;
@@ -75,6 +78,8 @@ static ssize_t store_rotate_type(struct device *dev,
         * Since the VRAM for this FB is not allocated at the moment we don't
         * need to do any further parameter checking at this point.
         */
+put_region:
+       omapfb_put_mem_region(rg);
 out:
        unlock_fb_info(fbi);
 
@@ -97,7 +102,7 @@ static ssize_t store_mirror(struct device *dev,
 {
        struct fb_info *fbi = dev_get_drvdata(dev);
        struct omapfb_info *ofbi = FB2OFB(fbi);
-       bool mirror;
+       unsigned long mirror;
        int r;
        struct fb_var_screeninfo new_var;
 
@@ -111,6 +116,8 @@ static ssize_t store_mirror(struct device *dev,
 
        ofbi->mirror = mirror;
 
+       omapfb_get_mem_region(ofbi->region);
+
        memcpy(&new_var, &fbi->var, sizeof(new_var));
        r = check_fb_var(fbi, &new_var);
        if (r)
@@ -125,6 +132,8 @@ static ssize_t store_mirror(struct device *dev,
 
        r = count;
 out:
+       omapfb_put_mem_region(ofbi->region);
+
        unlock_fb_info(fbi);
 
        return r;
@@ -263,11 +272,15 @@ static ssize_t store_overlays(struct device *dev, struct device_attribute *attr,
 
                DBG("detaching %d\n", ofbi->overlays[i]->id);
 
+               omapfb_get_mem_region(ofbi->region);
+
                omapfb_overlay_enable(ovl, 0);
 
                if (ovl->manager)
                        ovl->manager->apply(ovl->manager);
 
+               omapfb_put_mem_region(ofbi->region);
+
                for (t = i + 1; t < ofbi->num_overlays; t++) {
                        ofbi->rotation[t-1] = ofbi->rotation[t];
                        ofbi->overlays[t-1] = ofbi->overlays[t];
@@ -300,7 +313,12 @@ static ssize_t store_overlays(struct device *dev, struct device_attribute *attr,
        }
 
        if (added) {
+               omapfb_get_mem_region(ofbi->region);
+
                r = omapfb_apply_changes(fbi, 0);
+
+               omapfb_put_mem_region(ofbi->region);
+
                if (r)
                        goto out;
        }
@@ -388,7 +406,12 @@ static ssize_t store_overlays_rotate(struct device *dev,
                for (i = 0; i < num_ovls; ++i)
                        ofbi->rotation[i] = rotation[i];
 
+               omapfb_get_mem_region(ofbi->region);
+
                r = omapfb_apply_changes(fbi, 0);
+
+               omapfb_put_mem_region(ofbi->region);
+
                if (r)
                        goto out;
 
@@ -408,7 +431,7 @@ static ssize_t show_size(struct device *dev,
        struct fb_info *fbi = dev_get_drvdata(dev);
        struct omapfb_info *ofbi = FB2OFB(fbi);
 
-       return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region.size);
+       return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region->size);
 }
 
 static ssize_t store_size(struct device *dev, struct device_attribute *attr,
@@ -416,6 +439,8 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr,
 {
        struct fb_info *fbi = dev_get_drvdata(dev);
        struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       struct omapfb2_mem_region *rg;
        unsigned long size;
        int r;
        int i;
@@ -425,15 +450,33 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr,
        if (!lock_fb_info(fbi))
                return -ENODEV;
 
-       for (i = 0; i < ofbi->num_overlays; i++) {
-               if (ofbi->overlays[i]->info.enabled) {
-                       r = -EBUSY;
-                       goto out;
+       rg = ofbi->region;
+
+       down_write_nested(&rg->lock, rg->id);
+       atomic_inc(&rg->lock_count);
+
+       if (atomic_read(&rg->map_count)) {
+               r = -EBUSY;
+               goto out;
+       }
+
+       for (i = 0; i < fbdev->num_fbs; i++) {
+               struct omapfb_info *ofbi2 = FB2OFB(fbdev->fbs[i]);
+               int j;
+
+               if (ofbi2->region != rg)
+                       continue;
+
+               for (j = 0; j < ofbi2->num_overlays; j++) {
+                       if (ofbi2->overlays[j]->info.enabled) {
+                               r = -EBUSY;
+                               goto out;
+                       }
                }
        }
 
-       if (size != ofbi->region.size) {
-               r = omapfb_realloc_fbmem(fbi, size, ofbi->region.type);
+       if (size != ofbi->region->size) {
+               r = omapfb_realloc_fbmem(fbi, size, ofbi->region->type);
                if (r) {
                        dev_err(dev, "realloc fbmem failed\n");
                        goto out;
@@ -442,6 +485,9 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr,
 
        r = count;
 out:
+       atomic_dec(&rg->lock_count);
+       up_write(&rg->lock);
+
        unlock_fb_info(fbi);
 
        return r;
@@ -453,7 +499,7 @@ static ssize_t show_phys(struct device *dev,
        struct fb_info *fbi = dev_get_drvdata(dev);
        struct omapfb_info *ofbi = FB2OFB(fbi);
 
-       return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region.paddr);
+       return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region->paddr);
 }
 
 static ssize_t show_virt(struct device *dev,
@@ -462,7 +508,7 @@ static ssize_t show_virt(struct device *dev,
        struct fb_info *fbi = dev_get_drvdata(dev);
        struct omapfb_info *ofbi = FB2OFB(fbi);
 
-       return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region.vaddr);
+       return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region->vaddr);
 }
 
 static struct device_attribute omapfb_attrs[] = {
index cd54fdbfd8bb6bdcd2bec59815cd2be7747b3a33..1305fc9880badaaf62699a7ee916cff62034658c 100644 (file)
@@ -27,6 +27,8 @@
 #define DEBUG
 #endif
 
+#include <linux/rwsem.h>
+
 #include <plat/display.h>
 
 #ifdef DEBUG
@@ -44,6 +46,7 @@ extern unsigned int omapfb_debug;
 #define OMAPFB_MAX_OVL_PER_FB 3
 
 struct omapfb2_mem_region {
+       int             id;
        u32             paddr;
        void __iomem    *vaddr;
        struct vrfb     vrfb;
@@ -51,13 +54,15 @@ struct omapfb2_mem_region {
        u8              type;           /* OMAPFB_PLANE_MEM_* */
        bool            alloc;          /* allocated by the driver */
        bool            map;            /* kernel mapped by the driver */
+       atomic_t        map_count;
+       struct rw_semaphore lock;
+       atomic_t        lock_count;
 };
 
 /* appended to fb_info */
 struct omapfb_info {
        int id;
-       struct omapfb2_mem_region region;
-       atomic_t map_count;
+       struct omapfb2_mem_region *region;
        int num_overlays;
        struct omap_overlay *overlays[OMAPFB_MAX_OVL_PER_FB];
        struct omapfb2_device *fbdev;
@@ -76,6 +81,7 @@ struct omapfb2_device {
 
        unsigned num_fbs;
        struct fb_info *fbs[10];
+       struct omapfb2_mem_region regions[10];
 
        unsigned num_displays;
        struct omap_dss_device *displays[10];
@@ -117,6 +123,9 @@ int omapfb_update_window(struct fb_info *fbi,
 int dss_mode_to_fb_mode(enum omap_color_mode dssmode,
                        struct fb_var_screeninfo *var);
 
+int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
+               u16 posx, u16 posy, u16 outw, u16 outh);
+
 /* find the display connected to this fb, if any */
 static inline struct omap_dss_device *fb2display(struct fb_info *fbi)
 {
@@ -148,8 +157,24 @@ static inline int omapfb_overlay_enable(struct omap_overlay *ovl,
        struct omap_overlay_info info;
 
        ovl->get_overlay_info(ovl, &info);
+       if (info.enabled == enable)
+               return 0;
        info.enabled = enable;
        return ovl->set_overlay_info(ovl, &info);
 }
 
+static inline struct omapfb2_mem_region *
+omapfb_get_mem_region(struct omapfb2_mem_region *rg)
+{
+       down_read_nested(&rg->lock, rg->id);
+       atomic_inc(&rg->lock_count);
+       return rg;
+}
+
+static inline void omapfb_put_mem_region(struct omapfb2_mem_region *rg)
+{
+       atomic_dec(&rg->lock_count);
+       up_read(&rg->lock);
+}
+
 #endif
index 7e4cd616bcb57c0e51358073873502c9c417c3ef..c0b018790f07a3e2bc858eba8ea7acaa73a006a5 100644 (file)
@@ -85,6 +85,9 @@
 #define OMAPFB_MEMTYPE_SRAM            1
 #define OMAPFB_MEMTYPE_MAX             1
 
+#define OMAPFB_MEM_IDX_ENABLED 0x80
+#define OMAPFB_MEM_IDX_MASK    0x7f
+
 enum omapfb_color_format {
        OMAPFB_COLOR_RGB565 = 0,
        OMAPFB_COLOR_YUV422,
@@ -136,7 +139,7 @@ struct omapfb_plane_info {
        __u8  enabled;
        __u8  channel_out;
        __u8  mirror;
-       __u8  reserved1;
+       __u8  mem_idx;
        __u32 out_width;
        __u32 out_height;
        __u32 reserved2[12];