From 6bc056dfe4a4810109446305063796fec3a9bbd7 Mon Sep 17 00:00:00 2001 From: Danny Nold Date: Thu, 10 Feb 2011 17:24:25 -0600 Subject: ENGR00137868 - EPDC fb: Accept unlimited updates for queued scheme Created a list for pending update and a list for update markers. Memory for list object for both is allocated dynamically. Accordingly, code was modified to work with these new object structures. Signed-off-by: Danny Nold --- drivers/video/mxc/mxc_epdc_fb.c | 579 ++++++++++++++++++++++++---------------- 1 file changed, 352 insertions(+), 227 deletions(-) (limited to 'drivers') diff --git a/drivers/video/mxc/mxc_epdc_fb.c b/drivers/video/mxc/mxc_epdc_fb.c index bf91d4175513..dd0cb5f0b59c 100644 --- a/drivers/video/mxc/mxc_epdc_fb.c +++ b/drivers/video/mxc/mxc_epdc_fb.c @@ -79,9 +79,19 @@ static unsigned long default_bpp = 16; struct update_marker_data { + struct list_head list; u32 update_marker; struct completion update_completion; int lut_num; + bool waiting; +}; + +struct update_desc_list { + struct list_head list; + struct mxcfb_update_data upd_data;/* Update parameters */ + u32 epdc_offs; /* Added to buffer ptr to resolve alignment */ + struct update_marker_data *upd_marker_data; + u32 update_order; /* Numeric ordering value for update */ }; /* This structure represents a list node containing both @@ -89,18 +99,15 @@ struct update_marker_data { * update processing task, and the update description (mode, region, etc.) */ struct update_data_list { struct list_head list; - struct mxcfb_update_data upd_data;/* Update parameters */ dma_addr_t phys_addr; /* Pointer to phys address of processed Y buf */ void *virt_addr; - u32 epdc_offs; /* Add to buffer pointer to resolve alignment */ u32 size; + dma_addr_t phys_addr_copybuf; /* Phys address of copied update data */ + void *virt_addr_copybuf; /* Used for PxP SW workaround */ + struct update_desc_list *update_desc; int lut_num; /* Assigned before update is processed into working buffer */ int collision_mask; /* Set when update results in collision */ /* Represents other LUTs that we collide with */ - struct update_marker_data *upd_marker_data; - u32 update_order; /* Numeric ordering value for update */ - dma_addr_t phys_addr_copybuf; /* Phys address of copied update data */ - void *virt_addr_copybuf; /* Used for PxP SW workaround */ }; struct mxc_epdc_fb_data { @@ -135,6 +142,7 @@ struct mxc_epdc_fb_data { bool waiting_for_idle; u32 auto_mode; u32 upd_scheme; + struct update_desc_list *upd_pending_list; struct update_data_list *upd_buf_queue; struct update_data_list *upd_buf_free_list; struct update_data_list *upd_buf_collision_list; @@ -151,7 +159,7 @@ struct mxc_epdc_fb_data { u32 working_buffer_phys; u32 working_buffer_size; u32 order_cnt; - struct update_marker_data update_marker_array[EPDC_MAX_NUM_UPDATES]; + struct update_marker_data *update_marker_list; u32 lut_update_order[EPDC_NUM_LUTS]; struct completion updates_done; struct delayed_work epdc_done_work; @@ -228,58 +236,58 @@ static bool is_free_list_full(struct mxc_epdc_fb_data *fb_data); static void dump_pxp_config(struct mxc_epdc_fb_data *fb_data, struct pxp_config_data *pxp_conf) { - dev_err(fb_data->dev, "S0 fmt 0x%x", + dev_info(fb_data->dev, "S0 fmt 0x%x", pxp_conf->s0_param.pixel_fmt); - dev_err(fb_data->dev, "S0 width 0x%x", + dev_info(fb_data->dev, "S0 width 0x%x", pxp_conf->s0_param.width); - dev_err(fb_data->dev, "S0 height 0x%x", + dev_info(fb_data->dev, "S0 height 0x%x", pxp_conf->s0_param.height); - dev_err(fb_data->dev, "S0 ckey 0x%x", + dev_info(fb_data->dev, "S0 ckey 0x%x", pxp_conf->s0_param.color_key); - dev_err(fb_data->dev, "S0 ckey en 0x%x", + dev_info(fb_data->dev, "S0 ckey en 0x%x", pxp_conf->s0_param.color_key_enable); - dev_err(fb_data->dev, "OL0 combine en 0x%x", + dev_info(fb_data->dev, "OL0 combine en 0x%x", pxp_conf->ol_param[0].combine_enable); - dev_err(fb_data->dev, "OL0 fmt 0x%x", + dev_info(fb_data->dev, "OL0 fmt 0x%x", pxp_conf->ol_param[0].pixel_fmt); - dev_err(fb_data->dev, "OL0 width 0x%x", + dev_info(fb_data->dev, "OL0 width 0x%x", pxp_conf->ol_param[0].width); - dev_err(fb_data->dev, "OL0 height 0x%x", + dev_info(fb_data->dev, "OL0 height 0x%x", pxp_conf->ol_param[0].height); - dev_err(fb_data->dev, "OL0 ckey 0x%x", + dev_info(fb_data->dev, "OL0 ckey 0x%x", pxp_conf->ol_param[0].color_key); - dev_err(fb_data->dev, "OL0 ckey en 0x%x", + dev_info(fb_data->dev, "OL0 ckey en 0x%x", pxp_conf->ol_param[0].color_key_enable); - dev_err(fb_data->dev, "OL0 alpha 0x%x", + dev_info(fb_data->dev, "OL0 alpha 0x%x", pxp_conf->ol_param[0].global_alpha); - dev_err(fb_data->dev, "OL0 alpha en 0x%x", + dev_info(fb_data->dev, "OL0 alpha en 0x%x", pxp_conf->ol_param[0].global_alpha_enable); - dev_err(fb_data->dev, "OL0 local alpha en 0x%x", + dev_info(fb_data->dev, "OL0 local alpha en 0x%x", pxp_conf->ol_param[0].local_alpha_enable); - dev_err(fb_data->dev, "Out fmt 0x%x", + dev_info(fb_data->dev, "Out fmt 0x%x", pxp_conf->out_param.pixel_fmt); - dev_err(fb_data->dev, "Out width 0x%x", + dev_info(fb_data->dev, "Out width 0x%x", pxp_conf->out_param.width); - dev_err(fb_data->dev, "Out height 0x%x", + dev_info(fb_data->dev, "Out height 0x%x", pxp_conf->out_param.height); - dev_err(fb_data->dev, + dev_info(fb_data->dev, "drect left 0x%x right 0x%x width 0x%x height 0x%x", pxp_conf->proc_data.drect.left, pxp_conf->proc_data.drect.top, pxp_conf->proc_data.drect.width, pxp_conf->proc_data.drect.height); - dev_err(fb_data->dev, + dev_info(fb_data->dev, "srect left 0x%x right 0x%x width 0x%x height 0x%x", pxp_conf->proc_data.srect.left, pxp_conf->proc_data.srect.top, pxp_conf->proc_data.srect.width, pxp_conf->proc_data.srect.height); - dev_err(fb_data->dev, "Scaling en 0x%x", pxp_conf->proc_data.scaling); - dev_err(fb_data->dev, "HFlip en 0x%x", pxp_conf->proc_data.hflip); - dev_err(fb_data->dev, "VFlip en 0x%x", pxp_conf->proc_data.vflip); - dev_err(fb_data->dev, "Rotation 0x%x", pxp_conf->proc_data.rotate); - dev_err(fb_data->dev, "BG Color 0x%x", pxp_conf->proc_data.bgcolor); + dev_info(fb_data->dev, "Scaling en 0x%x", pxp_conf->proc_data.scaling); + dev_info(fb_data->dev, "HFlip en 0x%x", pxp_conf->proc_data.hflip); + dev_info(fb_data->dev, "VFlip en 0x%x", pxp_conf->proc_data.vflip); + dev_info(fb_data->dev, "Rotation 0x%x", pxp_conf->proc_data.rotate); + dev_info(fb_data->dev, "BG Color 0x%x", pxp_conf->proc_data.bgcolor); } static void dump_epdc_reg(void) @@ -339,27 +347,28 @@ static void dump_epdc_reg(void) static void dump_update_data(struct device *dev, struct update_data_list *upd_data_list) { - dev_err(dev, + dev_info(dev, "X = %d, Y = %d, Width = %d, Height = %d, WaveMode = %d, " "LUT = %d, Coll Mask = 0x%x, order = %d\n", - upd_data_list->upd_data.update_region.left, - upd_data_list->upd_data.update_region.top, - upd_data_list->upd_data.update_region.width, - upd_data_list->upd_data.update_region.height, - upd_data_list->upd_data.waveform_mode, upd_data_list->lut_num, + upd_data_list->update_desc->upd_data.update_region.left, + upd_data_list->update_desc->upd_data.update_region.top, + upd_data_list->update_desc->upd_data.update_region.width, + upd_data_list->update_desc->upd_data.update_region.height, + upd_data_list->update_desc->upd_data.waveform_mode, + upd_data_list->lut_num, upd_data_list->collision_mask, - upd_data_list->update_order); + upd_data_list->update_desc->update_order); } static void dump_collision_list(struct mxc_epdc_fb_data *fb_data) { struct update_data_list *plist; - dev_err(fb_data->dev, "Collision List:\n"); + dev_info(fb_data->dev, "Collision List:\n"); if (list_empty(&fb_data->upd_buf_collision_list->list)) - dev_err(fb_data->dev, "Empty"); + dev_info(fb_data->dev, "Empty"); list_for_each_entry(plist, &fb_data->upd_buf_collision_list->list, list) { - dev_err(fb_data->dev, "Virt Addr = 0x%x, Phys Addr = 0x%x ", + 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); } @@ -369,11 +378,11 @@ static void dump_free_list(struct mxc_epdc_fb_data *fb_data) { struct update_data_list *plist; - dev_err(fb_data->dev, "Free List:\n"); + dev_info(fb_data->dev, "Free List:\n"); if (list_empty(&fb_data->upd_buf_free_list->list)) - dev_err(fb_data->dev, "Empty"); + dev_info(fb_data->dev, "Empty"); list_for_each_entry(plist, &fb_data->upd_buf_free_list->list, list) { - dev_err(fb_data->dev, "Virt Addr = 0x%x, Phys Addr = 0x%x ", + 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); } @@ -383,24 +392,49 @@ static void dump_queue(struct mxc_epdc_fb_data *fb_data) { struct update_data_list *plist; - dev_err(fb_data->dev, "Queue:\n"); + dev_info(fb_data->dev, "Queue:\n"); if (list_empty(&fb_data->upd_buf_queue->list)) - dev_err(fb_data->dev, "Empty"); + dev_info(fb_data->dev, "Empty"); list_for_each_entry(plist, &fb_data->upd_buf_queue->list, list) { - dev_err(fb_data->dev, "Virt Addr = 0x%x, Phys Addr = 0x%x ", + 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_desc_data(struct device *dev, + struct update_desc_list *upd_desc_list) +{ + dev_info(dev, + "X = %d, Y = %d, Width = %d, Height = %d, WaveMode = %d, " + "order = %d\n", + upd_desc_list->upd_data.update_region.left, + upd_desc_list->upd_data.update_region.top, + upd_desc_list->upd_data.update_region.width, + upd_desc_list->upd_data.update_region.height, + upd_desc_list->upd_data.waveform_mode, + upd_desc_list->update_order); +} + +static void dump_pending_list(struct mxc_epdc_fb_data *fb_data) +{ + struct update_desc_list *plist; + + dev_info(fb_data->dev, "Queue:\n"); + if (list_empty(&fb_data->upd_pending_list->list)) + dev_info(fb_data->dev, "Empty"); + list_for_each_entry(plist, &fb_data->upd_pending_list->list, list) + dump_desc_data(fb_data->dev, plist); +} + static void dump_all_updates(struct mxc_epdc_fb_data *fb_data) { dump_free_list(fb_data); dump_queue(fb_data); dump_collision_list(fb_data); - dev_err(fb_data->dev, "Current update being processed:\n"); + dev_info(fb_data->dev, "Current update being processed:\n"); if (fb_data->cur_update == NULL) - dev_err(fb_data->dev, "No current update\n"); + dev_info(fb_data->dev, "No current update\n"); else dump_update_data(fb_data->dev, fb_data->cur_update); } @@ -1431,6 +1465,8 @@ EXPORT_SYMBOL(mxc_epdc_fb_set_upd_scheme); static void copy_before_process(struct mxc_epdc_fb_data *fb_data, struct update_data_list *upd_data_list) { + struct mxcfb_update_data *upd_data = + &upd_data_list->update_desc->upd_data; int i; unsigned char *temp_buf_ptr = upd_data_list->virt_addr_copybuf; unsigned char *src_ptr; @@ -1442,14 +1478,14 @@ static void copy_before_process(struct mxc_epdc_fb_data *fb_data, int x_trailing_bytes, y_trailing_bytes; /* Set source buf pointer based on input source, panning, etc. */ - if (upd_data_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER) { - src_upd_region = &upd_data_list->upd_data.alt_buffer_data.alt_update_region; + if (upd_data->flags & EPDC_FLAG_USE_ALT_BUFFER) { + src_upd_region = &upd_data->alt_buffer_data.alt_update_region; src_stride = - upd_data_list->upd_data.alt_buffer_data.width * bpp/8; - src_ptr = upd_data_list->upd_data.alt_buffer_data.virt_addr + upd_data->alt_buffer_data.width * bpp/8; + src_ptr = upd_data->alt_buffer_data.virt_addr + src_upd_region->top * src_stride; } else { - src_upd_region = &upd_data_list->upd_data.update_region; + src_upd_region = &upd_data->update_region; src_stride = fb_data->epdc_fb_var.xres_virtual * bpp/8; src_ptr = fb_data->info.screen_base + fb_data->fb_offset + src_upd_region->top * src_stride; @@ -1501,6 +1537,7 @@ static int epdc_process_update(struct update_data_list *upd_data_list, int pix_per_line_added; bool use_temp_buf = false; struct mxcfb_rect temp_buf_upd_region; + struct update_desc_list *upd_desc_list = upd_data_list->update_desc; int ret; @@ -1513,14 +1550,14 @@ static int epdc_process_update(struct update_data_list *upd_data_list, * Are we using FB or an alternate (overlay) * buffer for source of update? */ - if (upd_data_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER) { - src_width = upd_data_list->upd_data.alt_buffer_data.width; - src_height = upd_data_list->upd_data.alt_buffer_data.height; - src_upd_region = &upd_data_list->upd_data.alt_buffer_data.alt_update_region; + if (upd_desc_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER) { + src_width = upd_desc_list->upd_data.alt_buffer_data.width; + src_height = upd_desc_list->upd_data.alt_buffer_data.height; + src_upd_region = &upd_desc_list->upd_data.alt_buffer_data.alt_update_region; } else { src_width = fb_data->epdc_fb_var.xres_virtual; src_height = fb_data->epdc_fb_var.yres; - src_upd_region = &upd_data_list->upd_data.update_region; + src_upd_region = &upd_desc_list->upd_data.update_region; } bytes_per_pixel = fb_data->epdc_fb_var.bits_per_pixel/8; @@ -1567,7 +1604,7 @@ static int epdc_process_update(struct update_data_list *upd_data_list, line_overflow = true; if (((width_unaligned || height_unaligned || input_unaligned) && - (upd_data_list->upd_data.waveform_mode == WAVEFORM_MODE_AUTO)) + (upd_desc_list->upd_data.waveform_mode == WAVEFORM_MODE_AUTO)) || line_overflow) { dev_dbg(fb_data->dev, "Copying update before processing.\n"); @@ -1654,7 +1691,7 @@ static int epdc_process_update(struct update_data_list *upd_data_list, pxp_output_offs = post_rotation_ycoord * width_pxp_blocks + pxp_output_shift; - upd_data_list->epdc_offs = ALIGN(pxp_output_offs, 8); + upd_desc_list->epdc_offs = ALIGN(pxp_output_offs, 8); mutex_lock(&fb_data->pxp_mutex); @@ -1663,9 +1700,9 @@ static int epdc_process_update(struct update_data_list *upd_data_list, if (use_temp_buf) sg_dma_address(&fb_data->sg[0]) = upd_data_list->phys_addr_copybuf; - else if (upd_data_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER) + else if (upd_desc_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER) sg_dma_address(&fb_data->sg[0]) = - upd_data_list->upd_data.alt_buffer_data.phys_addr + upd_desc_list->upd_data.alt_buffer_data.phys_addr + pxp_input_offs; else { sg_dma_address(&fb_data->sg[0]) = @@ -1688,9 +1725,9 @@ static int epdc_process_update(struct update_data_list *upd_data_list, * Set PxP LUT transform type based on update flags. */ fb_data->pxp_conf.proc_data.lut_transform = 0; - if (upd_data_list->upd_data.flags & EPDC_FLAG_ENABLE_INVERSION) + if (upd_desc_list->upd_data.flags & EPDC_FLAG_ENABLE_INVERSION) fb_data->pxp_conf.proc_data.lut_transform |= PXP_LUT_INVERT; - if (upd_data_list->upd_data.flags & EPDC_FLAG_FORCE_MONOCHROME) + if (upd_desc_list->upd_data.flags & EPDC_FLAG_FORCE_MONOCHROME) fb_data->pxp_conf.proc_data.lut_transform |= PXP_LUT_BLACK_WHITE; @@ -1727,42 +1764,42 @@ static int epdc_process_update(struct update_data_list *upd_data_list, mutex_unlock(&fb_data->pxp_mutex); /* Update waveform mode from PxP histogram results */ - if (upd_data_list->upd_data.waveform_mode == WAVEFORM_MODE_AUTO) { + if (upd_desc_list->upd_data.waveform_mode == WAVEFORM_MODE_AUTO) { if (hist_stat & 0x1) - upd_data_list->upd_data.waveform_mode = + upd_desc_list->upd_data.waveform_mode = fb_data->wv_modes.mode_du; else if (hist_stat & 0x2) - upd_data_list->upd_data.waveform_mode = + upd_desc_list->upd_data.waveform_mode = fb_data->wv_modes.mode_gc4; else if (hist_stat & 0x4) - upd_data_list->upd_data.waveform_mode = + upd_desc_list->upd_data.waveform_mode = fb_data->wv_modes.mode_gc8; else if (hist_stat & 0x8) - upd_data_list->upd_data.waveform_mode = + upd_desc_list->upd_data.waveform_mode = fb_data->wv_modes.mode_gc16; else - upd_data_list->upd_data.waveform_mode = + upd_desc_list->upd_data.waveform_mode = fb_data->wv_modes.mode_gc32; dev_dbg(fb_data->dev, "hist_stat = 0x%x, new waveform = 0x%x\n", - hist_stat, upd_data_list->upd_data.waveform_mode); + hist_stat, upd_desc_list->upd_data.waveform_mode); } return 0; } -static int epdc_submit_merge(struct update_data_list *upd_data_list, - struct update_data_list *update_to_merge) +static int epdc_submit_merge(struct update_desc_list *upd_desc_list, + struct update_desc_list *update_to_merge) { struct mxcfb_update_data *a, *b; struct mxcfb_rect *arect, *brect; struct mxcfb_rect combine; bool use_flags = false; - a = &upd_data_list->upd_data; + a = &upd_desc_list->upd_data; b = &update_to_merge->upd_data; - arect = &upd_data_list->upd_data.update_region; + arect = &upd_desc_list->upd_data.update_region; brect = &update_to_merge->upd_data.update_region; /* @@ -1813,14 +1850,14 @@ static int epdc_submit_merge(struct update_data_list *upd_data_list, /* Preserve marker value for merged update */ if (b->update_marker != 0) { a->update_marker = b->update_marker; - upd_data_list->upd_marker_data = + upd_desc_list->upd_marker_data = update_to_merge->upd_marker_data; } /* Merged update should take on the earliest order */ - upd_data_list->update_order = - (upd_data_list->update_order > update_to_merge->update_order) ? - upd_data_list->update_order : update_to_merge->update_order; + upd_desc_list->update_order = + (upd_desc_list->update_order > update_to_merge->update_order) ? + upd_desc_list->update_order : update_to_merge->update_order; return MERGE_OK; } @@ -1829,13 +1866,16 @@ static void epdc_submit_work_func(struct work_struct *work) { int temp_index; struct update_data_list *next_update; - struct update_data_list *temp; + struct update_data_list *temp_update; + struct update_desc_list *next_desc; + struct update_desc_list *temp_desc; unsigned long flags; struct mxc_epdc_fb_data *fb_data = container_of(work, struct mxc_epdc_fb_data, epdc_submit_work); struct update_data_list *upd_data_list = NULL; struct mxcfb_rect adj_update_region; bool end_merge = false; + struct update_data_list *buf_free_list = fb_data->upd_buf_free_list; /* Protect access to buffer queues and to update HW */ spin_lock_irqsave(&fb_data->queue_lock, flags); @@ -1845,7 +1885,7 @@ static void epdc_submit_work_func(struct work_struct *work) * Go through all updates in the collision list and check to see * if the collision mask has been fully cleared */ - list_for_each_entry_safe(next_update, temp, + list_for_each_entry_safe(next_update, temp_update, &fb_data->upd_buf_collision_list->list, list) { if (next_update->collision_mask != 0) @@ -1864,15 +1904,18 @@ static void epdc_submit_work_func(struct work_struct *work) /* If not merging, we have our update */ break; } else { - switch (epdc_submit_merge(upd_data_list, - next_update)) { + switch (epdc_submit_merge(upd_data_list->update_desc, + next_update->update_desc)) { case MERGE_OK: dev_dbg(fb_data->dev, "Update merged [collision]\n"); + list_del_init(&next_update->update_desc->list); + kfree(next_update->update_desc); + next_update->update_desc = NULL; list_del_init(&next_update->list); /* Add to free buffer list */ list_add_tail(&next_update->list, - &fb_data->upd_buf_free_list->list); + &buf_free_list->list); break; case MERGE_FAIL: dev_dbg(fb_data->dev, @@ -1893,36 +1936,51 @@ static void epdc_submit_work_func(struct work_struct *work) } /* - * Skip update queue only if we found a collision + * Skip pending update list only if we found a collision * update and we are not merging */ if (!((fb_data->upd_scheme == UPDATE_SCHEME_QUEUE) && upd_data_list)) { /* - * If we didn't find a collision update ready to go, - * we try to grab one from the update queue + * If we didn't find a collision update ready to go, we + * need to get a free buffer and match it to a pending update. */ - list_for_each_entry_safe(next_update, temp, - &fb_data->upd_buf_queue->list, list) { + + /* + * Can't proceed if there are no free buffers (and we don't + * already have a collision update selected) + */ + if (!upd_data_list && + list_empty(&fb_data->upd_buf_free_list->list)) { + spin_unlock_irqrestore(&fb_data->queue_lock, flags); + return; + } + + list_for_each_entry_safe(next_desc, temp_desc, + &fb_data->upd_pending_list->list, list) { dev_dbg(fb_data->dev, "Found a pending update!\n"); if (!upd_data_list) { - upd_data_list = next_update; - list_del_init(&next_update->list); + if (list_empty(&buf_free_list->list)) + break; + upd_data_list = + list_entry(buf_free_list->list.next, + struct update_data_list, list); + list_del_init(&upd_data_list->list); + upd_data_list->update_desc = next_desc; + list_del_init(&next_desc->list); if (fb_data->upd_scheme == UPDATE_SCHEME_QUEUE) /* If not merging, we have an update */ break; } else { - switch (epdc_submit_merge(upd_data_list, - next_update)) { + switch (epdc_submit_merge(upd_data_list->update_desc, + next_desc)) { case MERGE_OK: dev_dbg(fb_data->dev, "Update merged [queue]\n"); - list_del_init(&next_update->list); - /* Add to free buffer list */ - list_add_tail(&next_update->list, - &fb_data->upd_buf_free_list->list); + list_del_init(&next_desc->list); + kfree(next_desc); break; case MERGE_FAIL: dev_dbg(fb_data->dev, @@ -1953,16 +2011,19 @@ static void epdc_submit_work_func(struct work_struct *work) dev_dbg(fb_data->dev, "PXP processing error.\n"); /* Protect access to buffer queues and to update HW */ spin_lock_irqsave(&fb_data->queue_lock, flags); + list_del_init(&upd_data_list->update_desc->list); + kfree(upd_data_list->update_desc); + upd_data_list->update_desc = NULL; /* Add to free buffer list */ - list_add_tail(&upd_data_list->list, - &fb_data->upd_buf_free_list->list); + list_add_tail(&upd_data_list->list, &buf_free_list->list); /* Release buffer queues */ spin_unlock_irqrestore(&fb_data->queue_lock, flags); return; } /* Get rotation-adjusted coordinates */ - adjust_coordinates(fb_data, &upd_data_list->upd_data.update_region, + adjust_coordinates(fb_data, + &upd_data_list->update_desc->upd_data.update_region, &adj_update_region); /* Protect access to buffer queues and to update HW */ @@ -2009,36 +2070,36 @@ static void epdc_submit_work_func(struct work_struct *work) /* LUTs are available, so we get one here */ fb_data->cur_update = upd_data_list; - fb_data->cur_update->lut_num = epdc_get_next_lut(); + upd_data_list->lut_num = epdc_get_next_lut(); /* Associate LUT with update marker */ - if ((fb_data->cur_update->upd_marker_data) - && (fb_data->cur_update->upd_marker_data->update_marker != 0)) - fb_data->cur_update->upd_marker_data->lut_num = + if (upd_data_list->update_desc->upd_marker_data) + upd_data_list->update_desc->upd_marker_data->lut_num = fb_data->cur_update->lut_num; /* Mark LUT with order */ - fb_data->lut_update_order[fb_data->cur_update->lut_num] = - fb_data->cur_update->update_order; + fb_data->lut_update_order[upd_data_list->lut_num] = + upd_data_list->update_desc->update_order; /* Enable Collision and WB complete IRQs */ epdc_working_buf_intr(true); - epdc_lut_complete_intr(fb_data->cur_update->lut_num, true); + epdc_lut_complete_intr(upd_data_list->lut_num, true); /* Program EPDC update to process buffer */ - if (fb_data->cur_update->upd_data.temp != TEMP_USE_AMBIENT) { + if (upd_data_list->update_desc->upd_data.temp != TEMP_USE_AMBIENT) { temp_index = mxc_epdc_fb_get_temp_index(fb_data, - fb_data->cur_update->upd_data.temp); + upd_data_list->update_desc->upd_data.temp); epdc_set_temp(temp_index); } - epdc_set_update_addr(fb_data->cur_update->phys_addr - + fb_data->cur_update->epdc_offs); + epdc_set_update_addr(upd_data_list->phys_addr + + upd_data_list->update_desc->epdc_offs); epdc_set_update_coord(adj_update_region.left, adj_update_region.top); epdc_set_update_dimensions(adj_update_region.width, adj_update_region.height); - epdc_submit_update(fb_data->cur_update->lut_num, - fb_data->cur_update->upd_data.waveform_mode, - fb_data->cur_update->upd_data.update_mode, false, 0); + epdc_submit_update(upd_data_list->lut_num, + upd_data_list->update_desc->upd_data.waveform_mode, + upd_data_list->update_desc->upd_data.update_mode, + false, 0); /* Release buffer queues */ spin_unlock_irqrestore(&fb_data->queue_lock, flags); @@ -2052,10 +2113,11 @@ int mxc_epdc_fb_send_update(struct mxcfb_update_data *upd_data, (struct mxc_epdc_fb_data *)info:g_fb_data; struct update_data_list *upd_data_list = NULL; unsigned long flags; - int i; struct mxcfb_rect *screen_upd_region; /* Region on screen to update */ int temp_index; int ret; + struct update_desc_list *upd_desc; + struct update_marker_data *marker_data; /* Has EPDC HW been initialized? */ if (!fb_data->hw_ready) { @@ -2109,56 +2171,67 @@ int mxc_epdc_fb_send_update(struct mxcfb_update_data *upd_data, return -EPERM; } + if (fb_data->upd_scheme == UPDATE_SCHEME_SNAPSHOT) { + /* + * Get available intermediate (PxP output) buffer to hold + * processed update region + */ + if (list_empty(&fb_data->upd_buf_free_list->list)) { + dev_err(fb_data->dev, + "No free intermediate buffers available.\n"); + spin_unlock_irqrestore(&fb_data->queue_lock, flags); + return -ENOMEM; + } + + /* Grab first available buffer and delete from the free list */ + upd_data_list = + list_entry(fb_data->upd_buf_free_list->list.next, + struct update_data_list, list); + + list_del_init(&upd_data_list->list); + } + /* - * Get available intermediate (PxP output) buffer to hold - * processed update region + * Create new update data structure, fill it with new update + * data and add it to the list of pending updates */ - if (list_empty(&fb_data->upd_buf_free_list->list)) { + upd_desc = kzalloc(sizeof(struct update_desc_list), GFP_KERNEL); + if (!upd_desc) { dev_err(fb_data->dev, - "No free intermediate buffers available.\n"); + "Insufficient system memory for update! Aborting.\n"); + if (fb_data->upd_scheme == UPDATE_SCHEME_SNAPSHOT) { + list_add(&upd_data_list->list, + &fb_data->upd_buf_free_list->list); + } spin_unlock_irqrestore(&fb_data->queue_lock, flags); - return -ENOMEM; + return -EPERM; } - - /* Grab first available buffer and delete it from the free list */ - upd_data_list = - list_entry(fb_data->upd_buf_free_list->list.next, - struct update_data_list, list); - - list_del_init(&upd_data_list->list); - - /* copy update parameters to the current update data object */ - memcpy(&upd_data_list->upd_data, upd_data, - sizeof(struct mxcfb_update_data)); - memcpy(&upd_data_list->upd_data.update_region, &upd_data->update_region, - sizeof(struct mxcfb_rect)); + upd_desc->upd_data = *upd_data; + upd_desc->update_order = fb_data->order_cnt++; + list_add_tail(&upd_desc->list, &fb_data->upd_pending_list->list); /* If marker specified, associate it with a completion */ if (upd_data->update_marker != 0) { - /* Find available update marker and set it up */ - for (i = 0; i < EPDC_MAX_NUM_UPDATES; i++) { - /* Marker value set to 0 signifies it is not currently in use */ - if (fb_data->update_marker_array[i].update_marker == 0) { - fb_data->update_marker_array[i].update_marker = upd_data->update_marker; - init_completion(&fb_data->update_marker_array[i].update_completion); - upd_data_list->upd_marker_data = &fb_data->update_marker_array[i]; - break; - } + /* Allocate new update marker and set it up */ + marker_data = kzalloc(sizeof(struct update_marker_data), + GFP_KERNEL); + if (!marker_data) { + dev_err(fb_data->dev, "No memory for marker!\n"); + spin_unlock_irqrestore(&fb_data->queue_lock, flags); + return -ENOMEM; } - } else { - if (upd_data_list->upd_marker_data) - upd_data_list->upd_marker_data->update_marker = 0; + upd_desc->upd_marker_data = marker_data; + marker_data->update_marker = upd_data->update_marker; + marker_data->lut_num = INVALID_LUT; + init_completion(&marker_data->update_completion); + /* Add marker to marker list */ + list_add_tail(&marker_data->list, + &fb_data->update_marker_list->list); } - upd_data_list->update_order = fb_data->order_cnt++; - if (fb_data->upd_scheme != UPDATE_SCHEME_SNAPSHOT) { /* Queued update scheme processing */ - /* Add processed Y buffer to update list */ - list_add_tail(&upd_data_list->list, - &fb_data->upd_buf_queue->list); - spin_unlock_irqrestore(&fb_data->queue_lock, flags); /* Signal workqueue to handle new update */ @@ -2172,11 +2245,15 @@ int mxc_epdc_fb_send_update(struct mxcfb_update_data *upd_data, spin_unlock_irqrestore(&fb_data->queue_lock, flags); + /* Set descriptor for current update, delete from pending list */ + upd_data_list->update_desc = upd_desc; + list_del_init(&upd_desc->list); + /* * Hold on to original screen update region, which we * will ultimately use when telling EPDC where to update on panel */ - screen_upd_region = &upd_data_list->upd_data.update_region; + screen_upd_region = &upd_desc->upd_data.update_region; ret = epdc_process_update(upd_data_list, fb_data); if (ret) { @@ -2185,10 +2262,10 @@ int mxc_epdc_fb_send_update(struct mxcfb_update_data *upd_data, } /* Pass selected waveform mode back to user */ - upd_data->waveform_mode = upd_data_list->upd_data.waveform_mode; + upd_data->waveform_mode = upd_desc->upd_data.waveform_mode; /* Get rotation-adjusted coordinates */ - adjust_coordinates(fb_data, &upd_data_list->upd_data.update_region, + adjust_coordinates(fb_data, &upd_desc->upd_data.update_region, NULL); /* Grab lock for queue manipulation and update submission */ @@ -2216,33 +2293,33 @@ int mxc_epdc_fb_send_update(struct mxcfb_update_data *upd_data, upd_data_list->lut_num = epdc_get_next_lut(); /* Associate LUT with update marker */ - if (upd_data_list->upd_marker_data) - if (upd_data_list->upd_marker_data->update_marker != 0) - upd_data_list->upd_marker_data->lut_num = upd_data_list->lut_num; + if (upd_data_list->update_desc->upd_marker_data) + upd_data_list->update_desc->upd_marker_data->lut_num = + upd_data_list->lut_num; /* Mark LUT as containing new update */ fb_data->lut_update_order[upd_data_list->lut_num] = - upd_data_list->update_order; + upd_desc->update_order; /* Clear status and Enable LUT complete and WB complete IRQs */ epdc_working_buf_intr(true); - epdc_lut_complete_intr(fb_data->cur_update->lut_num, true); + epdc_lut_complete_intr(upd_data_list->lut_num, true); /* Program EPDC update to process buffer */ - epdc_set_update_addr(upd_data_list->phys_addr + upd_data_list->epdc_offs); + epdc_set_update_addr(upd_data_list->phys_addr + upd_desc->epdc_offs); epdc_set_update_coord(screen_upd_region->left, screen_upd_region->top); epdc_set_update_dimensions(screen_upd_region->width, screen_upd_region->height); - if (upd_data_list->upd_data.temp != TEMP_USE_AMBIENT) { + if (upd_desc->upd_data.temp != TEMP_USE_AMBIENT) { temp_index = mxc_epdc_fb_get_temp_index(fb_data, - upd_data_list->upd_data.temp); + upd_desc->upd_data.temp); epdc_set_temp(temp_index); } else epdc_set_temp(fb_data->temp_index); epdc_submit_update(upd_data_list->lut_num, - upd_data_list->upd_data.waveform_mode, - upd_data_list->upd_data.update_mode, false, 0); + upd_desc->upd_data.waveform_mode, + upd_desc->upd_data.update_mode, false, 0); spin_unlock_irqrestore(&fb_data->queue_lock, flags); return 0; @@ -2253,30 +2330,58 @@ int mxc_epdc_fb_wait_update_complete(u32 update_marker, struct fb_info *info) { struct mxc_epdc_fb_data *fb_data = info ? (struct mxc_epdc_fb_data *)info:g_fb_data; - int ret; - int i; + struct update_marker_data *next_marker; + struct update_marker_data *temp; + unsigned long flags; + bool marker_found = false; + int ret = 0; /* 0 is an invalid update_marker value */ if (update_marker == 0) return -EINVAL; /* - * Wait for completion associated with update_marker requested. + * Find completion associated with update_marker requested. * Note: If update completed already, marker will have been - * cleared and we will just return + * cleared, it won't be found, and function will just return. */ - for (i = 0; i < EPDC_MAX_NUM_UPDATES; i++) { - if (fb_data->update_marker_array[i].update_marker == update_marker) { - dev_dbg(fb_data->dev, "Waiting for marker %d\n", update_marker); - ret = wait_for_completion_timeout(&fb_data->update_marker_array[i].update_completion, msecs_to_jiffies(5000)); - if (!ret) - dev_err(fb_data->dev, "Timed out waiting for update completion\n"); - dev_dbg(fb_data->dev, "marker %d signalled!\n", update_marker); + + /* Grab queue lock to protect access to marker list */ + spin_lock_irqsave(&fb_data->queue_lock, flags); + + list_for_each_entry_safe(next_marker, temp, + &fb_data->update_marker_list->list, list) { + if (next_marker->update_marker == update_marker) { + dev_dbg(fb_data->dev, "Waiting for marker %d\n", + update_marker); + next_marker->waiting = true; + marker_found = true; break; } } - return 0; + spin_unlock_irqrestore(&fb_data->queue_lock, flags); + + /* + * If marker not found, it has either been signalled already + * or the update request failed. In either case, just return. + */ + if (!marker_found) + return ret; + + ret = wait_for_completion_timeout(&next_marker->update_completion, + msecs_to_jiffies(5000)); + if (!ret) { + dev_err(fb_data->dev, + "Timed out waiting for update completion\n"); + list_del_init(&next_marker->list); + ret = -ETIMEDOUT; + } + + /* Free update marker object */ + kfree(next_marker); + + return ret; } EXPORT_SYMBOL(mxc_epdc_fb_wait_update_complete); @@ -2445,14 +2550,25 @@ void mxc_epdc_fb_flush_updates(struct mxc_epdc_fb_data *fb_data) /* Grab queue lock to prevent any new updates from being submitted */ spin_lock_irqsave(&fb_data->queue_lock, flags); - if (!is_free_list_full(fb_data)) { + /* + * 3 places to check for updates that are active or pending: + * 1) Updates in the pending list + * 2) Update buffers in use (e.g., PxP processing) + * 3) Active updates to panel - We can key off of EPDC + * power state to know if we have active updates. + */ + if (!list_empty(&fb_data->upd_pending_list->list) || + !is_free_list_full(fb_data) || + ((fb_data->power_state == POWER_STATE_ON) && + !fb_data->powering_down)) { /* Initialize event signalling updates are done */ init_completion(&fb_data->updates_done); fb_data->waiting_for_idle = true; 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(2000)); + wait_for_completion_timeout(&fb_data->updates_done, + msecs_to_jiffies(10000)); spin_lock_irqsave(&fb_data->queue_lock, flags); fb_data->waiting_for_idle = false; } @@ -2507,9 +2623,8 @@ static int mxc_epdc_fb_pan_display(struct fb_var_screeninfo *var, if (!(var->vmode & FB_VMODE_YWRAP)) y_bottom += var->yres; - if (y_bottom > info->var.yres_virtual) { + if (y_bottom > info->var.yres_virtual) return -EINVAL; - } spin_lock_irqsave(&fb_data->queue_lock, flags); @@ -2575,8 +2690,10 @@ static bool is_free_list_full(struct mxc_epdc_fb_data *fb_data) static bool do_updates_overlap(struct update_data_list *update1, struct update_data_list *update2) { - struct mxcfb_rect *rect1 = &update1->upd_data.update_region; - struct mxcfb_rect *rect2 = &update2->upd_data.update_region; + struct mxcfb_rect *rect1 = + &update1->update_desc->upd_data.update_region; + struct mxcfb_rect *rect2 = + &update2->update_desc->upd_data.update_region; __u32 bottom1, bottom2, right1, right2; bottom1 = rect1->top + rect1->height; bottom2 = rect2->top + rect2->height; @@ -2596,6 +2713,8 @@ static irqreturn_t mxc_epdc_irq_handler(int irq, void *dev_id) struct mxc_epdc_fb_data *fb_data = dev_id; struct update_data_list *collision_update; struct mxcfb_rect *next_upd_region; + struct update_marker_data *next_marker; + struct update_marker_data *temp; unsigned long flags; int temp_index; u32 luts_completed_mask; @@ -2603,7 +2722,7 @@ static irqreturn_t mxc_epdc_irq_handler(int irq, void *dev_id) u32 missed_coll_mask = 0; u32 lut; bool ignore_collision = false; - int i, j; + int i; /* * If we just completed one-time panel init, bypass @@ -2673,27 +2792,27 @@ static irqreturn_t mxc_epdc_irq_handler(int irq, void *dev_id) } /* Signal completion if anyone waiting on this LUT */ - for (j = 0; j < EPDC_MAX_NUM_UPDATES; j++) { - if (fb_data->update_marker_array[j].lut_num != i) + list_for_each_entry_safe(next_marker, temp, + &fb_data->update_marker_list->list, list) { + if (next_marker->lut_num != i) continue; + /* Found marker to signal - remove from marker list */ + list_del_init(&next_marker->list); + /* Signal completion of update */ - dev_dbg(fb_data->dev, - "Signaling marker %d\n", - fb_data->update_marker_array[j].update_marker); - complete(&fb_data->update_marker_array[j].update_completion); - /* Ensure this doesn't get signaled again inadvertently */ - fb_data->update_marker_array[j].lut_num = INVALID_LUT; - /* - * Setting marker to 0 is OK - any wait call will - * return when marker doesn't match any in array - */ - fb_data->update_marker_array[j].update_marker = 0; + dev_dbg(fb_data->dev, "Signaling marker %d\n", + next_marker->update_marker); + if (next_marker->waiting) + complete(&next_marker->update_completion); + else + kfree(next_marker); } } /* Check to see if all updates have completed */ - if (is_free_list_full(fb_data) && + if (list_empty(&fb_data->upd_pending_list->list) && + is_free_list_full(fb_data) && (fb_data->cur_update == NULL) && !epdc_any_luts_active()) { @@ -2784,8 +2903,10 @@ static irqreturn_t mxc_epdc_irq_handler(int irq, void *dev_id) continue; if (fb_data->lut_update_order[lut] >= - fb_data->cur_update->update_order) { - dev_dbg(fb_data->dev, "Ignoring collision with newer update.\n"); + fb_data->cur_update->update_desc->update_order) { + dev_dbg(fb_data->dev, + "Ignoring collision with" + "newer update.\n"); ignore_collision = true; break; } @@ -2797,12 +2918,11 @@ static irqreturn_t mxc_epdc_irq_handler(int irq, void *dev_id) &fb_data->upd_buf_free_list->list); } else { /* - * If update has a marker, clear the LUT, since we - * don't want to signal that it is complete. + * If update has a marker, clear the LUT, since + * we don't want to signal that it is complete. */ - if (fb_data->cur_update->upd_marker_data) - if (fb_data->cur_update->upd_marker_data->update_marker != 0) - fb_data->cur_update->upd_marker_data->lut_num = INVALID_LUT; + if (fb_data->cur_update->update_desc->upd_marker_data) + fb_data->cur_update->update_desc->upd_marker_data->lut_num = INVALID_LUT; /* Move to collision list */ list_add_tail(&fb_data->cur_update->list, @@ -2892,34 +3012,38 @@ static irqreturn_t mxc_epdc_irq_handler(int irq, void *dev_id) fb_data->cur_update->lut_num = epdc_get_next_lut(); /* Associate LUT with update marker */ - if ((fb_data->cur_update->upd_marker_data) - && (fb_data->cur_update->upd_marker_data->update_marker != 0)) - fb_data->cur_update->upd_marker_data->lut_num = + if (fb_data->cur_update->update_desc->upd_marker_data) + fb_data->cur_update->update_desc->upd_marker_data->lut_num = fb_data->cur_update->lut_num; /* Mark LUT as containing new update */ fb_data->lut_update_order[fb_data->cur_update->lut_num] = - fb_data->cur_update->update_order; + fb_data->cur_update->update_desc->update_order; /* Enable Collision and WB complete IRQs */ epdc_working_buf_intr(true); epdc_lut_complete_intr(fb_data->cur_update->lut_num, true); /* Program EPDC update to process buffer */ - next_upd_region = &fb_data->cur_update->upd_data.update_region; - if (fb_data->cur_update->upd_data.temp != TEMP_USE_AMBIENT) { - temp_index = mxc_epdc_fb_get_temp_index(fb_data, fb_data->cur_update->upd_data.temp); + next_upd_region = + &fb_data->cur_update->update_desc->upd_data.update_region; + if (fb_data->cur_update->update_desc->upd_data.temp + != TEMP_USE_AMBIENT) { + temp_index = mxc_epdc_fb_get_temp_index(fb_data, + fb_data->cur_update->update_desc->upd_data.temp); epdc_set_temp(temp_index); } else epdc_set_temp(fb_data->temp_index); - epdc_set_update_addr(fb_data->cur_update->phys_addr + fb_data->cur_update->epdc_offs); + epdc_set_update_addr(fb_data->cur_update->phys_addr + + fb_data->cur_update->update_desc->epdc_offs); epdc_set_update_coord(next_upd_region->left, next_upd_region->top); epdc_set_update_dimensions(next_upd_region->width, next_upd_region->height); epdc_submit_update(fb_data->cur_update->lut_num, - fb_data->cur_update->upd_data.waveform_mode, - fb_data->cur_update->upd_data.update_mode, false, 0); + fb_data->cur_update->update_desc->upd_data.waveform_mode, + fb_data->cur_update->update_desc->upd_data.update_mode, + false, 0); /* Release buffer queues */ spin_unlock_irqrestore(&fb_data->queue_lock, flags); @@ -3390,22 +3514,28 @@ int __devinit mxc_epdc_fb_probe(struct platform_device *pdev) fb_data->fb_offset = 0; /* Allocate head objects for our lists */ + fb_data->upd_pending_list = + kzalloc(sizeof(struct update_desc_list), GFP_KERNEL); fb_data->upd_buf_queue = kzalloc(sizeof(struct update_data_list), GFP_KERNEL); fb_data->upd_buf_collision_list = kzalloc(sizeof(struct update_data_list), GFP_KERNEL); fb_data->upd_buf_free_list = kzalloc(sizeof(struct update_data_list), GFP_KERNEL); - if ((fb_data->upd_buf_queue == NULL) || (fb_data->upd_buf_free_list == NULL) - || (fb_data->upd_buf_collision_list == NULL)) { + if ((fb_data->upd_pending_list == NULL) || + (fb_data->upd_buf_queue == NULL) || + (fb_data->upd_buf_free_list == NULL) || + (fb_data->upd_buf_collision_list == NULL)) { ret = -ENOMEM; goto out_dma_fb; } /* - * Initialize lists for update requests, update collisions, + * Initialize lists for pending updates, + * active update requests, update collisions, * and available update (PxP output) buffers */ + INIT_LIST_HEAD(&fb_data->upd_pending_list->list); INIT_LIST_HEAD(&fb_data->upd_buf_queue->list); INIT_LIST_HEAD(&fb_data->upd_buf_free_list->list); INIT_LIST_HEAD(&fb_data->upd_buf_collision_list->list); @@ -3418,10 +3548,6 @@ int __devinit mxc_epdc_fb_probe(struct platform_device *pdev) goto out_upd_buffers; } - /* Clear update data structure */ - memset(&upd_list->upd_data, 0, - sizeof(struct mxcfb_update_data)); - /* * Each update buffer is 1 byte per pixel, and can * be as big as the full-screen frame buffer @@ -3500,11 +3626,10 @@ int __devinit mxc_epdc_fb_probe(struct platform_device *pdev) fb_data->wv_modes.mode_gc16 = 2; fb_data->wv_modes.mode_gc32 = 2; - /* Initialize markers */ - for (i = 0; i < EPDC_MAX_NUM_UPDATES; i++) { - fb_data->update_marker_array[i].update_marker = 0; - fb_data->update_marker_array[i].lut_num = INVALID_LUT; - } + /* Initialize marker list */ + fb_data->update_marker_list = + kzalloc(sizeof(struct update_marker_data), GFP_KERNEL); + INIT_LIST_HEAD(&fb_data->update_marker_list->list); /* Initialize all LUTs to inactive */ for (i = 0; i < EPDC_NUM_LUTS; i++) -- cgit v1.2.3