summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorDanny Nold <dannynold@freescale.com>2011-03-14 20:30:25 -0500
committerDanny Nold <dannynold@freescale.com>2011-03-16 09:51:48 -0500
commita6793365e041b39d17a1c86a4e80338a595542ba (patch)
tree1f72d1721232d5874a08e4c35b3f3f4ac02e37d1 /drivers
parent36913edd6354fc84ebd70287ea8da0104fd03644 (diff)
ENGR00140638 - EPDC fb: System may hang if entering suspend while updates active
- Added signalling to ensure suspend call does not return until EPDC has powered down. - Fixed race condition in IST. The working buffer (WB) process for a new update may report collision with LUTs that complete and are processed and removed by the IST before the working buffer completes. When this occurs, the collision may wait (forever) for a LUT that has already completed. This was putting the update queues into a bad state, preventing the update pipe from being flushed out. Solved this by tracking all completed LUTs after the WB process begins, and then removing them from the collision mask once the WB completes. Signed-off-by: Danny Nold <dannynold@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/video/mxc/mxc_epdc_fb.c55
1 files changed, 45 insertions, 10 deletions
diff --git a/drivers/video/mxc/mxc_epdc_fb.c b/drivers/video/mxc/mxc_epdc_fb.c
index 021993ad3fa1..fee6367f5ed0 100644
--- a/drivers/video/mxc/mxc_epdc_fb.c
+++ b/drivers/video/mxc/mxc_epdc_fb.c
@@ -131,6 +131,8 @@ struct mxc_epdc_fb_data {
int epdc_irq;
struct device *dev;
int power_state;
+ int wait_for_powerdown;
+ struct completion powerdown_compl;
struct clk *epdc_clk_axi;
struct clk *epdc_clk_pix;
struct regulator *display_regulator;
@@ -162,6 +164,7 @@ struct mxc_epdc_fb_data {
u32 order_cnt;
struct list_head full_marker_list;
u32 lut_update_order[EPDC_NUM_LUTS];
+ u32 luts_complete_wb;
struct completion updates_done;
struct delayed_work epdc_done_work;
struct workqueue_struct *epdc_submit_workqueue;
@@ -382,11 +385,9 @@ static void dump_free_list(struct mxc_epdc_fb_data *fb_data)
dev_info(fb_data->dev, "Free List:\n");
if (list_empty(&fb_data->upd_buf_free_list))
dev_info(fb_data->dev, "Empty");
- list_for_each_entry(plist, &fb_data->upd_buf_free_list, list) {
+ list_for_each_entry(plist, &fb_data->upd_buf_free_list, list)
dev_info(fb_data->dev, "Virt Addr = 0x%x, Phys Addr = 0x%x ",
(u32)plist->virt_addr, plist->phys_addr);
- dump_update_data(fb_data->dev, plist);
- }
}
static void dump_queue(struct mxc_epdc_fb_data *fb_data)
@@ -883,6 +884,11 @@ static void epdc_powerdown(struct mxc_epdc_fb_data *fb_data)
fb_data->power_state = POWER_STATE_OFF;
fb_data->powering_down = false;
+ if (fb_data->wait_for_powerdown) {
+ fb_data->wait_for_powerdown = false;
+ complete(&fb_data->powerdown_compl);
+ }
+
mutex_unlock(&fb_data->power_mutex);
}
@@ -2071,6 +2077,9 @@ static void epdc_submit_work_func(struct work_struct *work)
fb_data->cur_update = upd_data_list;
upd_data_list->lut_num = epdc_get_next_lut();
+ /* Reset mask for LUTS that have completed during WB processing */
+ fb_data->luts_complete_wb = 0;
+
/* Associate LUT with update marker */
list_for_each_entry_safe(next_marker, temp_marker,
&upd_data_list->update_desc->upd_marker_list, upd_list)
@@ -2304,6 +2313,9 @@ int mxc_epdc_fb_send_update(struct mxcfb_update_data *upd_data,
/* Save current update */
fb_data->cur_update = upd_data_list;
+ /* Reset mask for LUTS that have completed during WB processing */
+ fb_data->luts_complete_wb = 0;
+
/* LUTs are available, so we get one here */
upd_data_list->lut_num = epdc_get_next_lut();
@@ -2562,6 +2574,7 @@ static void mxc_epdc_fb_deferred_io(struct fb_info *info,
void mxc_epdc_fb_flush_updates(struct mxc_epdc_fb_data *fb_data)
{
unsigned long flags;
+ int ret;
/* Grab queue lock to prevent any new updates from being submitted */
spin_lock_irqsave(&fb_data->queue_lock, flags);
@@ -2582,8 +2595,12 @@ void mxc_epdc_fb_flush_updates(struct mxc_epdc_fb_data *fb_data)
spin_unlock_irqrestore(&fb_data->queue_lock, flags);
/* Wait for any currently active updates to complete */
- wait_for_completion_timeout(&fb_data->updates_done,
- msecs_to_jiffies(10000));
+ ret = wait_for_completion_timeout(&fb_data->updates_done,
+ msecs_to_jiffies(5000));
+ if (!ret)
+ dev_err(fb_data->dev,
+ "Flush updates timeout! ret = 0x%x\n", ret);
+
spin_lock_irqsave(&fb_data->queue_lock, flags);
fb_data->waiting_for_idle = false;
}
@@ -2594,6 +2611,7 @@ void mxc_epdc_fb_flush_updates(struct mxc_epdc_fb_data *fb_data)
static int mxc_epdc_fb_blank(int blank, struct fb_info *info)
{
struct mxc_epdc_fb_data *fb_data = (struct mxc_epdc_fb_data *)info;
+ int ret;
dev_dbg(fb_data->dev, "blank = %d\n", blank);
@@ -2604,6 +2622,23 @@ static int mxc_epdc_fb_blank(int blank, struct fb_info *info)
switch (blank) {
case FB_BLANK_POWERDOWN:
+ mxc_epdc_fb_flush_updates(fb_data);
+ /* Wait for powerdown */
+ mutex_lock(&fb_data->power_mutex);
+ if (fb_data->power_state != POWER_STATE_OFF) {
+ fb_data->wait_for_powerdown = true;
+ init_completion(&fb_data->powerdown_compl);
+ mutex_unlock(&fb_data->power_mutex);
+ ret = wait_for_completion_timeout(&fb_data->powerdown_compl,
+ msecs_to_jiffies(5000));
+ if (!ret) {
+ dev_err(fb_data->dev,
+ "No powerdown received!\n");
+ return -ETIMEDOUT;
+ }
+ } else
+ mutex_unlock(&fb_data->power_mutex);
+ break;
case FB_BLANK_VSYNC_SUSPEND:
case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_NORMAL:
@@ -2732,7 +2767,6 @@ static irqreturn_t mxc_epdc_irq_handler(int irq, void *dev_id)
struct update_marker_data *temp;
unsigned long flags;
int temp_index;
- u32 luts_completed_mask;
u32 temp_mask;
u32 missed_coll_mask = 0;
u32 lut;
@@ -2774,7 +2808,6 @@ static irqreturn_t mxc_epdc_irq_handler(int irq, void *dev_id)
spin_lock_irqsave(&fb_data->queue_lock, flags);
/* Free any LUTs that have completed */
- luts_completed_mask = 0;
for (i = 0; i < EPDC_NUM_LUTS; i++) {
if (!epdc_is_lut_complete(i))
continue;
@@ -2797,7 +2830,7 @@ static irqreturn_t mxc_epdc_irq_handler(int irq, void *dev_id)
epdc_clear_lut_complete_irq(i);
- luts_completed_mask |= 1 << i;
+ fb_data->luts_complete_wb |= 1 << i;
fb_data->lut_update_order[i] = 0;
@@ -2906,8 +2939,9 @@ static irqreturn_t mxc_epdc_irq_handler(int irq, void *dev_id)
missed_coll_mask);
}
- /* Clear collisions that just completed */
- fb_data->cur_update->collision_mask &= ~luts_completed_mask;
+ /* Clear collisions that completed since WB began */
+ fb_data->cur_update->collision_mask &=
+ ~fb_data->luts_complete_wb;
dev_dbg(fb_data->dev, "\nCollision mask = 0x%x\n",
fb_data->cur_update->collision_mask);
@@ -3809,6 +3843,7 @@ int __devinit mxc_epdc_fb_probe(struct platform_device *pdev)
fb_data->blank = FB_BLANK_UNBLANK;
fb_data->power_state = POWER_STATE_OFF;
fb_data->powering_down = false;
+ fb_data->wait_for_powerdown = false;
fb_data->pwrdown_delay = 0;
/* Register FB */