diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index a205ed4..2fd08d8 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -114,6 +114,9 @@ struct wb_priv_data {
bool info_dirty;
struct omap_dss_writeback_info info;
+ /* If true, writeback output is enabled */
+ bool updating;
+
/*
* If true in memory to memory mode, a manager is connected to it.
* However, it may not be active.@@ -285,6 +288,7 @@ static int dss_check_settings_apply(struct omap_overlay_manager *mgr)
static bool need_isr(void)
{
const int num_mgrs = dss_feat_get_num_mgrs();
+ const int num_wbs = dss_feat_get_num_wbs();
int i;
for (i = 0; i < num_mgrs; ++i) {@@ -362,6 +366,20 @@ static bool need_isr(void)
}
}
+ for (i = 0; i < num_wbs; i++) {
+ struct omap_dss_output *wb = omap_dss_get_writeback();
+ struct wb_priv_data *wp = get_wb_priv(wb);
+
+ if (!wp->enabled)
+ continue;
+
+ if (wb_manual_update(wb)) {
+ /* to catch FRAMEDONEWB */
+ if (wp->updating)
+ return true;
+ }
+ }
+
return false;
}
@@ -841,6 +859,7 @@ static void dss_apply_irq_handler(void *data, u32 mask);
static void dss_register_vsync_isr(void)
{
const int num_mgrs = dss_feat_get_num_mgrs();
+ const int num_wbs = dss_feat_get_num_wbs();
u32 mask;
int r, i;
@@ -851,6 +870,9 @@ static void dss_register_vsync_isr(void)
for (i = 0; i < num_mgrs; ++i)
mask |= dispc_mgr_get_framedone_irq(i);
+ for (i = 0; i < num_wbs; i++)
+ mask |= dispc_wb_get_framedone_irq();
+
r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
WARN_ON(r);
@@ -860,6 +882,7 @@ static void dss_register_vsync_isr(void)
static void dss_unregister_vsync_isr(void)
{
const int num_mgrs = dss_feat_get_num_mgrs();
+ const int num_wbs = dss_feat_get_num_wbs();
u32 mask;
int r, i;
@@ -870,6 +893,9 @@ static void dss_unregister_vsync_isr(void)
for (i = 0; i < num_mgrs; ++i)
mask |= dispc_mgr_get_framedone_irq(i);
+ for (i = 0; i < num_wbs; i++)
+ mask |= dispc_wb_get_framedone_irq();
+
r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask);
WARN_ON(r);
@@ -879,6 +905,7 @@ static void dss_unregister_vsync_isr(void)
static void dss_apply_irq_handler(void *data, u32 mask)
{
const int num_mgrs = dss_feat_get_num_mgrs();
+ const int num_wbs = dss_feat_get_num_wbs();
int i;
bool extra_updating;
@@ -906,6 +933,16 @@ static void dss_apply_irq_handler(void *data, u32 mask)
}
}
+ for (i = 0; i < num_wbs; i++) {
+ struct omap_dss_output *wb = omap_dss_get_writeback();
+ struct wb_priv_data *wp = get_wb_priv(wb);
+
+ if (!wp->enabled)
+ continue;
+
+ wp->updating = dispc_wb_is_enabled();
+ }
+
dss_write_regs();
dss_set_go_bits();
@@ -1685,6 +1722,11 @@ void dss_wb_disable(struct omap_dss_output *wb)
if (!wp->enabled)
goto out;
+ if (wp->updating) {
+ DSSERR("can't disable writeback in the middle of an update\n");
+ goto out;
+ }
+
spin_lock_irqsave(&data_lock, flags);
wp->updating = false;@@ -1694,3 +1736,44 @@ void dss_wb_disable(struct omap_dss_output *wb)
out:
mutex_unlock(&apply_lock);
}
+
+/*
+ * When doing a writeback mem to mem update, the updating field for writeback
+ * private data is true, but for manager private data the updating field is
+ * false. This is because a manager isn't really 'enabled' in HW, it's the
+ * writeback pipeline which initiates the transfer.
+ */
+void dss_wb_start_update(struct omap_dss_output *wb)
+{
+ struct omap_overlay_manager *mgr = wb->manager;
+ struct wb_priv_data *wp = get_wb_priv(wb);
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&data_lock, flags);
+
+ WARN_ON(wp->updating);
+
+ /* add some similar check for writeback later */
+
+ r = dss_check_settings(mgr);
+ if (r) {
+ DSSERR("cannot start manual update: illegal configuration\n");
+ spin_unlock_irqrestore(&data_lock, flags);
+ return;
+ }
+
+ dss_wb_write_regs(wb);
+
+ dss_mgr_write_regs(mgr);
+ dss_mgr_write_regs_extra(mgr);
+
+ wp->updating = true;
+
+ if (!dss_data.irq_enabled && need_isr())
+ dss_register_vsync_isr();
+
+ dispc_wb_enable(true);
+
+ spin_unlock_irqrestore(&data_lock, flags);
+}diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index f9e7074..8c70b08 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -217,6 +217,7 @@ void dss_wb_get_info(struct omap_dss_output *wb,
int omap_dss_wb_apply(struct omap_dss_output *wb);
int dss_wb_enable(struct omap_dss_output *wb);
void dss_wb_disable(struct omap_dss_output *wb);
+void dss_wb_start_update(struct omap_dss_output *wb);
/* output */
void dss_register_output(struct omap_dss_output *out);
diff --git a/drivers/video/omap2/dss/writeback.c b/drivers/video/omap2/dss/writeback.c
index 09394fb..7a25e99 100644
--- a/drivers/video/omap2/dss/writeback.c
+++ b/drivers/video/omap2/dss/writeback.c
@@ -20,6 +20,8 @@
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/module.h>
+#include <linux/semaphore.h>
+#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <video/omapdss.h>
@@ -27,7 +29,15 @@
#include "dss.h"
struct writeback_data {
+ struct platform_device *pdev;
+
struct mutex lock;
+ struct semaphore bus_lock;
+
+ void (*framedone_callback)(int, void *);
+ void *framedone_data;
+
+ struct delayed_work framedone_timeout_work;
struct omap_dss_output output;
/*@@ -71,6 +81,32 @@ int dss_writeback_calc_channel_in(struct omap_overlay_manager *mgr,
return 0;
}
+void omapdss_writeback_bus_lock(struct omap_dss_output *wb)
+{
+ struct platform_device *wbdev = writeback_get_wbdev_from_output(wb);
+ struct writeback_data *wb_data = writeback_get_drv_data(wbdev);
+
+ down(&wb_data->bus_lock);
+}
+EXPORT_SYMBOL(omapdss_writeback_bus_lock);
+
+void omapdss_writeback_bus_unlock(struct omap_dss_output *wb)
+{
+ struct platform_device *wbdev = writeback_get_wbdev_from_output(wb);
+ struct writeback_data *wb_data = writeback_get_drv_data(wbdev);
+
+ up(&wb_data->bus_lock);
+}
+EXPORT_SYMBOL(omapdss_writeback_bus_unlock);
+
+static bool writeback_bus_is_locked(struct omap_dss_output *wb)
+{
+ struct platform_device *wbdev = writeback_get_wbdev_from_output(wb);
+ struct writeback_data *wb_data = writeback_get_drv_data(wbdev);
+
+ return wb_data->bus_lock.count = 0;
+}
+
void omapdss_writeback_set_input_size(struct omap_dss_output *wb, u16 w, u16 h)
{
struct platform_device *wbdev = writeback_get_wbdev_from_output(wb);@@ -88,11 +124,39 @@ void omapdss_writeback_set_input_size(struct omap_dss_output *wb, u16 w, u16 h)
}
EXPORT_SYMBOL(omapdss_writeback_set_input_size);
+static void writeback_handle_framedone(struct platform_device *wbdev, int error)
+{
+ struct writeback_data *wb_data = writeback_get_drv_data(wbdev);
+
+ wb_data->framedone_callback(error, wb_data->framedone_data);
+}
+
+static void writeback_framedone_timeout_work_callback(struct work_struct *work)
+{
+ struct writeback_data *wb_data = container_of(work,
+ struct writeback_data, framedone_timeout_work.work);
+
+ DSSERR("FRAMEDONE WB not received for 100ms\n");
+
+ writeback_handle_framedone(wb_data->pdev, -ETIMEDOUT);
+}
+
+static void writeback_framedone_irq_callback(void *data, u32 mask)
+{
+ struct platform_device *wbdev = (struct platform_device *) data;
+ struct writeback_data *wb_data = writeback_get_drv_data(wbdev);
+
+ cancel_delayed_work(&wb_data->framedone_timeout_work);
+
+ writeback_handle_framedone(wb_data->pdev, 0);
+}
+
static void writeback_config_manager(struct omap_dss_output *wb)
{
struct platform_device *wbdev = writeback_get_wbdev_from_output(wb);
struct writeback_data *wb_data = writeback_get_drv_data(wbdev);
struct dss_lcd_mgr_config lcd_config;
+ int r;
dss_mgr_set_timings(wb->manager, &wb_data->input_timings);
@@ -110,6 +174,11 @@ static void writeback_config_manager(struct omap_dss_output *wb)
*/
dss_mgr_set_lcd_config(wb->manager, &lcd_config);
+ r = omap_dispc_register_isr(writeback_framedone_irq_callback,
+ (void *) wbdev, DISPC_IRQ_FRAMEDONEWB);
+ if (r)
+ DSSERR("Failed to register for FRAMEDONEWB irq\n");
+
dss_mgr_enable(wb->manager);
}
@@ -119,6 +188,8 @@ int omapdss_writeback_enable(struct omap_dss_output *wb)
struct writeback_data *wb_data = writeback_get_drv_data(wbdev);
int r;
+ WARN_ON(!writeback_bus_is_locked(wb));
+
mutex_lock(&wb_data->lock);
r = dispc_runtime_get();
@@ -139,18 +210,48 @@ void omapdss_writeback_disable(struct omap_dss_output *wb)
{
struct platform_device *wbdev = writeback_get_wbdev_from_output(wb);
struct writeback_data *wb_data = writeback_get_drv_data(wbdev);
+ int r;
+
+ WARN_ON(!writeback_bus_is_locked(wb));
mutex_lock(&wb_data->lock);
dss_wb_disable(wb);
dss_mgr_disable(wb->manager);
+ r = omap_dispc_unregister_isr(writeback_framedone_irq_callback,
+ (void *) wbdev, DISPC_IRQ_FRAMEDONEWB);
+ if (r)
+ DSSERR("Failed to unregister FRAMEDONEWB irq\n");
+
dispc_runtime_put();
mutex_unlock(&wb_data->lock);
}
EXPORT_SYMBOL(omapdss_writeback_disable);
+int omapdss_writeback_update(struct omap_dss_output *wb,
+ void (*callback)(int, void *), void *data)
+{
+ struct platform_device *wbdev = writeback_get_wbdev_from_output(wb);
+ struct writeback_data *wb_data = writeback_get_drv_data(wbdev);
+ int r;
+
+ WARN_ON(!writeback_bus_is_locked(wb));
+
+ wb_data->framedone_callback = callback;
+ wb_data->framedone_data = data;
+
+ r = schedule_delayed_work(&wb_data->framedone_timeout_work,
+ msecs_to_jiffies(100));
+ BUG_ON(r = 0);
+
+ dss_wb_start_update(wb);
+
+ return 0;
+}
+EXPORT_SYMBOL(omapdss_writeback_update);
+
int omapdss_writeback_apply(struct omap_dss_output *wb)
{
return omap_dss_wb_apply(wb);@@ -199,7 +300,12 @@ static int __init omap_writeback_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, wb_data);
+ wb_data->pdev = pdev;
mutex_init(&wb_data->lock);
+ sema_init(&wb_data->bus_lock, 1);
+
+ INIT_DEFERRABLE_WORK(&wb_data->framedone_timeout_work,
+ writeback_framedone_timeout_work_callback);
/* initialize with dummy timings */
wb_data->input_timings = (struct omap_video_timings)
diff --git a/include/video/omapdss.h b/include/video/omapdss.h
index f63c0cb..c7f3ac7 100644
--- a/include/video/omapdss.h
+++ b/include/video/omapdss.h
@@ -837,9 +837,13 @@ void omapdss_rfbi_set_data_lines(struct omap_dss_device *dssdev,
void omapdss_rfbi_set_interface_timings(struct omap_dss_device *dssdev,
struct rfbi_timings *timings);
+void omapdss_writeback_bus_lock(struct omap_dss_output *wb);
+void omapdss_writeback_bus_unlock(struct omap_dss_output *wb);
int omapdss_writeback_enable(struct omap_dss_output *wb);
void omapdss_writeback_disable(struct omap_dss_output *wb);
void omapdss_writeback_set_input_size(struct omap_dss_output *wb, u16 w, u16 h);
+int omapdss_writeback_update(struct omap_dss_output *wb,
+ void (*callback)(int, void *), void *data);
int omapdss_writeback_apply(struct omap_dss_output *wb);
int omapdss_writeback_set_info(struct omap_dss_output *wb,
struct omap_dss_writeback_info *info);
--
1.7.9.5