Skip to content

Commit fd9a5e6

Browse files
Mikulas Patockagregkh
authored andcommitted
tgafb: fix data copying
commit 6b0df68 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 <mpatocka@redhat.com> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 0e0dc73 commit fd9a5e6

File tree

1 file changed

+51
-213
lines changed

1 file changed

+51
-213
lines changed

drivers/video/tgafb.c

Lines changed: 51 additions & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,222 +1146,57 @@ copyarea_line_32bpp(struct fb_info *info, u32 dy, u32 sy,
11461146
__raw_writel(TGA_MODE_SBM_24BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
11471147
}
11481148

1149-
/* The general case of forward copy in 8bpp mode. */
1149+
/* The (almost) general case of backward copy in 8bpp mode. */
11501150
static inline void
1151-
copyarea_foreward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
1152-
u32 height, u32 width, u32 line_length)
1151+
copyarea_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
1152+
u32 height, u32 width, u32 line_length,
1153+
const struct fb_copyarea *area)
11531154
{
11541155
struct tga_par *par = (struct tga_par *) info->par;
1155-
unsigned long i, copied, left;
1156-
unsigned long dpos, spos, dalign, salign, yincr;
1157-
u32 smask_first, dmask_first, dmask_last;
1158-
int pixel_shift, need_prime, need_second;
1159-
unsigned long n64, n32, xincr_first;
1156+
unsigned i, yincr;
1157+
int depos, sepos, backward, last_step, step;
1158+
u32 mask_last;
1159+
unsigned n32;
11601160
void __iomem *tga_regs;
11611161
void __iomem *tga_fb;
11621162

1163-
yincr = line_length;
1164-
if (dy > sy) {
1165-
dy += height - 1;
1166-
sy += height - 1;
1167-
yincr = -yincr;
1168-
}
1169-
1170-
/* Compute the offsets and alignments in the frame buffer.
1171-
More than anything else, these control how we do copies. */
1172-
dpos = dy * line_length + dx;
1173-
spos = sy * line_length + sx;
1174-
dalign = dpos & 7;
1175-
salign = spos & 7;
1176-
dpos &= -8;
1177-
spos &= -8;
1178-
1179-
/* Compute the value for the PIXELSHIFT register. This controls
1180-
both non-co-aligned source and destination and copy direction. */
1181-
if (dalign >= salign)
1182-
pixel_shift = dalign - salign;
1183-
else
1184-
pixel_shift = 8 - (salign - dalign);
1185-
1186-
/* Figure out if we need an additional priming step for the
1187-
residue register. */
1188-
need_prime = (salign > dalign);
1189-
if (need_prime)
1190-
dpos -= 8;
1191-
1192-
/* Begin by copying the leading unaligned destination. Copy enough
1193-
to make the next destination address 32-byte aligned. */
1194-
copied = 32 - (dalign + (dpos & 31));
1195-
if (copied == 32)
1196-
copied = 0;
1197-
xincr_first = (copied + 7) & -8;
1198-
smask_first = dmask_first = (1ul << copied) - 1;
1199-
smask_first <<= salign;
1200-
dmask_first <<= dalign + need_prime*8;
1201-
if (need_prime && copied > 24)
1202-
copied -= 8;
1203-
left = width - copied;
1204-
1205-
/* Care for small copies. */
1206-
if (copied > width) {
1207-
u32 t;
1208-
t = (1ul << width) - 1;
1209-
t <<= dalign + need_prime*8;
1210-
dmask_first &= t;
1211-
left = 0;
1212-
}
1213-
1214-
/* Attempt to use 64-byte copies. This is only possible if the
1215-
source and destination are co-aligned at 64 bytes. */
1216-
n64 = need_second = 0;
1217-
if ((dpos & 63) == (spos & 63)
1218-
&& (height == 1 || line_length % 64 == 0)) {
1219-
/* We may need a 32-byte copy to ensure 64 byte alignment. */
1220-
need_second = (dpos + xincr_first) & 63;
1221-
if ((need_second & 32) != need_second)
1222-
printk(KERN_ERR "tgafb: need_second wrong\n");
1223-
if (left >= need_second + 64) {
1224-
left -= need_second;
1225-
n64 = left / 64;
1226-
left %= 64;
1227-
} else
1228-
need_second = 0;
1229-
}
1230-
1231-
/* Copy trailing full 32-byte sections. This will be the main
1232-
loop if the 64 byte loop can't be used. */
1233-
n32 = left / 32;
1234-
left %= 32;
1235-
1236-
/* Copy the trailing unaligned destination. */
1237-
dmask_last = (1ul << left) - 1;
1238-
1239-
tga_regs = par->tga_regs_base;
1240-
tga_fb = par->tga_fb_base;
1241-
1242-
/* Set up the MODE and PIXELSHIFT registers. */
1243-
__raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1244-
__raw_writel(pixel_shift, tga_regs+TGA_PIXELSHIFT_REG);
1245-
wmb();
1246-
1247-
for (i = 0; i < height; ++i) {
1248-
unsigned long j;
1249-
void __iomem *sfb;
1250-
void __iomem *dfb;
1251-
1252-
sfb = tga_fb + spos;
1253-
dfb = tga_fb + dpos;
1254-
if (dmask_first) {
1255-
__raw_writel(smask_first, sfb);
1256-
wmb();
1257-
__raw_writel(dmask_first, dfb);
1258-
wmb();
1259-
sfb += xincr_first;
1260-
dfb += xincr_first;
1261-
}
1262-
1263-
if (need_second) {
1264-
__raw_writel(0xffffffff, sfb);
1265-
wmb();
1266-
__raw_writel(0xffffffff, dfb);
1267-
wmb();
1268-
sfb += 32;
1269-
dfb += 32;
1270-
}
1271-
1272-
if (n64 && (((unsigned long)sfb | (unsigned long)dfb) & 63))
1273-
printk(KERN_ERR
1274-
"tgafb: misaligned copy64 (s:%p, d:%p)\n",
1275-
sfb, dfb);
1276-
1277-
for (j = 0; j < n64; ++j) {
1278-
__raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC);
1279-
wmb();
1280-
__raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST);
1281-
wmb();
1282-
sfb += 64;
1283-
dfb += 64;
1284-
}
1285-
1286-
for (j = 0; j < n32; ++j) {
1287-
__raw_writel(0xffffffff, sfb);
1288-
wmb();
1289-
__raw_writel(0xffffffff, dfb);
1290-
wmb();
1291-
sfb += 32;
1292-
dfb += 32;
1293-
}
1294-
1295-
if (dmask_last) {
1296-
__raw_writel(0xffffffff, sfb);
1297-
wmb();
1298-
__raw_writel(dmask_last, dfb);
1299-
wmb();
1300-
}
1301-
1302-
spos += yincr;
1303-
dpos += yincr;
1163+
/* Do acceleration only if we are aligned on 8 pixels */
1164+
if ((dx | sx | width) & 7) {
1165+
cfb_copyarea(info, area);
1166+
return;
13041167
}
13051168

1306-
/* Reset the MODE register to normal. */
1307-
__raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1308-
}
1309-
1310-
/* The (almost) general case of backward copy in 8bpp mode. */
1311-
static inline void
1312-
copyarea_backward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
1313-
u32 height, u32 width, u32 line_length,
1314-
const struct fb_copyarea *area)
1315-
{
1316-
struct tga_par *par = (struct tga_par *) info->par;
1317-
unsigned long i, left, yincr;
1318-
unsigned long depos, sepos, dealign, sealign;
1319-
u32 mask_first, mask_last;
1320-
unsigned long n32;
1321-
void __iomem *tga_regs;
1322-
void __iomem *tga_fb;
1323-
13241169
yincr = line_length;
13251170
if (dy > sy) {
13261171
dy += height - 1;
13271172
sy += height - 1;
13281173
yincr = -yincr;
13291174
}
1175+
backward = dy == sy && dx > sx && dx < sx + width;
13301176

13311177
/* Compute the offsets and alignments in the frame buffer.
13321178
More than anything else, these control how we do copies. */
1333-
depos = dy * line_length + dx + width;
1334-
sepos = sy * line_length + sx + width;
1335-
dealign = depos & 7;
1336-
sealign = sepos & 7;
1337-
1338-
/* ??? The documentation appears to be incorrect (or very
1339-
misleading) wrt how pixel shifting works in backward copy
1340-
mode, i.e. when PIXELSHIFT is negative. I give up for now.
1341-
Do handle the common case of co-aligned backward copies,
1342-
but frob everything else back on generic code. */
1343-
if (dealign != sealign) {
1344-
cfb_copyarea(info, area);
1345-
return;
1346-
}
1347-
1348-
/* We begin the copy with the trailing pixels of the
1349-
unaligned destination. */
1350-
mask_first = (1ul << dealign) - 1;
1351-
left = width - dealign;
1352-
1353-
/* Care for small copies. */
1354-
if (dealign > width) {
1355-
mask_first ^= (1ul << (dealign - width)) - 1;
1356-
left = 0;
1357-
}
1179+
depos = dy * line_length + dx;
1180+
sepos = sy * line_length + sx;
1181+
if (backward)
1182+
depos += width, sepos += width;
13581183

13591184
/* Next copy full words at a time. */
1360-
n32 = left / 32;
1361-
left %= 32;
1185+
n32 = width / 32;
1186+
last_step = width % 32;
13621187

13631188
/* Finally copy the unaligned head of the span. */
1364-
mask_last = -1 << (32 - left);
1189+
mask_last = (1ul << last_step) - 1;
1190+
1191+
if (!backward) {
1192+
step = 32;
1193+
last_step = 32;
1194+
} else {
1195+
step = -32;
1196+
last_step = -last_step;
1197+
sepos -= 32;
1198+
depos -= 32;
1199+
}
13651200

13661201
tga_regs = par->tga_regs_base;
13671202
tga_fb = par->tga_fb_base;
@@ -1378,25 +1213,33 @@ copyarea_backward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
13781213

13791214
sfb = tga_fb + sepos;
13801215
dfb = tga_fb + depos;
1381-
if (mask_first) {
1382-
__raw_writel(mask_first, sfb);
1383-
wmb();
1384-
__raw_writel(mask_first, dfb);
1385-
wmb();
1386-
}
13871216

1388-
for (j = 0; j < n32; ++j) {
1389-
sfb -= 32;
1390-
dfb -= 32;
1217+
for (j = 0; j < n32; j++) {
1218+
if (j < 2 && j + 1 < n32 && !backward &&
1219+
!(((unsigned long)sfb | (unsigned long)dfb) & 63)) {
1220+
do {
1221+
__raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC);
1222+
wmb();
1223+
__raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST);
1224+
wmb();
1225+
sfb += 64;
1226+
dfb += 64;
1227+
j += 2;
1228+
} while (j + 1 < n32);
1229+
j--;
1230+
continue;
1231+
}
13911232
__raw_writel(0xffffffff, sfb);
13921233
wmb();
13931234
__raw_writel(0xffffffff, dfb);
13941235
wmb();
1236+
sfb += step;
1237+
dfb += step;
13951238
}
13961239

13971240
if (mask_last) {
1398-
sfb -= 32;
1399-
dfb -= 32;
1241+
sfb += last_step - step;
1242+
dfb += last_step - step;
14001243
__raw_writel(mask_last, sfb);
14011244
wmb();
14021245
__raw_writel(mask_last, dfb);
@@ -1457,14 +1300,9 @@ tgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
14571300
else if (bpp == 32)
14581301
cfb_copyarea(info, area);
14591302

1460-
/* Detect overlapping source and destination that requires
1461-
a backward copy. */
1462-
else if (dy == sy && dx > sx && dx < sx + width)
1463-
copyarea_backward_8bpp(info, dx, dy, sx, sy, height,
1464-
width, line_length, area);
14651303
else
1466-
copyarea_foreward_8bpp(info, dx, dy, sx, sy, height,
1467-
width, line_length);
1304+
copyarea_8bpp(info, dx, dy, sx, sy, height,
1305+
width, line_length, area);
14681306
}
14691307

14701308

0 commit comments

Comments
 (0)