cfbcopyarea.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  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 <linux/slab.h>
  29. #include <asm/types.h>
  30. #include <asm/io.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. * Compose two values, using a bitmask as decision value
  40. * This is equivalent to (a & mask) | (b & ~mask)
  41. */
  42. static inline unsigned long
  43. comp(unsigned long a, unsigned long b, unsigned long mask)
  44. {
  45. return ((a ^ b) & mask) ^ b;
  46. }
  47. /*
  48. * Generic bitwise copy algorithm
  49. */
  50. static void
  51. bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
  52. int src_idx, int bits, unsigned n)
  53. {
  54. unsigned long first, last;
  55. int const shift = dst_idx-src_idx;
  56. int left, right;
  57. first = FB_SHIFT_HIGH(~0UL, dst_idx);
  58. last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
  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. unsigned long d0, d1;
  96. int m;
  97. // Different alignment for source and dest
  98. right = shift & (bits - 1);
  99. left = -shift & (bits - 1);
  100. if (dst_idx+n <= bits) {
  101. // Single destination word
  102. if (last)
  103. first &= last;
  104. if (shift > 0) {
  105. // Single source word
  106. FB_WRITEL( comp( FB_READL(src) >> right, FB_READL(dst), first), dst);
  107. } else if (src_idx+n <= bits) {
  108. // Single source word
  109. FB_WRITEL( comp(FB_READL(src) << left, FB_READL(dst), first), dst);
  110. } else {
  111. // 2 source words
  112. d0 = FB_READL(src++);
  113. d1 = FB_READL(src);
  114. FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
  115. }
  116. } else {
  117. // Multiple destination words
  118. /** We must always remember the last value read, because in case
  119. SRC and DST overlap bitwise (e.g. when moving just one pixel in
  120. 1bpp), we always collect one full long for DST and that might
  121. overlap with the current long from SRC. We store this value in
  122. 'd0'. */
  123. d0 = FB_READL(src++);
  124. // Leading bits
  125. if (shift > 0) {
  126. // Single source word
  127. FB_WRITEL( comp(d0 >> right, FB_READL(dst), first), dst);
  128. dst++;
  129. n -= bits - dst_idx;
  130. } else {
  131. // 2 source words
  132. d1 = FB_READL(src++);
  133. FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
  134. d0 = d1;
  135. dst++;
  136. n -= bits - dst_idx;
  137. }
  138. // Main chunk
  139. m = n % bits;
  140. n /= bits;
  141. while (n >= 4) {
  142. d1 = FB_READL(src++);
  143. FB_WRITEL(d0 << left | d1 >> right, dst++);
  144. d0 = d1;
  145. d1 = FB_READL(src++);
  146. FB_WRITEL(d0 << left | d1 >> right, dst++);
  147. d0 = d1;
  148. d1 = FB_READL(src++);
  149. FB_WRITEL(d0 << left | d1 >> right, dst++);
  150. d0 = d1;
  151. d1 = FB_READL(src++);
  152. FB_WRITEL(d0 << left | d1 >> right, dst++);
  153. d0 = d1;
  154. n -= 4;
  155. }
  156. while (n--) {
  157. d1 = FB_READL(src++);
  158. FB_WRITEL(d0 << left | d1 >> right, dst++);
  159. d0 = d1;
  160. }
  161. // Trailing bits
  162. if (last) {
  163. if (m <= right) {
  164. // Single source word
  165. FB_WRITEL( comp(d0 << left, FB_READL(dst), last), dst);
  166. } else {
  167. // 2 source words
  168. d1 = FB_READL(src);
  169. FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), last), dst);
  170. }
  171. }
  172. }
  173. }
  174. }
  175. /*
  176. * Generic bitwise copy algorithm, operating backward
  177. */
  178. static void
  179. bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
  180. int src_idx, int bits, unsigned n)
  181. {
  182. unsigned long first, last;
  183. int shift;
  184. dst += (n-1)/bits;
  185. src += (n-1)/bits;
  186. if ((n-1) % bits) {
  187. dst_idx += (n-1) % bits;
  188. dst += dst_idx >> (ffs(bits) - 1);
  189. dst_idx &= bits - 1;
  190. src_idx += (n-1) % bits;
  191. src += src_idx >> (ffs(bits) - 1);
  192. src_idx &= bits - 1;
  193. }
  194. shift = dst_idx-src_idx;
  195. first = FB_SHIFT_LOW(~0UL, bits - 1 - dst_idx);
  196. last = ~(FB_SHIFT_LOW(~0UL, bits - 1 - ((dst_idx-n) % bits)));
  197. if (!shift) {
  198. // Same alignment for source and dest
  199. if ((unsigned long)dst_idx+1 >= n) {
  200. // Single word
  201. if (last)
  202. first &= last;
  203. FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
  204. } else {
  205. // Multiple destination words
  206. // Leading bits
  207. if (first != ~0UL) {
  208. FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
  209. dst--;
  210. src--;
  211. n -= dst_idx+1;
  212. }
  213. // Main chunk
  214. n /= bits;
  215. while (n >= 8) {
  216. FB_WRITEL(FB_READL(src--), dst--);
  217. FB_WRITEL(FB_READL(src--), dst--);
  218. FB_WRITEL(FB_READL(src--), dst--);
  219. FB_WRITEL(FB_READL(src--), dst--);
  220. FB_WRITEL(FB_READL(src--), dst--);
  221. FB_WRITEL(FB_READL(src--), dst--);
  222. FB_WRITEL(FB_READL(src--), dst--);
  223. FB_WRITEL(FB_READL(src--), dst--);
  224. n -= 8;
  225. }
  226. while (n--)
  227. FB_WRITEL(FB_READL(src--), dst--);
  228. // Trailing bits
  229. if (last)
  230. FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
  231. }
  232. } else {
  233. // Different alignment for source and dest
  234. int const left = -shift & (bits-1);
  235. int const right = shift & (bits-1);
  236. if ((unsigned long)dst_idx+1 >= n) {
  237. // Single destination word
  238. if (last)
  239. first &= last;
  240. if (shift < 0) {
  241. // Single source word
  242. FB_WRITEL( comp( FB_READL(src)<<left, FB_READL(dst), first), dst);
  243. } else if (1+(unsigned long)src_idx >= n) {
  244. // Single source word
  245. FB_WRITEL( comp( FB_READL(src)>>right, FB_READL(dst), first), dst);
  246. } else {
  247. // 2 source words
  248. FB_WRITEL( comp( (FB_READL(src)>>right | FB_READL(src-1)<<left), FB_READL(dst), first), dst);
  249. }
  250. } else {
  251. // Multiple destination words
  252. /** We must always remember the last value read, because in case
  253. SRC and DST overlap bitwise (e.g. when moving just one pixel in
  254. 1bpp), we always collect one full long for DST and that might
  255. overlap with the current long from SRC. We store this value in
  256. 'd0'. */
  257. unsigned long d0, d1;
  258. int m;
  259. d0 = FB_READL(src--);
  260. // Leading bits
  261. if (shift < 0) {
  262. // Single source word
  263. FB_WRITEL( comp( (d0 << left), FB_READL(dst), first), dst);
  264. } else {
  265. // 2 source words
  266. d1 = FB_READL(src--);
  267. FB_WRITEL( comp( (d0>>right | d1<<left), FB_READL(dst), first), dst);
  268. d0 = d1;
  269. }
  270. dst--;
  271. n -= dst_idx+1;
  272. // Main chunk
  273. m = n % bits;
  274. n /= bits;
  275. while (n >= 4) {
  276. d1 = FB_READL(src--);
  277. FB_WRITEL(d0 >> right | d1 << left, dst--);
  278. d0 = d1;
  279. d1 = FB_READL(src--);
  280. FB_WRITEL(d0 >> right | d1 << left, dst--);
  281. d0 = d1;
  282. d1 = FB_READL(src--);
  283. FB_WRITEL(d0 >> right | d1 << left, dst--);
  284. d0 = d1;
  285. d1 = FB_READL(src--);
  286. FB_WRITEL(d0 >> right | d1 << left, dst--);
  287. d0 = d1;
  288. n -= 4;
  289. }
  290. while (n--) {
  291. d1 = FB_READL(src--);
  292. FB_WRITEL(d0 >> right | d1 << left, dst--);
  293. d0 = d1;
  294. }
  295. // Trailing bits
  296. if (last) {
  297. if (m <= left) {
  298. // Single source word
  299. FB_WRITEL( comp(d0 >> right, FB_READL(dst), last), dst);
  300. } else {
  301. // 2 source words
  302. d1 = FB_READL(src);
  303. FB_WRITEL( comp(d0>>right | d1<<left, FB_READL(dst), last), dst);
  304. }
  305. }
  306. }
  307. }
  308. }
  309. void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
  310. {
  311. u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
  312. u32 height = area->height, width = area->width;
  313. unsigned long const bits_per_line = p->fix.line_length*8u;
  314. unsigned long __iomem *dst = NULL, *src = NULL;
  315. int bits = BITS_PER_LONG, bytes = bits >> 3;
  316. int dst_idx = 0, src_idx = 0, rev_copy = 0;
  317. if (p->state != FBINFO_STATE_RUNNING)
  318. return;
  319. /* if the beginning of the target area might overlap with the end of
  320. the source area, be have to copy the area reverse. */
  321. if ((dy == sy && dx > sx) || (dy > sy)) {
  322. dy += height;
  323. sy += height;
  324. rev_copy = 1;
  325. }
  326. // split the base of the framebuffer into a long-aligned address and the
  327. // index of the first bit
  328. dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
  329. dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
  330. // add offset of source and target area
  331. dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
  332. src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
  333. if (p->fbops->fb_sync)
  334. p->fbops->fb_sync(p);
  335. if (rev_copy) {
  336. while (height--) {
  337. dst_idx -= bits_per_line;
  338. src_idx -= bits_per_line;
  339. dst += dst_idx >> (ffs(bits) - 1);
  340. dst_idx &= (bytes - 1);
  341. src += src_idx >> (ffs(bits) - 1);
  342. src_idx &= (bytes - 1);
  343. bitcpy_rev(dst, dst_idx, src, src_idx, bits,
  344. width*p->var.bits_per_pixel);
  345. }
  346. } else {
  347. while (height--) {
  348. dst += dst_idx >> (ffs(bits) - 1);
  349. dst_idx &= (bytes - 1);
  350. src += src_idx >> (ffs(bits) - 1);
  351. src_idx &= (bytes - 1);
  352. bitcpy(dst, dst_idx, src, src_idx, bits,
  353. width*p->var.bits_per_pixel);
  354. dst_idx += bits_per_line;
  355. src_idx += bits_per_line;
  356. }
  357. }
  358. }
  359. EXPORT_SYMBOL(cfb_copyarea);
  360. MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
  361. MODULE_DESCRIPTION("Generic software accelerated copyarea");
  362. MODULE_LICENSE("GPL");