summaryrefslogtreecommitdiffstats
path: root/master/eink-auto-update
diff options
context:
space:
mode:
authorroot <root@no.no.james.local>2015-12-27 20:15:45 +0000
committerroot <root@no.no.james.local>2015-12-27 20:15:45 +0000
commite119f271350c7eb5da50908196f05704feda011a (patch)
tree2fedc9ae8608405bfc2867dad1ab9d3613fcb909 /master/eink-auto-update
downloadlinux-3.0.35-kobo-pq-master.zip
linux-3.0.35-kobo-pq-master.tar.gz
linux-3.0.35-kobo-pq-master.tar.bz2
first working versionHEADmaster
Diffstat (limited to 'master/eink-auto-update')
-rw-r--r--master/eink-auto-update418
1 files changed, 418 insertions, 0 deletions
diff --git a/master/eink-auto-update b/master/eink-auto-update
new file mode 100644
index 0000000..de9f574
--- /dev/null
+++ b/master/eink-auto-update
@@ -0,0 +1,418 @@
+diff --git a/drivers/video/mxc/mxc_epdc_fb.c b/drivers/video/mxc/mxc_epdc_fb.c
+index 39de51d..511c980 100644
+--- a/drivers/video/mxc/mxc_epdc_fb.c
++++ b/drivers/video/mxc/mxc_epdc_fb.c
+@@ -47,6 +47,9 @@
+ #include <linux/regulator/driver.h>
+ #include <linux/fsl_devices.h>
+ #include <linux/bitops.h>
++#include <linux/sort.h>
++#include <linux/crc16.h>
++
+ #include <mach/epdc.h>
+ #include <mach/dma.h>
+ #include <asm/cacheflush.h>
+@@ -140,10 +143,17 @@ uint32_t aa_time_stamp[2];
+
+ #define NUM_SCREENS_MIN 2
+
++#if 0
+ #define EPDC_V1_NUM_LUTS 16
+ #define EPDC_V1_MAX_NUM_UPDATES 20
+ #define EPDC_V2_NUM_LUTS 64
+ #define EPDC_V2_MAX_NUM_UPDATES 64
++#else
++#define EPDC_V1_NUM_LUTS 16
++#define EPDC_V1_MAX_NUM_UPDATES 2
++#define EPDC_V2_NUM_LUTS 64
++#define EPDC_V2_MAX_NUM_UPDATES 6
++#endif
+ #define EPDC_MAX_NUM_BUFFERS 2
+ #define INVALID_LUT (-1)
+ #define DRY_RUN_NO_LUT 100
+@@ -187,6 +197,9 @@ static int vcom_nominal;
+
+ static unsigned long default_bpp = 16;
+
++static int *_line_ranges; // temporary buffer for calculating which line ranges needs to be updated
++static u16 *_blocksCRC16; // CRC16 checksum of every 16x8 pixel block
++
+ struct update_marker_data {
+ struct list_head full_list;
+ struct list_head upd_list;
+@@ -4775,6 +4788,29 @@ static int mxc_epdc_fb_ioctl(struct fb_info *info, unsigned int cmd,
+ return ret;
+ }
+
++// calculates CRC16 of 8x8 pixel block
++static u16 calcBlockCRC16(struct mxc_epdc_fb_data *fb_data, u16 x, u16 y)
++{
++ int bpp = fb_data->epdc_fb_var.bits_per_pixel / 8;
++ int src_stride = fb_data->epdc_fb_var.xres_virtual * bpp;
++ unsigned char *src_ptr;
++ u16 res;
++
++ src_ptr = fb_data->info.screen_base + fb_data->fb_offset + y * src_stride + x * bpp;
++
++ res = crc16(0, src_ptr, 8 * bpp); src_ptr += src_stride;
++ res = crc16(res, src_ptr, 8 * bpp); src_ptr += src_stride;
++ res = crc16(res, src_ptr, 8 * bpp); src_ptr += src_stride;
++ res = crc16(res, src_ptr, 8 * bpp); src_ptr += src_stride;
++ res = crc16(res, src_ptr, 8 * bpp); src_ptr += src_stride;
++ res = crc16(res, src_ptr, 8 * bpp); src_ptr += src_stride;
++ res = crc16(res, src_ptr, 8 * bpp); src_ptr += src_stride;
++ res = crc16(res, src_ptr, 8 * bpp);
++
++ return res;
++}
++
++#if 0
+ static void mxc_epdc_fb_update_pages(struct mxc_epdc_fb_data *fb_data,
+ u16 y1, u16 y2)
+ {
+@@ -4793,7 +4829,141 @@ static void mxc_epdc_fb_update_pages(struct mxc_epdc_fb_data *fb_data,
+
+ mxc_epdc_fb_send_update(&update, &fb_data->info);
+ }
++#else
++
++
++static void refresh_whole_screen(struct mxc_epdc_fb_data *fb_data)
++{
++ struct mxcfb_update_data update;
++ int x, y;
++ u16 *crcFB;
++
++ GALLEN_DBGLOCAL_BEGIN();
++
++ /* Do full screen update */
++ update.update_region.left = 0;
++ update.update_region.width = fb_data->epdc_fb_var.xres;
++ update.update_region.top = 0;
++ update.update_region.height = fb_data->epdc_fb_var.yres;
++ update.waveform_mode = WAVEFORM_MODE_AUTO;
++ update.update_mode = UPDATE_MODE_FULL;
++ update.update_marker = 0;
++ update.temp = TEMP_USE_AMBIENT;
++ update.flags = 0;
++
++ // update crc checksums
++ crcFB = _blocksCRC16;
++ for (y = 0; y < fb_data->epdc_fb_var.yres; y += 8)
++ {
++ for (x = 0; x < fb_data->epdc_fb_var.xres; x += 8)
++ {
++ *crcFB++ = calcBlockCRC16(fb_data, x, y);
++ }
++ }
++
++ // refresh screen
++ mxc_epdc_fb_send_update(&update, &fb_data->info);
++
++ GALLEN_DBGLOCAL_END();
++}
+
++
++static void mxc_epdc_fb_update_pages(struct mxc_epdc_fb_data *fb_data,
++ u16 y1, u16 y2)
++{
++ struct mxcfb_update_data update;
++ int x, y, first_x;
++ int low_y;
++ int high_y;
++ u16 crc;
++ u16 *crcFB;
++ bool change;
++
++ /* Do partial screen update */
++ update.update_region.left = 0;
++ update.update_region.width = fb_data->epdc_fb_var.xres;
++ update.update_region.top = y1;
++ update.update_region.height = y2 - y1 + 1;
++ update.waveform_mode = WAVEFORM_MODE_AUTO;
++ update.update_mode = UPDATE_MODE_PARTIAL;
++ update.update_marker = 0;
++ update.temp = TEMP_USE_AMBIENT;
++ update.flags = 0;
++
++ low_y = y2;
++ high_y = y1;
++ first_x = -1;
++ for (x = 0; x < fb_data->epdc_fb_var.xres; x += 8)
++ {
++ change = false;
++ for (y = y1; y < y2; y += 8)
++ {
++ crc = calcBlockCRC16(fb_data, x, y);
++ crcFB = &_blocksCRC16[(y/8) * (fb_data->native_width/8) + x/8];
++
++ if (*crcFB != crc)
++ {
++ *crcFB = crc;
++ change = true;
++
++ if (y<low_y) low_y=y;
++ if (y>high_y) high_y=y;
++ }
++ }
++
++ if (change)
++ {
++ if (first_x < 0) first_x = x;
++ }
++ else
++ {
++ // flush previous blocks
++ if (first_x >= 0)
++ {
++ high_y += 8;
++ if (high_y > y2) high_y=y2;
++ update.update_region.left = first_x;
++ update.update_region.width = x - first_x;
++ update.update_region.top = low_y;
++ update.update_region.height = high_y - low_y;
++ mxc_epdc_fb_send_update(&update, &fb_data->info);
++
++ first_x = -1;
++ low_y = y2;
++ high_y = y1;
++ }
++ }
++ }
++
++ // flush last blocks
++ if (first_x >= 0)
++ {
++ high_y += 8;
++ if (high_y > y2) high_y=y2;
++ update.update_region.left = first_x;
++ update.update_region.width = fb_data->epdc_fb_var.xres - first_x;
++ update.update_region.top = low_y;
++ update.update_region.height = high_y - low_y;
++ mxc_epdc_fb_send_update(&update, &fb_data->info);
++ }
++
++}
++#endif
++
++// compare two line ranges (y1/y2)
++static int line_ranges_cmp(const void *a, const void *b)
++{
++ const int *ia = (const int*)a;
++ const int *ib = (const int*)b;
++
++ // increasing y1
++ if (ia[0] != ib[0]) return ia[0] - ib[0];
++
++ // but decreasing y2
++ return ib[1] - ia[1];
++}
++
++#if 0
+ /* this is called back from the deferred io workqueue */
+ static void mxc_epdc_fb_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+@@ -4802,6 +4972,7 @@ static void mxc_epdc_fb_deferred_io(struct fb_info *info,
+ struct page *page;
+ unsigned long beg, end;
+ int y1, y2, miny, maxy;
++ int number_of_changed_lines;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+@@ -4828,6 +4999,153 @@ static void mxc_epdc_fb_deferred_io(struct fb_info *info,
+
+ GALLEN_DBGLOCAL_END();
+ }
++#else
++/* this is called back from the deferred io workqueue */
++static void mxc_epdc_fb_deferred_io(struct fb_info *info,
++ struct list_head *pagelist)
++{
++ struct mxc_epdc_fb_data *fb_data = (struct mxc_epdc_fb_data *)info;
++ struct page *page;
++ unsigned long beg, end;
++ int i, y1, y2, last_y1, last_y2;
++ int no_ranges;
++ int number_of_changed_lines;
++
++ GALLEN_DBGLOCAL_BEGIN();
++
++ if (fb_data->auto_mode != AUTO_UPDATE_MODE_AUTOMATIC_MODE) {
++ return;
++ }
++
++ // collect y1/y2 ranges to update
++ no_ranges = 0;
++ last_y1 = -1; last_y2 = -1;
++ list_for_each_entry(page, pagelist, lru)
++ {
++ beg = page->index << PAGE_SHIFT;
++ end = beg + PAGE_SIZE - 1;
++ y1 = beg / info->fix.line_length;
++ y2 = end / info->fix.line_length;
++
++
++ // align lines to 8
++ y1 &= ~0x7;
++ if (((y2 + 1) & 0x7) != 0)
++ {
++ y2++; y2 &= ~0x7; y2 += 7;
++ }
++
++
++
++ if (y2 >= fb_data->epdc_fb_var.yres)
++ {
++ y2 = fb_data->epdc_fb_var.yres - 1;
++ }
++
++
++ if (y1 != last_y1 || y2 != last_y2)
++ {
++ if (no_ranges > fb_data->native_height)
++ {
++ // too many ranges (temp array size is too short)
++ // update whole screen
++ mxc_epdc_fb_update_pages(fb_data, 0, fb_data->epdc_fb_var.yres - 1);
++ return;
++ }
++ else
++ {
++ _line_ranges[no_ranges*2 + 0] = last_y1 = y1;
++ _line_ranges[no_ranges*2 + 1] = last_y2 = y2;
++ no_ranges++;
++ }
++ }
++ }
++
++ // sort y1/y2 ranges - increasing y1 but decresing y2
++ if (no_ranges > 1)
++ {
++ sort(_line_ranges, no_ranges, sizeof(int)*2, line_ranges_cmp, NULL);
++ }
++
++
++ // calculate number of changed lines
++ number_of_changed_lines = 0;
++ for (i = 0; i < no_ranges; i++)
++ {
++ y1 = _line_ranges[i*2 + 0];
++ y2 = _line_ranges[i*2 + 1];
++
++ if (i == 0)
++ {
++ last_y1 = y1;
++ last_y2 = y2;
++ }
++
++ if (y1 > last_y2 + 1)
++ {
++ number_of_changed_lines += (last_y2 - last_y1 + 1);
++
++ last_y1 = y1;
++ last_y2 = y2;
++ }
++ else
++ {
++ if (y2 > last_y2)
++ {
++ last_y2 = y2;
++ }
++ }
++ }
++ if (no_ranges > 0)
++ {
++ number_of_changed_lines += (last_y2 - last_y1 + 1);
++ }
++
++#if 0
++ // if too many lines were changed - refresh whole screen
++ if (number_of_changed_lines > (fb_data->native_height *3) / 4) {
++ refresh_whole_screen(fb_data);
++ return;
++ }
++#endif
++
++ // merge & update ranges
++ for (i = 0; i < no_ranges; i++)
++ {
++ y1 = _line_ranges[i*2 + 0];
++ y2 = _line_ranges[i*2 + 1];
++
++ if (i == 0)
++ {
++ last_y1 = y1;
++ last_y2 = y2;
++ }
++
++ if (y1 > last_y2 + 1)
++ {
++ // flush previous range
++ mxc_epdc_fb_update_pages(fb_data, last_y1, last_y2);
++
++ last_y1 = y1;
++ last_y2 = y2;
++ }
++ else
++ {
++ if (y2 > last_y2) {
++ last_y2 = y2;
++ }
++ }
++ }
++
++ // flush last range
++ if (no_ranges > 0)
++ {
++ mxc_epdc_fb_update_pages(fb_data, last_y1, last_y2);
++ }
++
++ GALLEN_DBGLOCAL_END();
++}
++#endif
+
+ void mxc_epdc_fb_flush_updates(struct mxc_epdc_fb_data *fb_data)
+ {
+@@ -5033,7 +5351,7 @@ static struct fb_ops mxc_epdc_fb_ops = {
+ };
+
+ static struct fb_deferred_io mxc_epdc_fb_defio = {
+- .delay = HZ,
++ .delay = HZ / 10,
+ .deferred_io = mxc_epdc_fb_deferred_io,
+ };
+
+@@ -7070,6 +7388,18 @@ int __devinit mxc_epdc_fb_probe(struct platform_device *pdev)
+
+ info->fbdefio = &mxc_epdc_fb_defio;
+ #ifdef CONFIG_FB_MXC_EINK_AUTO_UPDATE_MODE
++ // allocate memory for update algorithms
++ _line_ranges = kmalloc(sizeof(int)*2*fb_data->native_height, GFP_KERNEL);
++ if (!_line_ranges)
++ {
++ dev_err(&pdev->dev, "cannot allocate memory for _line_ranges array\n");
++ }
++ _blocksCRC16 = kzalloc(sizeof(u16)*(fb_data->native_width/8)*(fb_data->native_height/8), GFP_KERNEL);
++ if (!_line_ranges)
++ {
++ dev_err(&pdev->dev, "cannot allocate memory for _blocksCRC16 array\n");
++ }
++
+ fb_deferred_io_init(info);
+ #endif
+
+@@ -7415,6 +7745,13 @@ static int mxc_epdc_fb_remove(struct platform_device *pdev)
+ }
+ #ifdef CONFIG_FB_MXC_EINK_AUTO_UPDATE_MODE
+ fb_deferred_io_cleanup(&fb_data->info);
++
++ // free memory for update algorithms
++ if (_line_ranges)
++ {
++ kfree(_line_ranges); _line_ranges = 0;
++ kfree(_blocksCRC16); _blocksCRC16 = 0;
++ }
+ #endif
+
+ dma_free_writecombine(&pdev->dev, fb_data->map_size, fb_data->info.screen_base,