cfbcopyarea.c 10 KB

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