diff options
-rw-r--r-- | drivers/video/mxc/mxc_epdc_fb.c | 210 | ||||
-rw-r--r-- | include/linux/mxcfb.h | 3 |
2 files changed, 200 insertions, 13 deletions
diff --git a/drivers/video/mxc/mxc_epdc_fb.c b/drivers/video/mxc/mxc_epdc_fb.c index 0b3923c2cea6..0405234608bb 100644 --- a/drivers/video/mxc/mxc_epdc_fb.c +++ b/drivers/video/mxc/mxc_epdc_fb.c @@ -50,6 +50,7 @@ #include <linux/bitops.h> #include <mach/epdc.h> #include <mach/dma.h> +#include <asm/cacheflush.h> #include "epdc_regs.h" @@ -262,6 +263,16 @@ static int pxp_complete_update(struct mxc_epdc_fb_data *fb_data, u32 *hist_stat) static void draw_mode0(struct mxc_epdc_fb_data *fb_data); static bool is_free_list_full(struct mxc_epdc_fb_data *fb_data); +static void do_dithering_processing_Y1_v1_0( + unsigned char *update_region_ptr, + struct mxcfb_rect *update_region, + unsigned long update_region_stride, + int *err_dist); +static void do_dithering_processing_Y4_v1_0( + unsigned char *update_region_ptr, + struct mxcfb_rect *update_region, + unsigned long update_region_stride, + int *err_dist); #ifdef DEBUG static void dump_pxp_config(struct mxc_epdc_fb_data *fb_data, @@ -2226,6 +2237,7 @@ static void epdc_submit_work_func(struct work_struct *work) bool end_merge = false; bool is_transform; u32 update_addr; + int *err_dist; int ret; /* Protect access to buffer queues and to update HW */ @@ -2371,9 +2383,10 @@ static void epdc_submit_work_func(struct work_struct *work) * PxP in versions 2.0 and earlier of EPDC. */ is_transform = upd_data_list->update_desc->upd_data.flags & - (EPDC_FLAG_ENABLE_INVERSION | - EPDC_FLAG_FORCE_MONOCHROME | EPDC_FLAG_USE_CMAP) ? - true : false; + (EPDC_FLAG_ENABLE_INVERSION | EPDC_FLAG_USE_DITHERING_Y1 | + EPDC_FLAG_USE_DITHERING_Y4 | EPDC_FLAG_FORCE_MONOCHROME | + EPDC_FLAG_USE_CMAP) ? true : false; + if ((fb_data->epdc_fb_var.rotate == FB_ROTATE_UR) && (fb_data->epdc_fb_var.grayscale == GRAYSCALE_8BIT) && !is_transform && (fb_data->rev > 20)) { @@ -2391,7 +2404,6 @@ static void epdc_submit_work_func(struct work_struct *work) update_addr = fb_data->info.fix.smem_start + ((upd_region->top * fb_data->info.var.xres_virtual) + upd_region->left) * fb_data->info.var.bits_per_pixel/8; - upd_data_list->update_desc->epdc_stride = fb_data->info.var.xres_virtual * fb_data->info.var.bits_per_pixel/8; @@ -2457,6 +2469,45 @@ static void epdc_submit_work_func(struct work_struct *work) } /* + * Dithering Processing (Version 1.0 - for i.MX508 and i.MX6SL) + */ + if (upd_data_list->update_desc->upd_data.flags & + EPDC_FLAG_USE_DITHERING_Y1) { + + err_dist = kzalloc((fb_data->info.var.xres_virtual + 3) * 3 + * sizeof(int), GFP_KERNEL); + + /* Dithering Y8 -> Y1 */ + do_dithering_processing_Y1_v1_0( + (uint8_t *)(upd_data_list->virt_addr + + upd_data_list->update_desc->epdc_offs), + &adj_update_region, + (fb_data->rev < 20) ? + ALIGN(adj_update_region.width, 8) : + adj_update_region.width, + err_dist); + + kfree(err_dist); + } else if (upd_data_list->update_desc->upd_data.flags & + EPDC_FLAG_USE_DITHERING_Y4) { + + err_dist = kzalloc((fb_data->info.var.xres_virtual + 3) * 3 + * sizeof(int), GFP_KERNEL); + + /* Dithering Y8 -> Y1 */ + do_dithering_processing_Y4_v1_0( + (uint8_t *)(upd_data_list->virt_addr + + upd_data_list->update_desc->epdc_offs), + &adj_update_region, + (fb_data->rev < 20) ? + ALIGN(adj_update_region.width, 8) : + adj_update_region.width, + err_dist); + + kfree(err_dist); + } + + /* * If there are no LUTs available, * then we must wait for the resource to become free. * The IST will signal this event. @@ -3041,6 +3092,25 @@ static int mxc_epdc_fb_ioctl(struct fb_info *info, unsigned int cmd, ret = 0; break; } + + case MXCFB_GET_WORK_BUFFER: + { + /* copy the epdc working buffer to the user space */ + struct mxc_epdc_fb_data *fb_data = info ? + (struct mxc_epdc_fb_data *)info:g_fb_data; + flush_cache_all(); + outer_flush_all(); + if (copy_to_user((void __user *)arg, + (const void *) fb_data->working_buffer_virt, + fb_data->working_buffer_size)) + ret = -EFAULT; + else + ret = 0; + flush_cache_all(); + outer_flush_all(); + break; + } + default: break; } @@ -4454,8 +4524,9 @@ int __devinit mxc_epdc_fb_probe(struct platform_device *pdev) * be as big as the full-screen frame buffer */ fb_data->virt_addr_updbuf[i] = - dma_alloc_coherent(fb_data->info.device, fb_data->max_pix_size, - &fb_data->phys_addr_updbuf[i], GFP_DMA); + kmalloc(fb_data->max_pix_size, GFP_KERNEL); + fb_data->phys_addr_updbuf[i] = + virt_to_phys(fb_data->virt_addr_updbuf[i]); if (fb_data->virt_addr_updbuf[i] == NULL) { ret = -ENOMEM; goto out_upd_buffers; @@ -4737,10 +4808,7 @@ out_copybuffer: out_upd_buffers: for (i = 0; i < fb_data->max_num_buffers; i++) if (fb_data->virt_addr_updbuf[i] != NULL) - dma_free_writecombine(&pdev->dev, - fb_data->max_pix_size, - fb_data->virt_addr_updbuf[i], - fb_data->phys_addr_updbuf[i]); + kfree(fb_data->virt_addr_updbuf[i]); if (fb_data->virt_addr_updbuf != NULL) kfree(fb_data->virt_addr_updbuf); if (fb_data->phys_addr_updbuf != NULL) @@ -4785,9 +4853,7 @@ static int mxc_epdc_fb_remove(struct platform_device *pdev) for (i = 0; i < fb_data->max_num_buffers; i++) if (fb_data->virt_addr_updbuf[i] != NULL) - dma_free_writecombine(&pdev->dev, fb_data->max_pix_size, - fb_data->virt_addr_updbuf[i], - fb_data->phys_addr_updbuf[i]); + kfree(fb_data->virt_addr_updbuf[i]); if (fb_data->virt_addr_updbuf != NULL) kfree(fb_data->virt_addr_updbuf); if (fb_data->phys_addr_updbuf != NULL) @@ -5082,6 +5148,124 @@ static int pxp_complete_update(struct mxc_epdc_fb_data *fb_data, u32 *hist_stat) return 0; } +/* + * Different dithering algorithm can be used. We chose + * to implement Bill Atkinson's algorithm as an example + * Thanks Bill Atkinson for his dithering algorithm. + */ + +/* + * Dithering algorithm implementation - Y8->Y1 version 1.0 for i.MX + */ +static void do_dithering_processing_Y1_v1_0( + unsigned char *update_region_ptr, + struct mxcfb_rect *update_region, + unsigned long update_region_stride, + int *err_dist) +{ + + /* create a temp error distribution array */ + int bwPix; + int y; + int col; + int *err_dist_l0, *err_dist_l1, *err_dist_l2, distrib_error; + int width_3 = update_region->width + 3; + char *y8buf; + int x_offset = 0; + + /* prime a few elements the error distribution array */ + for (y = 0; y < update_region->height; y++) { + /* Dithering the Y8 in sbuf to BW suitable for A2 waveform */ + err_dist_l0 = err_dist + (width_3) * (y % 3); + err_dist_l1 = err_dist + (width_3) * ((y + 1) % 3); + err_dist_l2 = err_dist + (width_3) * ((y + 2) % 3); + + y8buf = update_region_ptr + x_offset; + + /* scan the line and convert the Y8 to BW */ + for (col = 1; col <= update_region->width; col++) { + bwPix = *(err_dist_l0 + col) + *y8buf; + + if (bwPix >= 128) { + *y8buf++ = 0xff; + distrib_error = (bwPix - 255) >> 3; + } else { + *y8buf++ = 0; + distrib_error = bwPix >> 3; + } + + /* modify the error distribution buffer */ + *(err_dist_l0 + col + 2) += distrib_error; + *(err_dist_l1 + col + 1) += distrib_error; + *(err_dist_l0 + col + 1) += distrib_error; + *(err_dist_l1 + col - 1) += distrib_error; + *(err_dist_l1 + col) += distrib_error; + *(err_dist_l2 + col) = distrib_error; + } + x_offset += update_region_stride; + } + + flush_cache_all(); + outer_flush_all(); +} + +/* + * Dithering algorithm implementation - Y8->Y4 version 1.0 for i.MX + */ + +static void do_dithering_processing_Y4_v1_0( + unsigned char *update_region_ptr, + struct mxcfb_rect *update_region, + unsigned long update_region_stride, + int *err_dist) +{ + + /* create a temp error distribution array */ + int gcPix; + int y; + int col; + int *err_dist_l0, *err_dist_l1, *err_dist_l2, distrib_error; + int width_3 = update_region->width + 3; + char *y8buf; + int x_offset = 0; + + /* prime a few elements the error distribution array */ + for (y = 0; y < update_region->height; y++) { + /* Dithering the Y8 in sbuf to Y4 */ + err_dist_l0 = err_dist + (width_3) * (y % 3); + err_dist_l1 = err_dist + (width_3) * ((y + 1) % 3); + err_dist_l2 = err_dist + (width_3) * ((y + 2) % 3); + + y8buf = update_region_ptr + x_offset; + + /* scan the line and convert the Y8 to Y4 */ + for (col = 1; col <= update_region->width; col++) { + gcPix = *(err_dist_l0 + col) + *y8buf; + + if (gcPix > 255) + gcPix = 255; + else if (gcPix < 0) + gcPix = 0; + + distrib_error = (*y8buf - (gcPix & 0xf0)) >> 3; + + *y8buf++ = gcPix & 0xf0; + + /* modify the error distribution buffer */ + *(err_dist_l0 + col + 2) += distrib_error; + *(err_dist_l1 + col + 1) += distrib_error; + *(err_dist_l0 + col + 1) += distrib_error; + *(err_dist_l1 + col - 1) += distrib_error; + *(err_dist_l1 + col) += distrib_error; + *(err_dist_l2 + col) = distrib_error; + } + x_offset += update_region_stride; + } + + flush_cache_all(); + outer_flush_all(); +} + static int __init mxc_epdc_fb_init(void) { return platform_driver_register(&mxc_epdc_fb_driver); diff --git a/include/linux/mxcfb.h b/include/linux/mxcfb.h index 60e0aa09d0b8..e852a8af7369 100644 --- a/include/linux/mxcfb.h +++ b/include/linux/mxcfb.h @@ -92,6 +92,8 @@ struct mxcfb_rect { #define EPDC_FLAG_USE_ALT_BUFFER 0x100 #define EPDC_FLAG_TEST_COLLISION 0x200 #define EPDC_FLAG_GROUP_UPDATE 0x400 +#define EPDC_FLAG_USE_DITHERING_Y1 0x2000 +#define EPDC_FLAG_USE_DITHERING_Y4 0x4000 #define FB_POWERDOWN_DISABLE -1 @@ -152,6 +154,7 @@ struct mxcfb_waveform_modes { #define MXCFB_SET_PWRDOWN_DELAY _IOW('F', 0x30, int32_t) #define MXCFB_GET_PWRDOWN_DELAY _IOR('F', 0x31, int32_t) #define MXCFB_SET_UPDATE_SCHEME _IOW('F', 0x32, __u32) +#define MXCFB_GET_WORK_BUFFER _IOWR('F', 0x34, unsigned long) #ifdef __KERNEL__ |