From: Greg Kroah-Hartman Date: Fri, 9 May 2014 06:15:09 +0000 (+0200) Subject: 3.4-stable patches X-Git-Tag: v3.14.4~5 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8e071465cbc9039496dd6e1474c866f8a81d9031;p=thirdparty%2Fkernel%2Fstable-queue.git 3.4-stable patches added patches: dm-thin-fix-dangling-bio-in-process_deferred_bios-error-path.patch gpio-mxs-allow-for-recursive-enable_irq_wake-call.patch mtd-nuc900_nand-null-dereference-in-nuc900_nand_enable.patch mtd-sm_ftl-heap-corruption-in-sm_create_sysfs_attributes.patch skip-intel_crt_init-for-dell-xps-8700.patch tgafb-fix-data-copying.patch --- diff --git a/queue-3.4/dm-thin-fix-dangling-bio-in-process_deferred_bios-error-path.patch b/queue-3.4/dm-thin-fix-dangling-bio-in-process_deferred_bios-error-path.patch new file mode 100644 index 00000000000..1544cc16afb --- /dev/null +++ b/queue-3.4/dm-thin-fix-dangling-bio-in-process_deferred_bios-error-path.patch @@ -0,0 +1,34 @@ +From fe76cd88e654124d1431bb662a0fc6e99ca811a5 Mon Sep 17 00:00:00 2001 +From: Mike Snitzer +Date: Fri, 28 Mar 2014 02:15:02 -0400 +Subject: dm thin: fix dangling bio in process_deferred_bios error path + +From: Mike Snitzer + +commit fe76cd88e654124d1431bb662a0fc6e99ca811a5 upstream. + +If unable to ensure_next_mapping() we must add the current bio, which +was removed from the @bios list via bio_list_pop, back to the +deferred_bios list before all the remaining @bios. + +Signed-off-by: Mike Snitzer +Acked-by: Joe Thornber +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/md/dm-thin.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/md/dm-thin.c ++++ b/drivers/md/dm-thin.c +@@ -1446,9 +1446,9 @@ static void process_deferred_bios(struct + */ + if (ensure_next_mapping(pool)) { + spin_lock_irqsave(&pool->lock, flags); ++ bio_list_add(&pool->deferred_bios, bio); + bio_list_merge(&pool->deferred_bios, &bios); + spin_unlock_irqrestore(&pool->lock, flags); +- + break; + } + diff --git a/queue-3.4/gpio-mxs-allow-for-recursive-enable_irq_wake-call.patch b/queue-3.4/gpio-mxs-allow-for-recursive-enable_irq_wake-call.patch new file mode 100644 index 00000000000..11e1ded0da6 --- /dev/null +++ b/queue-3.4/gpio-mxs-allow-for-recursive-enable_irq_wake-call.patch @@ -0,0 +1,93 @@ +From a585f87c863e4e1d496459d382b802bf5ebe3717 Mon Sep 17 00:00:00 2001 +From: Marek Vasut +Date: Mon, 24 Mar 2014 03:38:10 +0100 +Subject: gpio: mxs: Allow for recursive enable_irq_wake() call + +From: Marek Vasut + +commit a585f87c863e4e1d496459d382b802bf5ebe3717 upstream. + +The scenario here is that someone calls enable_irq_wake() from somewhere +in the code. This will result in the lockdep producing a backtrace as can +be seen below. In my case, this problem is triggered when using the wl1271 +(TI WlCore) driver found in drivers/net/wireless/ti/ . + +The problem cause is rather obvious from the backtrace, but let's outline +the dependency. enable_irq_wake() grabs the IRQ buslock in irq_set_irq_wake(), +which in turns calls mxs_gpio_set_wake_irq() . But mxs_gpio_set_wake_irq() +calls enable_irq_wake() again on the one-level-higher IRQ , thus it tries to +grab the IRQ buslock again in irq_set_irq_wake() . Because the spinlock in +irq_set_irq_wake()->irq_get_desc_buslock()->__irq_get_desc_lock() is not +marked as recursive, lockdep will spew the stuff below. + +We know we can safely re-enter the lock, so use IRQ_GC_INIT_NESTED_LOCK to +fix the spew. + + ============================================= + [ INFO: possible recursive locking detected ] + 3.10.33-00012-gf06b763-dirty #61 Not tainted + --------------------------------------------- + kworker/0:1/18 is trying to acquire lock: + (&irq_desc_lock_class){-.-...}, at: [] __irq_get_desc_lock+0x48/0x88 + + but task is already holding lock: + (&irq_desc_lock_class){-.-...}, at: [] __irq_get_desc_lock+0x48/0x88 + + other info that might help us debug this: + Possible unsafe locking scenario: + + CPU0 + ---- + lock(&irq_desc_lock_class); + lock(&irq_desc_lock_class); + + *** DEADLOCK *** + + May be due to missing lock nesting notation + + 3 locks held by kworker/0:1/18: + #0: (events){.+.+.+}, at: [] process_one_work+0x134/0x4a4 + #1: ((&fw_work->work)){+.+.+.}, at: [] process_one_work+0x134/0x4a4 + #2: (&irq_desc_lock_class){-.-...}, at: [] __irq_get_desc_lock+0x48/0x88 + + stack backtrace: + CPU: 0 PID: 18 Comm: kworker/0:1 Not tainted 3.10.33-00012-gf06b763-dirty #61 + Workqueue: events request_firmware_work_func + [] (unwind_backtrace+0x0/0xf0) from [] (show_stack+0x10/0x14) + [] (show_stack+0x10/0x14) from [] (__lock_acquire+0x140c/0x1a64) + [] (__lock_acquire+0x140c/0x1a64) from [] (lock_acquire+0x9c/0x104) + [] (lock_acquire+0x9c/0x104) from [] (_raw_spin_lock_irqsave+0x44/0x58) + [] (_raw_spin_lock_irqsave+0x44/0x58) from [] (__irq_get_desc_lock+0x48/0x88) + [] (__irq_get_desc_lock+0x48/0x88) from [] (irq_set_irq_wake+0x20/0xf4) + [] (irq_set_irq_wake+0x20/0xf4) from [] (mxs_gpio_set_wake_irq+0x1c/0x24) + [] (mxs_gpio_set_wake_irq+0x1c/0x24) from [] (set_irq_wake_real+0x30/0x44) + [] (set_irq_wake_real+0x30/0x44) from [] (irq_set_irq_wake+0x8c/0xf4) + [] (irq_set_irq_wake+0x8c/0xf4) from [] (wlcore_nvs_cb+0x10c/0x97c) + [] (wlcore_nvs_cb+0x10c/0x97c) from [] (request_firmware_work_func+0x38/0x58) + [] (request_firmware_work_func+0x38/0x58) from [] (process_one_work+0x1c0/0x4a4) + [] (process_one_work+0x1c0/0x4a4) from [] (worker_thread+0x138/0x394) + [] (worker_thread+0x138/0x394) from [] (kthread+0xa4/0xb0) + [] (kthread+0xa4/0xb0) from [] (ret_from_fork+0x14/0x34) + wlcore: loaded + +Signed-off-by: Marek Vasut +Acked-by: Shawn Guo +Signed-off-by: Linus Walleij +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/gpio/gpio-mxs.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/gpio/gpio-mxs.c ++++ b/drivers/gpio/gpio-mxs.c +@@ -167,7 +167,8 @@ static void __init mxs_gpio_init_gc(stru + ct->regs.ack = PINCTRL_IRQSTAT(port->id) + MXS_CLR; + ct->regs.mask = PINCTRL_IRQEN(port->id); + +- irq_setup_generic_chip(gc, IRQ_MSK(32), 0, IRQ_NOREQUEST, 0); ++ irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK, ++ IRQ_NOREQUEST, 0); + } + + static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset) diff --git a/queue-3.4/mtd-nuc900_nand-null-dereference-in-nuc900_nand_enable.patch b/queue-3.4/mtd-nuc900_nand-null-dereference-in-nuc900_nand_enable.patch new file mode 100644 index 00000000000..2559b1a844f --- /dev/null +++ b/queue-3.4/mtd-nuc900_nand-null-dereference-in-nuc900_nand_enable.patch @@ -0,0 +1,32 @@ +From c69dbbf3335a21aae74376d7e5db50a486d52439 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Mon, 17 Feb 2014 23:03:08 +0300 +Subject: mtd: nuc900_nand: NULL dereference in nuc900_nand_enable() + +From: Dan Carpenter + +commit c69dbbf3335a21aae74376d7e5db50a486d52439 upstream. + +Instead of writing to "nand->reg + REG_FMICSR" we write to "REG_FMICSR" +which is NULL and not a valid register. + +Fixes: 8bff82cbc308 ('mtd: add nand support for w90p910 (v2)') +Signed-off-by: Dan Carpenter +Signed-off-by: Brian Norris +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/mtd/nand/nuc900_nand.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/mtd/nand/nuc900_nand.c ++++ b/drivers/mtd/nand/nuc900_nand.c +@@ -250,7 +250,7 @@ static void nuc900_nand_enable(struct nu + val = __raw_readl(nand->reg + REG_FMICSR); + + if (!(val & NAND_EN)) +- __raw_writel(val | NAND_EN, REG_FMICSR); ++ __raw_writel(val | NAND_EN, nand->reg + REG_FMICSR); + + val = __raw_readl(nand->reg + REG_SMCSR); + diff --git a/queue-3.4/mtd-sm_ftl-heap-corruption-in-sm_create_sysfs_attributes.patch b/queue-3.4/mtd-sm_ftl-heap-corruption-in-sm_create_sysfs_attributes.patch new file mode 100644 index 00000000000..b64077bfd97 --- /dev/null +++ b/queue-3.4/mtd-sm_ftl-heap-corruption-in-sm_create_sysfs_attributes.patch @@ -0,0 +1,53 @@ +From b4c233057771581698a13694ab6f33b48ce837dc Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Thu, 5 Dec 2013 17:53:50 +0300 +Subject: mtd: sm_ftl: heap corruption in sm_create_sysfs_attributes() + +From: Dan Carpenter + +commit b4c233057771581698a13694ab6f33b48ce837dc upstream. + +We always put a NUL terminator one space past the end of the "vendor" +buffer. Walter Harms also pointed out that this should just use +kstrndup(). + +Fixes: 7d17c02a01a1 ('mtd: Add new SmartMedia/xD FTL') + +Signed-off-by: Dan Carpenter +Signed-off-by: Brian Norris +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/mtd/sm_ftl.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +--- a/drivers/mtd/sm_ftl.c ++++ b/drivers/mtd/sm_ftl.c +@@ -59,15 +59,12 @@ struct attribute_group *sm_create_sysfs_ + struct attribute_group *attr_group; + struct attribute **attributes; + struct sm_sysfs_attribute *vendor_attribute; ++ char *vendor; + +- int vendor_len = strnlen(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, +- SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET); +- +- char *vendor = kmalloc(vendor_len, GFP_KERNEL); ++ vendor = kstrndup(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, ++ SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET, GFP_KERNEL); + if (!vendor) + goto error1; +- memcpy(vendor, ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, vendor_len); +- vendor[vendor_len] = 0; + + /* Initialize sysfs attributes */ + vendor_attribute = +@@ -78,7 +75,7 @@ struct attribute_group *sm_create_sysfs_ + sysfs_attr_init(&vendor_attribute->dev_attr.attr); + + vendor_attribute->data = vendor; +- vendor_attribute->len = vendor_len; ++ vendor_attribute->len = strlen(vendor); + vendor_attribute->dev_attr.attr.name = "vendor"; + vendor_attribute->dev_attr.attr.mode = S_IRUGO; + vendor_attribute->dev_attr.show = sm_attr_show; diff --git a/queue-3.4/series b/queue-3.4/series index 759edd69a26..e4088cafd55 100644 --- a/queue-3.4/series +++ b/queue-3.4/series @@ -14,3 +14,9 @@ libata-ahci-accommodate-tag-ordered-controllers.patch locks-allow-__break_lease-to-sleep-even-when-break_time-is-0.patch rtlwifi-rtl8192cu-fix-too-long-disable-of-irqs.patch rtlwifi-rtl8192se-fix-too-long-disable-of-irqs.patch +gpio-mxs-allow-for-recursive-enable_irq_wake-call.patch +tgafb-fix-data-copying.patch +mtd-nuc900_nand-null-dereference-in-nuc900_nand_enable.patch +mtd-sm_ftl-heap-corruption-in-sm_create_sysfs_attributes.patch +skip-intel_crt_init-for-dell-xps-8700.patch +dm-thin-fix-dangling-bio-in-process_deferred_bios-error-path.patch diff --git a/queue-3.4/skip-intel_crt_init-for-dell-xps-8700.patch b/queue-3.4/skip-intel_crt_init-for-dell-xps-8700.patch new file mode 100644 index 00000000000..f721c8bfc9c --- /dev/null +++ b/queue-3.4/skip-intel_crt_init-for-dell-xps-8700.patch @@ -0,0 +1,38 @@ +From 10b6ee4a87811a110cb01eaca01eb04da6801baf Mon Sep 17 00:00:00 2001 +From: Giacomo Comes +Date: Thu, 3 Apr 2014 14:13:55 -0400 +Subject: Skip intel_crt_init for Dell XPS 8700 + +From: Giacomo Comes + +commit 10b6ee4a87811a110cb01eaca01eb04da6801baf upstream. + +The Dell XPS 8700 has a onboard Display port and HDMI port and no VGA port. +The call intel_crt_init freeze the machine, so skip such call. + +Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=73559 +Signed-off-by: Giacomo Comes +Signed-off-by: Daniel Vetter +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/gpu/drm/i915/intel_crt.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/gpu/drm/i915/intel_crt.c ++++ b/drivers/gpu/drm/i915/intel_crt.c +@@ -579,6 +579,14 @@ static const struct dmi_system_id intel_ + DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"), + }, + }, ++ { ++ .callback = intel_no_crt_dmi_callback, ++ .ident = "DELL XPS 8700", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "XPS 8700"), ++ }, ++ }, + { } + }; + diff --git a/queue-3.4/tgafb-fix-data-copying.patch b/queue-3.4/tgafb-fix-data-copying.patch new file mode 100644 index 00000000000..7c546e89c21 --- /dev/null +++ b/queue-3.4/tgafb-fix-data-copying.patch @@ -0,0 +1,342 @@ +From 6b0df6827bb6fcacb158dff29ad0a62d6418b534 Mon Sep 17 00:00:00 2001 +From: Mikulas Patocka +Date: Thu, 23 Jan 2014 14:43:10 -0500 +Subject: tgafb: fix data copying + +From: Mikulas Patocka + +commit 6b0df6827bb6fcacb158dff29ad0a62d6418b534 upstream. + +The functions for data copying copyarea_foreward_8bpp and +copyarea_backward_8bpp are buggy, they produce screen corruption. + +This patch fixes the functions and moves the logic to one function +"copyarea_8bpp". For simplicity, the function only handles copying that +is aligned on 8 pixes. If we copy an unaligned area, generic function +cfb_copyarea is used. + +Signed-off-by: Mikulas Patocka +Signed-off-by: Tomi Valkeinen +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/video/tgafb.c | 264 +++++++++----------------------------------------- + 1 file changed, 51 insertions(+), 213 deletions(-) + +--- a/drivers/video/tgafb.c ++++ b/drivers/video/tgafb.c +@@ -1146,222 +1146,57 @@ copyarea_line_32bpp(struct fb_info *info + __raw_writel(TGA_MODE_SBM_24BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG); + } + +-/* The general case of forward copy in 8bpp mode. */ ++/* The (almost) general case of backward copy in 8bpp mode. */ + static inline void +-copyarea_foreward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy, +- u32 height, u32 width, u32 line_length) ++copyarea_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy, ++ u32 height, u32 width, u32 line_length, ++ const struct fb_copyarea *area) + { + struct tga_par *par = (struct tga_par *) info->par; +- unsigned long i, copied, left; +- unsigned long dpos, spos, dalign, salign, yincr; +- u32 smask_first, dmask_first, dmask_last; +- int pixel_shift, need_prime, need_second; +- unsigned long n64, n32, xincr_first; ++ unsigned i, yincr; ++ int depos, sepos, backward, last_step, step; ++ u32 mask_last; ++ unsigned n32; + void __iomem *tga_regs; + void __iomem *tga_fb; + +- yincr = line_length; +- if (dy > sy) { +- dy += height - 1; +- sy += height - 1; +- yincr = -yincr; +- } +- +- /* Compute the offsets and alignments in the frame buffer. +- More than anything else, these control how we do copies. */ +- dpos = dy * line_length + dx; +- spos = sy * line_length + sx; +- dalign = dpos & 7; +- salign = spos & 7; +- dpos &= -8; +- spos &= -8; +- +- /* Compute the value for the PIXELSHIFT register. This controls +- both non-co-aligned source and destination and copy direction. */ +- if (dalign >= salign) +- pixel_shift = dalign - salign; +- else +- pixel_shift = 8 - (salign - dalign); +- +- /* Figure out if we need an additional priming step for the +- residue register. */ +- need_prime = (salign > dalign); +- if (need_prime) +- dpos -= 8; +- +- /* Begin by copying the leading unaligned destination. Copy enough +- to make the next destination address 32-byte aligned. */ +- copied = 32 - (dalign + (dpos & 31)); +- if (copied == 32) +- copied = 0; +- xincr_first = (copied + 7) & -8; +- smask_first = dmask_first = (1ul << copied) - 1; +- smask_first <<= salign; +- dmask_first <<= dalign + need_prime*8; +- if (need_prime && copied > 24) +- copied -= 8; +- left = width - copied; +- +- /* Care for small copies. */ +- if (copied > width) { +- u32 t; +- t = (1ul << width) - 1; +- t <<= dalign + need_prime*8; +- dmask_first &= t; +- left = 0; +- } +- +- /* Attempt to use 64-byte copies. This is only possible if the +- source and destination are co-aligned at 64 bytes. */ +- n64 = need_second = 0; +- if ((dpos & 63) == (spos & 63) +- && (height == 1 || line_length % 64 == 0)) { +- /* We may need a 32-byte copy to ensure 64 byte alignment. */ +- need_second = (dpos + xincr_first) & 63; +- if ((need_second & 32) != need_second) +- printk(KERN_ERR "tgafb: need_second wrong\n"); +- if (left >= need_second + 64) { +- left -= need_second; +- n64 = left / 64; +- left %= 64; +- } else +- need_second = 0; +- } +- +- /* Copy trailing full 32-byte sections. This will be the main +- loop if the 64 byte loop can't be used. */ +- n32 = left / 32; +- left %= 32; +- +- /* Copy the trailing unaligned destination. */ +- dmask_last = (1ul << left) - 1; +- +- tga_regs = par->tga_regs_base; +- tga_fb = par->tga_fb_base; +- +- /* Set up the MODE and PIXELSHIFT registers. */ +- __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_COPY, tga_regs+TGA_MODE_REG); +- __raw_writel(pixel_shift, tga_regs+TGA_PIXELSHIFT_REG); +- wmb(); +- +- for (i = 0; i < height; ++i) { +- unsigned long j; +- void __iomem *sfb; +- void __iomem *dfb; +- +- sfb = tga_fb + spos; +- dfb = tga_fb + dpos; +- if (dmask_first) { +- __raw_writel(smask_first, sfb); +- wmb(); +- __raw_writel(dmask_first, dfb); +- wmb(); +- sfb += xincr_first; +- dfb += xincr_first; +- } +- +- if (need_second) { +- __raw_writel(0xffffffff, sfb); +- wmb(); +- __raw_writel(0xffffffff, dfb); +- wmb(); +- sfb += 32; +- dfb += 32; +- } +- +- if (n64 && (((unsigned long)sfb | (unsigned long)dfb) & 63)) +- printk(KERN_ERR +- "tgafb: misaligned copy64 (s:%p, d:%p)\n", +- sfb, dfb); +- +- for (j = 0; j < n64; ++j) { +- __raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC); +- wmb(); +- __raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST); +- wmb(); +- sfb += 64; +- dfb += 64; +- } +- +- for (j = 0; j < n32; ++j) { +- __raw_writel(0xffffffff, sfb); +- wmb(); +- __raw_writel(0xffffffff, dfb); +- wmb(); +- sfb += 32; +- dfb += 32; +- } +- +- if (dmask_last) { +- __raw_writel(0xffffffff, sfb); +- wmb(); +- __raw_writel(dmask_last, dfb); +- wmb(); +- } +- +- spos += yincr; +- dpos += yincr; ++ /* Do acceleration only if we are aligned on 8 pixels */ ++ if ((dx | sx | width) & 7) { ++ cfb_copyarea(info, area); ++ return; + } + +- /* Reset the MODE register to normal. */ +- __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG); +-} +- +-/* The (almost) general case of backward copy in 8bpp mode. */ +-static inline void +-copyarea_backward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy, +- u32 height, u32 width, u32 line_length, +- const struct fb_copyarea *area) +-{ +- struct tga_par *par = (struct tga_par *) info->par; +- unsigned long i, left, yincr; +- unsigned long depos, sepos, dealign, sealign; +- u32 mask_first, mask_last; +- unsigned long n32; +- void __iomem *tga_regs; +- void __iomem *tga_fb; +- + yincr = line_length; + if (dy > sy) { + dy += height - 1; + sy += height - 1; + yincr = -yincr; + } ++ backward = dy == sy && dx > sx && dx < sx + width; + + /* Compute the offsets and alignments in the frame buffer. + More than anything else, these control how we do copies. */ +- depos = dy * line_length + dx + width; +- sepos = sy * line_length + sx + width; +- dealign = depos & 7; +- sealign = sepos & 7; +- +- /* ??? The documentation appears to be incorrect (or very +- misleading) wrt how pixel shifting works in backward copy +- mode, i.e. when PIXELSHIFT is negative. I give up for now. +- Do handle the common case of co-aligned backward copies, +- but frob everything else back on generic code. */ +- if (dealign != sealign) { +- cfb_copyarea(info, area); +- return; +- } +- +- /* We begin the copy with the trailing pixels of the +- unaligned destination. */ +- mask_first = (1ul << dealign) - 1; +- left = width - dealign; +- +- /* Care for small copies. */ +- if (dealign > width) { +- mask_first ^= (1ul << (dealign - width)) - 1; +- left = 0; +- } ++ depos = dy * line_length + dx; ++ sepos = sy * line_length + sx; ++ if (backward) ++ depos += width, sepos += width; + + /* Next copy full words at a time. */ +- n32 = left / 32; +- left %= 32; ++ n32 = width / 32; ++ last_step = width % 32; + + /* Finally copy the unaligned head of the span. */ +- mask_last = -1 << (32 - left); ++ mask_last = (1ul << last_step) - 1; ++ ++ if (!backward) { ++ step = 32; ++ last_step = 32; ++ } else { ++ step = -32; ++ last_step = -last_step; ++ sepos -= 32; ++ depos -= 32; ++ } + + tga_regs = par->tga_regs_base; + tga_fb = par->tga_fb_base; +@@ -1378,25 +1213,33 @@ copyarea_backward_8bpp(struct fb_info *i + + sfb = tga_fb + sepos; + dfb = tga_fb + depos; +- if (mask_first) { +- __raw_writel(mask_first, sfb); +- wmb(); +- __raw_writel(mask_first, dfb); +- wmb(); +- } + +- for (j = 0; j < n32; ++j) { +- sfb -= 32; +- dfb -= 32; ++ for (j = 0; j < n32; j++) { ++ if (j < 2 && j + 1 < n32 && !backward && ++ !(((unsigned long)sfb | (unsigned long)dfb) & 63)) { ++ do { ++ __raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC); ++ wmb(); ++ __raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST); ++ wmb(); ++ sfb += 64; ++ dfb += 64; ++ j += 2; ++ } while (j + 1 < n32); ++ j--; ++ continue; ++ } + __raw_writel(0xffffffff, sfb); + wmb(); + __raw_writel(0xffffffff, dfb); + wmb(); ++ sfb += step; ++ dfb += step; + } + + if (mask_last) { +- sfb -= 32; +- dfb -= 32; ++ sfb += last_step - step; ++ dfb += last_step - step; + __raw_writel(mask_last, sfb); + wmb(); + __raw_writel(mask_last, dfb); +@@ -1457,14 +1300,9 @@ tgafb_copyarea(struct fb_info *info, con + else if (bpp == 32) + cfb_copyarea(info, area); + +- /* Detect overlapping source and destination that requires +- a backward copy. */ +- else if (dy == sy && dx > sx && dx < sx + width) +- copyarea_backward_8bpp(info, dx, dy, sx, sy, height, +- width, line_length, area); + else +- copyarea_foreward_8bpp(info, dx, dy, sx, sy, height, +- width, line_length); ++ copyarea_8bpp(info, dx, dy, sx, sy, height, ++ width, line_length, area); + } + +