diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/video/mxc/mxc_epdc_fb.c | 101 |
1 files changed, 91 insertions, 10 deletions
diff --git a/drivers/video/mxc/mxc_epdc_fb.c b/drivers/video/mxc/mxc_epdc_fb.c index 0405234608bb..b702788ae823 100644 --- a/drivers/video/mxc/mxc_epdc_fb.c +++ b/drivers/video/mxc/mxc_epdc_fb.c @@ -70,6 +70,10 @@ #define INVALID_LUT (-1) #define DRY_RUN_NO_LUT 100 +/* Maximum update buffer image width due to v2.0 and v2.1 errata ERR005313. */ +#define EPDC_V2_MAX_UPDATE_WIDTH 2047 +#define EPDC_V2_ROTATION_ALIGNMENT 8 + #define DEFAULT_TEMP_INDEX 0 #define DEFAULT_TEMP 20 /* room temp in deg Celsius */ @@ -203,6 +207,8 @@ struct mxc_epdc_fb_data { bool updates_active; int pwrdown_delay; unsigned long tce_prevent; + bool restrict_width; /* work around rev >=2.0 width and + stride restriction */ /* FB elements related to PxP DMA */ struct completion pxp_tx_cmpl; @@ -1941,10 +1947,10 @@ static int epdc_process_update(struct update_data_list *upd_data_list, * to copybuf in addition to the PxP structures */ mutex_lock(&fb_data->pxp_mutex); - if ((((width_unaligned || height_unaligned || input_unaligned) && + if (((((width_unaligned || height_unaligned || input_unaligned) && (upd_desc_list->upd_data.waveform_mode == WAVEFORM_MODE_AUTO)) - || line_overflow) && (fb_data->rev < 20)) { - + || line_overflow) && (fb_data->rev < 20)) || + fb_data->restrict_width) { dev_dbg(fb_data->dev, "Copying update before processing.\n"); /* Update to reflect what the new source buffer will be */ @@ -2148,7 +2154,8 @@ static int epdc_process_update(struct update_data_list *upd_data_list, } static int epdc_submit_merge(struct update_desc_list *upd_desc_list, - struct update_desc_list *update_to_merge) + struct update_desc_list *update_to_merge, + struct mxc_epdc_fb_data *fb_data) { struct mxcfb_update_data *a, *b; struct mxcfb_rect *arect, *brect; @@ -2206,6 +2213,19 @@ static int epdc_submit_merge(struct update_desc_list *upd_desc_list, (arect->top + arect->height - combine.top) : (brect->top + brect->height - combine.top); + /* Don't merge if combined width exceeds max width */ + if (fb_data->restrict_width) { + u32 max_width = EPDC_V2_MAX_UPDATE_WIDTH; + u32 combined_width = combine.width; + if (fb_data->epdc_fb_var.rotate != FB_ROTATE_UR) + max_width -= EPDC_V2_ROTATION_ALIGNMENT; + if ((fb_data->epdc_fb_var.rotate == FB_ROTATE_CW) || + (fb_data->epdc_fb_var.rotate == FB_ROTATE_CCW)) + combined_width = combine.height; + if (combined_width > max_width) + return MERGE_FAIL; + } + *arect = combine; /* Use flags of the later update */ @@ -2272,7 +2292,8 @@ static void epdc_submit_work_func(struct work_struct *work) break; } else { switch (epdc_submit_merge(upd_data_list->update_desc, - next_update->update_desc)) { + next_update->update_desc, + fb_data)) { case MERGE_OK: dev_dbg(fb_data->dev, "Update merged [collision]\n"); @@ -2342,7 +2363,7 @@ static void epdc_submit_work_func(struct work_struct *work) break; } else { switch (epdc_submit_merge(upd_data_list->update_desc, - next_desc)) { + next_desc, fb_data)) { case MERGE_OK: dev_dbg(fb_data->dev, "Update merged [queue]\n"); @@ -2389,7 +2410,8 @@ static void epdc_submit_work_func(struct work_struct *work) if ((fb_data->epdc_fb_var.rotate == FB_ROTATE_UR) && (fb_data->epdc_fb_var.grayscale == GRAYSCALE_8BIT) && - !is_transform && (fb_data->rev > 20)) { + !is_transform && (fb_data->rev > 20) && + !fb_data->restrict_width) { /* If needed, enable EPDC HW while ePxP is processing */ if ((fb_data->power_state == POWER_STATE_OFF) @@ -2623,8 +2645,7 @@ static void epdc_submit_work_func(struct work_struct *work) mutex_unlock(&fb_data->queue_mutex); } - -int mxc_epdc_fb_send_update(struct mxcfb_update_data *upd_data, +static int mxc_epdc_fb_send_single_update(struct mxcfb_update_data *upd_data, struct fb_info *info) { struct mxc_epdc_fb_data *fb_data = info ? @@ -2919,6 +2940,63 @@ int mxc_epdc_fb_send_update(struct mxcfb_update_data *upd_data, mutex_unlock(&fb_data->queue_mutex); return 0; } + +int mxc_epdc_fb_send_update(struct mxcfb_update_data *upd_data, + struct fb_info *info) +{ + struct mxc_epdc_fb_data *fb_data = info ? + (struct mxc_epdc_fb_data *)info:g_fb_data; + + if (!fb_data->restrict_width) { + /* No width restriction, send entire update region */ + return mxc_epdc_fb_send_single_update(upd_data, info); + } else { + int ret; + __u32 width, left; + __u32 marker; + __u32 *region_width, *region_left; + u32 max_upd_width = EPDC_V2_MAX_UPDATE_WIDTH; + + /* Further restrict max width due to pxp rotation + * alignment requirement + */ + if (fb_data->epdc_fb_var.rotate != FB_ROTATE_UR) + max_upd_width -= EPDC_V2_ROTATION_ALIGNMENT; + + /* Select split of width or height based on rotation */ + if ((fb_data->epdc_fb_var.rotate == FB_ROTATE_UR) || + (fb_data->epdc_fb_var.rotate == FB_ROTATE_UD)) { + region_width = &upd_data->update_region.width; + region_left = &upd_data->update_region.left; + } else { + region_width = &upd_data->update_region.height; + region_left = &upd_data->update_region.top; + } + + if (*region_width <= max_upd_width) + return mxc_epdc_fb_send_single_update(upd_data, info); + + width = *region_width; + left = *region_left; + marker = upd_data->update_marker; + upd_data->update_marker = 0; + + do { + *region_width = max_upd_width; + *region_left = left; + ret = mxc_epdc_fb_send_single_update(upd_data, info); + if (ret) + return ret; + width -= max_upd_width; + left += max_upd_width; + } while (width > max_upd_width); + + *region_width = width; + *region_left = left; + upd_data->update_marker = marker; + return mxc_epdc_fb_send_single_update(upd_data, info); + } +} EXPORT_SYMBOL(mxc_epdc_fb_send_update); int mxc_epdc_fb_wait_update_complete(struct mxcfb_update_marker_data *marker_data, @@ -4146,7 +4224,8 @@ static void mxc_epdc_fb_fw_handler(const struct firmware *fw, &fb_data->info); if (ret < 0) dev_err(fb_data->dev, - "Wait for update complete failed. Error = 0x%x", ret); + "Wait for initial update complete failed." + " Error = 0x%x", ret); } static int mxc_epdc_fb_init_hw(struct fb_info *info) @@ -4487,6 +4566,8 @@ int __devinit mxc_epdc_fb_probe(struct platform_device *pdev) } else { fb_data->num_luts = EPDC_V2_NUM_LUTS; fb_data->max_num_updates = EPDC_V2_MAX_NUM_UPDATES; + if (vmode->xres > EPDC_V2_MAX_UPDATE_WIDTH) + fb_data->restrict_width = true; } fb_data->max_num_buffers = EPDC_MAX_NUM_BUFFERS; |