cfbcopyarea.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. /*
  2. * Generic function for frame buffer with packed pixels of any depth.
  3. *
  4. * Copyright (C) 1999-2005 James Simmons <jsimmons@www.infradead.org>
  5. *
  6. * This file is subject to the terms and conditions of the GNU General Public
  7. * License. See the file COPYING in the main directory of this archive for
  8. * more details.
  9. *
  10. * NOTES:
  11. *
  12. * This is for cfb packed pixels. Iplan and such are incorporated in the
  13. * drivers that need them.
  14. *
  15. * FIXME
  16. *
  17. * Also need to add code to deal with cards endians that are different than
  18. * the native cpu endians. I also need to deal with MSB position in the word.
  19. *
  20. * The two functions or copying forward and backward could be split up like
  21. * the ones for filling, i.e. in aligned and unaligned versions. This would
  22. * help moving some redundant computations and branches out of the loop, too.
  23. */
  24. #include <linux/module.h>
  25. #include <linux/kernel.h>
  26. #include <linux/string.h>
  27. #include <linux/fb.h>
  28. #include <asm/types.h>
  29. #include <asm/io.h>
  30. #include "fb_draw.h"
  31. #if BITS_PER_LONG == 32
  32. # define FB_WRITEL fb_writel
  33. # define FB_READL fb_readl
  34. #else
  35. # define FB_WRITEL fb_writeq
  36. # define FB_READL fb_readq
  37. #endif
  38. /*
  39. * Generic bitwise copy algorithm
  40. */
  41. static void
  42. bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
  43. const unsigned long __iomem *src, unsigned src_idx, int bits,
  44. unsigned n, u32 bswapmask)
  45. {
  46. unsigned long first, last;
  47. int const shift = dst_idx-src_idx;
  48. #if 0
  49. /*
  50. * If you suspect bug in this function, compare it with this simple
  51. * memmove implementation.
  52. */
  53. memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
  54. (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
  55. return;
  56. #endif
  57. first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
  58. last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
  59. if (!shift) {
  60. // Same alignment for source and dest
  61. if (dst_idx+n <= bits) {
  62. // Single word
  63. if (last)
  64. first &= last;
  65. FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
  66. } else {
  67. // Multiple destination words
  68. // Leading bits
  69. if (first != ~0UL) {
  70. FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
  71. dst++;
  72. src++;
  73. n -= bits - dst_idx;
  74. }
  75. // Main chunk
  76. n /= bits;
  77. while (n >= 8) {
  78. FB_WRITEL(FB_READL(src++), dst++);
  79. FB_WRITEL(FB_READL(src++), dst++);
  80. FB_WRITEL(FB_READL(src++), dst++);
  81. FB_WRITEL(FB_READL(src++), dst++);
  82. FB_WRITEL(FB_READL(src++), dst++);
  83. FB_WRITEL(FB_READL(src++), dst++);
  84. FB_WRITEL(FB_READL(src++), dst++);
  85. FB_WRITEL(FB_READL(src++), dst++);
  86. n -= 8;
  87. }
  88. while (n--)
  89. FB_WRITEL(FB_READL(src++), dst++);
  90. // Trailing bits
  91. if (last)
  92. FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
  93. }
  94. } else {
  95. /* Different alignment for source and dest */
  96. unsigned long d0, d1;
  97. int m;
  98. int const left = shift & (bits - 1);
  99. int const right = -shift & (bits - 1);
  100. if (dst_idx+n <= bits) {
  101. // Single destination word
  102. if (last)
  103. first &= last;
  104. d0 = FB_READL(src);
  105. d0 = fb_rev_pixels_in_long(d0, bswapmask);
  106. if (shift > 0) {
  107. // Single source word
  108. d0 <<= left;
  109. } else if (src_idx+n <= bits) {
  110. // Single source word
  111. d0 >>= right;
  112. } else {
  113. // 2 source words
  114. d1 = FB_READL(src + 1);
  115. d1 = fb_rev_pixels_in_long(d1, bswapmask);
  116. d0 = d0 >> right | d1 << left;
  117. }
  118. d0 = fb_rev_pixels_in_long(d0, bswapmask);
  119. FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
  120. } else {
  121. // Multiple destination words
  122. /** We must always remember the last value read, because in case
  123. SRC and DST overlap bitwise (e.g. when moving just one pixel in
  124. 1bpp), we always collect one full long for DST and that might
  125. overlap with the current long from SRC. We store this value in
  126. 'd0'. */
  127. d0 = FB_READL(src++);
  128. d0 = fb_rev_pixels_in_long(d0, bswapmask);
  129. // Leading bits
  130. if (shift > 0) {
  131. // Single source word
  132. d1 = d0;
  133. d0 <<= left;
  134. n -= bits - dst_idx;
  135. } else {
  136. // 2 source words
  137. d1 = FB_READL(src++);
  138. d1 = fb_rev_pixels_in_long(d1, bswapmask);
  139. d0 = d0 >> right | d1 << left;
  140. n -= bits - dst_idx;
  141. }
  142. d0 = fb_rev_pixels_in_long(d0, bswapmask);
  143. FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
  144. d0 = d1;
  145. dst++;
  146. // Main chunk
  147. m = n % bits;
  148. n /= bits;
  149. while ((n >= 4) && !bswapmask) {
  150. d1 = FB_READL(src++);
  151. FB_WRITEL(d0 >> right | d1 << left, dst++);
  152. d0 = d1;
  153. d1 = FB_READL(src++);
  154. FB_WRITEL(d0 >> right | d1 << left, dst++);
  155. d0 = d1;
  156. d1 = FB_READL(src++);
  157. FB_WRITEL(d0 >> right | d1 << left, dst++);
  158. d0 = d1;
  159. d1 = FB_READL(src++);
  160. FB_WRITEL(d0 >> right | d1 << left, dst++);
  161. d0 = d1;
  162. n -= 4;
  163. }
  164. while (n--) {
  165. d1 = FB_READL(src++);
  166. d1 = fb_rev_pixels_in_long(d1, bswapmask);
  167. d0 = d0 >> right | d1 << left;
  168. d0 = fb_rev_pixels_in_long(d0, bswapmask);
  169. FB_WRITEL(d0, dst++);
  170. d0 = d1;
  171. }
  172. // Trailing bits
  173. if (m) {
  174. if (m <= bits - right) {
  175. // Single source word
  176. d0 >>= right;
  177. } else {
  178. // 2 source words
  179. d1 = FB_READL(src);
  180. d1 = fb_rev_pixels_in_long(d1,
  181. bswapmask);
  182. d0 = d0 >> right | d1 << left;
  183. }
  184. d0 = fb_rev_pixels_in_long(d0, bswapmask);
  185. FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
  186. }
  187. }
  188. }
  189. }
  190. /*
  191. * Generic bitwise copy algorithm, operating backward
  192. */
  193. static void
  194. bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
  195. const unsigned long __iomem *src, unsigned src_idx, int bits,
  196. unsigned n, u32 bswapmask)
  197. {
  198. unsigned long first, last;
  199. int shift;
  200. #if 0
  201. /*
  202. * If you suspect bug in this function, compare it with this simple
  203. * memmove implementation.
  204. */
  205. memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
  206. (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
  207. return;
  208. #endif
  209. dst += (dst_idx + n - 1) / bits;
  210. src += (src_idx + n - 1) / bits;
  211. dst_idx = (dst_idx + n - 1) % bits;
  212. src_idx = (src_idx + n - 1) % bits;
  213. shift = dst_idx-src_idx;
  214. first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask);
  215. last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask);
  216. if (!shift) {
  217. // Same alignment for source and dest
  218. if ((unsigned long)dst_idx+1 >= n) {
  219. // Single word
  220. if (first)
  221. last &= first;
  222. FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
  223. } else {
  224. // Multiple destination words
  225. // Leading bits
  226. if (first) {
  227. FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
  228. dst--;
  229. src--;
  230. n -= dst_idx+1;
  231. }
  232. // Main chunk
  233. n /= bits;
  234. while (n >= 8) {
  235. FB_WRITEL(FB_READL(src--), dst--);
  236. FB_WRITEL(FB_READL(src--), dst--);
  237. FB_WRITEL(FB_READL(src--), dst--);
  238. FB_WRITEL(FB_READL(src--), dst--);
  239. FB_WRITEL(FB_READL(src--), dst--);
  240. FB_WRITEL(FB_READL(src--), dst--);
  241. FB_WRITEL(FB_READL(src--), dst--);
  242. FB_WRITEL(FB_READL(src--), dst--);
  243. n -= 8;
  244. }
  245. while (n--)
  246. FB_WRITEL(FB_READL(src--), dst--);
  247. // Trailing bits
  248. if (last != -1UL)
  249. FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
  250. }
  251. } else {
  252. // Different alignment for source and dest
  253. unsigned long d0, d1;
  254. int m;
  255. int const left = shift & (bits-1);
  256. int const right = -shift & (bits-1);
  257. if ((unsigned long)dst_idx+1 >= n) {
  258. // Single destination word
  259. if (first)
  260. last &= first;
  261. d0 = FB_READL(src);
  262. if (shift < 0) {
  263. // Single source word
  264. d0 >>= right;
  265. } else if (1+(unsigned long)src_idx >= n) {
  266. // Single source word
  267. d0 <<= left;
  268. } else {
  269. // 2 source words
  270. d1 = FB_READL(src - 1);
  271. d1 = fb_rev_pixels_in_long(d1, bswapmask);
  272. d0 = d0 << left | d1 >> right;
  273. }
  274. d0 = fb_rev_pixels_in_long(d0, bswapmask);
  275. FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
  276. } else {
  277. // Multiple destination words
  278. /** We must always remember the last value read, because in case
  279. SRC and DST overlap bitwise (e.g. when moving just one pixel in
  280. 1bpp), we always collect one full long for DST and that might
  281. overlap with the current long from SRC. We store this value in
  282. 'd0'. */
  283. d0 = FB_READL(src--);
  284. d0 = fb_rev_pixels_in_long(d0, bswapmask);
  285. // Leading bits
  286. if (shift < 0) {
  287. // Single source word
  288. d1 = d0;
  289. d0 >>= right;
  290. } else {
  291. // 2 source words
  292. d1 = FB_READL(src--);
  293. d1 = fb_rev_pixels_in_long(d1, bswapmask);
  294. d0 = d0 << left | d1 >> right;
  295. }
  296. d0 = fb_rev_pixels_in_long(d0, bswapmask);
  297. if (!first)
  298. FB_WRITEL(d0, dst);
  299. else
  300. FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
  301. d0 = d1;
  302. dst--;
  303. n -= dst_idx+1;
  304. // Main chunk
  305. m = n % bits;
  306. n /= bits;
  307. while ((n >= 4) && !bswapmask) {
  308. d1 = FB_READL(src--);
  309. FB_WRITEL(d0 << left | d1 >> right, dst--);
  310. d0 = d1;
  311. d1 = FB_READL(src--);
  312. FB_WRITEL(d0 << left | d1 >> right, dst--);
  313. d0 = d1;
  314. d1 = FB_READL(src--);
  315. FB_WRITEL(d0 << left | d1 >> right, dst--);
  316. d0 = d1;
  317. d1 = FB_READL(src--);
  318. FB_WRITEL(d0 << left | d1 >> right, dst--);
  319. d0 = d1;
  320. n -= 4;
  321. }
  322. while (n--) {
  323. d1 = FB_READL(src--);
  324. d1 = fb_rev_pixels_in_long(d1, bswapmask);
  325. d0 = d0 << left | d1 >> right;
  326. d0 = fb_rev_pixels_in_long(d0, bswapmask);
  327. FB_WRITEL(d0, dst--);
  328. d0 = d1;
  329. }
  330. // Trailing bits
  331. if (m) {
  332. if (m <= bits - left) {
  333. // Single source word
  334. d0 <<= left;
  335. } else {
  336. // 2 source words
  337. d1 = FB_READL(src);
  338. d1 = fb_rev_pixels_in_long(d1,
  339. bswapmask);
  340. d0 = d0 << left | d1 >> right;
  341. }
  342. d0 = fb_rev_pixels_in_long(d0, bswapmask);
  343. FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
  344. }
  345. }
  346. }
  347. }
  348. void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
  349. {
  350. u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
  351. u32 height = area->height, width = area->width;
  352. unsigned long const bits_per_line = p->fix.line_length*8u;
  353. unsigned long __iomem *base = NULL;
  354. int bits = BITS_PER_LONG, bytes = bits >> 3;
  355. unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
  356. u32 bswapmask = fb_compute_bswapmask(p);
  357. if (p->state != FBINFO_STATE_RUNNING)
  358. return;
  359. /* if the beginning of the target area might overlap with the end of
  360. the source area, be have to copy the area reverse. */
  361. if ((dy == sy && dx > sx) || (dy > sy)) {
  362. dy += height;
  363. sy += height;
  364. rev_copy = 1;
  365. }
  366. // split the base of the framebuffer into a long-aligned address and the
  367. // index of the first bit
  368. base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
  369. dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
  370. // add offset of source and target area
  371. dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
  372. src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
  373. if (p->fbops->fb_sync)
  374. p->fbops->fb_sync(p);
  375. if (rev_copy) {
  376. while (height--) {
  377. dst_idx -= bits_per_line;
  378. src_idx -= bits_per_line;
  379. bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
  380. base + (src_idx / bits), src_idx % bits, bits,
  381. width*p->var.bits_per_pixel, bswapmask);
  382. }
  383. } else {
  384. while (height--) {
  385. bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
  386. base + (src_idx / bits), src_idx % bits, bits,
  387. width*p->var.bits_per_pixel, bswapmask);
  388. dst_idx += bits_per_line;
  389. src_idx += bits_per_line;
  390. }
  391. }
  392. }
  393. EXPORT_SYMBOL(cfb_copyarea);
  394. MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
  395. MODULE_DESCRIPTION("Generic software accelerated copyarea");
  396. MODULE_LICENSE("GPL");