1
0

imx_fb.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. /*
  2. * (C) Copyright 2000
  3. * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
  4. *
  5. * SPDX-License-Identifier: GPL-2.0+
  6. */
  7. #include <stdint.h>
  8. //#include <common.h>
  9. //#include <exports.h>
  10. #include <video_fb.h>
  11. //#define BF_CS1(addr,mask,value) (value? (*(addr) |= mask) : (*(addr) ^= mask))
  12. #include "io.h"
  13. #include "arch/clock.h"
  14. #include "arch/imx-regs.h"
  15. //#include "arch/sys_proto.h"
  16. //#include "asm/imx-common/dma.h"
  17. /* 1 second delay should be plenty of time for block reset. */
  18. #define RESET_MAX_TIMEOUT 1000000
  19. #define MXS_BLOCK_SFTRST (1 << 31)
  20. #define MXS_BLOCK_CLKGATE (1 << 30)
  21. #define PS2KHZ(ps) (1000000000UL / (ps))
  22. /*
  23. * The PLL frequency is 480MHz and XTAL frequency is 24MHz
  24. * iMX23: datasheet section 4.2
  25. * iMX28: datasheet section 10.2
  26. */
  27. #define PLL_FREQ_KHZ 480000
  28. #define PLL_FREQ_COEF 18
  29. #define XTAL_FREQ_KHZ 24000
  30. #define PLL_FREQ_MHZ (PLL_FREQ_KHZ / 1000)
  31. #define XTAL_FREQ_MHZ (XTAL_FREQ_KHZ / 1000)
  32. #define MXC_SSPCLK_MAX MXC_SSPCLK0
  33. /******************************************************************
  34. * Resolution Struct
  35. ******************************************************************/
  36. struct ctfb_res_modes {
  37. int xres; /* visible resolution */
  38. int yres;
  39. int refresh; /* vertical refresh rate in hz */
  40. /* Timing: All values in pixclocks, except pixclock (of course) */
  41. int pixclock; /* pixel clock in ps (pico seconds) */
  42. int pixclock_khz; /* pixel clock in kHz */
  43. int left_margin; /* time from sync to picture */
  44. int right_margin; /* time from picture to sync */
  45. int upper_margin; /* time from sync to picture */
  46. int lower_margin;
  47. int hsync_len; /* length of horizontal sync */
  48. int vsync_len; /* length of vertical sync */
  49. int sync; /* see FB_SYNC_* */
  50. int vmode; /* see FB_VMODE_* */
  51. };
  52. static void mxs_lcd_init(GraphicDevice *panel, struct ctfb_res_modes *mode, int bpp);
  53. int mxs_wait_mask_set(struct mxs_register_32 *reg, uint32_t mask, unsigned
  54. int timeout);
  55. int mxs_wait_mask_clr(struct mxs_register_32 *reg, uint32_t mask, unsigned
  56. int timeout);
  57. int mxs_reset_block(struct mxs_register_32 *reg);
  58. void mxs_set_lcdclk(uint32_t freq);
  59. void blit_string(char* str, int len, int x, int y);
  60. void *memset(void *s, int c, size_t n);
  61. #define FRAMEBUFFER (uint8_t*)0x40003e80
  62. #define FB_PITCH 640
  63. int hello_world (int argc, char * const argv[])
  64. {
  65. int i;
  66. struct ctfb_res_modes mode;
  67. /*
  68. * DENX M28EVK:
  69. * setenv videomode
  70. * video=ctfb:x:800,y:480,depth:18,mode:0,pclk:30066,
  71. * le:0,ri:256,up:0,lo:45,hs:1,vs:1,sync:100663296,vmode:0
  72. *
  73. * Freescale mx23evk/mx28evk with a Seiko 4.3'' WVGA panel:
  74. * setenv videomode
  75. * video=ctfb:x:800,y:480,depth:24,mode:0,pclk:29851,
  76. * le:89,ri:164,up:23,lo:10,hs:10,vs:10,sync:0,vmode:0
  77. */
  78. // http://tinyvga.com/vga-timing/640x480@60Hz
  79. // success!
  80. // green line: mw.b 0x40003e80 0x04 0x280
  81. // loadx 0x43000000
  82. // go 0x43000000
  83. mode.xres = 640;
  84. mode.yres = 480;
  85. mode.refresh = 31468; // seems unused
  86. mode.pixclock_khz = 25175; // unused, see picoseconds
  87. mode.hsync_len = 96;
  88. mode.vsync_len = 2;
  89. mode.pixclock = 39721; // picoseconds
  90. mode.lower_margin = 33;
  91. mode.upper_margin = 10;
  92. mode.left_margin = 48;
  93. mode.right_margin = 16;
  94. // exec !! sx /home/mntmn/code/u-boot/examples/standalone/hello_world.bin
  95. GraphicDevice gdevice; // only framebuffer is relevant here
  96. gdevice.frameAdrs = (uint32_t)(FRAMEBUFFER-0x3e80);
  97. /*
  98. PINS:
  99. CON1
  100. 28 VSYNC
  101. 29 HSYNC
  102. 4-11 DATA (RGB)
  103. */
  104. //printf ("Interim? MXS_LCDIF_BASE at %p\n", (void*)MXS_LCDIF_BASE);
  105. mxs_lcd_init(&gdevice, &mode, 8);
  106. //printf("mxs_lcd_init done!\n");
  107. //&imx_ccm->cgr3 |= (MXC_CCM_CCGR3_LCDIF1_PIX_MASK | MXC_CCM_CCGR3_DISP_AXI_MASK);
  108. //&imx_ccm->cgr2 |= (MXC_CCM_CCGR2_LCD_MASK);
  109. /*
  110. mxs_set_lcdclk(PS2KHZ(mode->pixclock));
  111. mxs_reset_block(&lcdif->hw_lcdif_ctrl_reg);
  112. //enable_lcdif_clock(&lcdif->hw_lcdif_ctrl);
  113. BF_CS1(&lcdif->hw_lcdif_ctrl, LCDIF_CTRL_DOTCLK_MODE, 1);
  114. BF_CS1(&lcdif->hw_lcdif_ctrl, LCDIF_CTRL_BYPASS_COUNT, 1);
  115. BF_CS1(&lcdif->hw_lcdif_vdctrl0, LCDIF_VDCTRL0_VSYNC_OEB, 0); //Vsync is always an output in the DOTCLK mode
  116. BF_CS1(&lcdif->hw_lcdif_vdctrl0, LCDIF_VDCTRL0_VSYNC_POL, 0);
  117. BF_CS1(&lcdif->hw_lcdif_vdctrl0, LCDIF_VDCTRL0_HSYNC_POL, 0);
  118. BF_CS1(&lcdif->hw_lcdif_vdctrl0, LCDIF_VDCTRL0_DOTCLK_POL, 0);
  119. BF_CS1(&lcdif->hw_lcdif_vdctrl0, LCDIF_VDCTRL0_ENABLE_POL, 0);
  120. BF_CS1(&lcdif->hw_lcdif_vdctrl0, LCDIF_VDCTRL0_ENABLE_PRESENT, 1);
  121. BF_CS1(&lcdif->hw_lcdif_vdctrl0, LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT, 1);
  122. BF_CS1(&lcdif->hw_lcdif_vdctrl0, LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT, 1);
  123. lcdif->hw_lcdif_vdctrl0 ^= LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_MASK;
  124. lcdif->hw_lcdif_vdctrl0 |= 2<<LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_OFFSET;
  125. lcdif->hw_lcdif_vdctrl1 ^= LCDIF_VDCTRL1_VSYNC_PERIOD_MASK;
  126. lcdif->hw_lcdif_vdctrl1 |= 280<<LCDIF_VDCTRL1_VSYNC_PERIOD_OFFSET;
  127. lcdif->hw_lcdif_vdctrl2 ^= LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH_MASK;
  128. lcdif->hw_lcdif_vdctrl2 |= 10<<LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH_OFFSET;
  129. lcdif->hw_lcdif_vdctrl2 ^= LCDIF_VDCTRL2_HSYNC_PERIOD_MASK;
  130. lcdif->hw_lcdif_vdctrl2 |= 360<<LCDIF_VDCTRL2_HSYNC_PERIOD_OFFSET;
  131. //Assuming LCD_DATABUS_WIDTH
  132. //is 24bit
  133. BF_CS1(&lcdif->hw_lcdif_vdctrl3, LCDIF_VDCTRL3_VSYNC_ONLY, 0);
  134. lcdif->hw_lcdif_vdctrl3 ^= LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT_MASK;
  135. lcdif->hw_lcdif_vdctrl3 |= 20<<LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT_OFFSET;
  136. lcdif->hw_lcdif_vdctrl3 ^= LCDIF_VDCTRL3_VERTICAL_WAIT_CNT_MASK;
  137. lcdif->hw_lcdif_vdctrl3 |= 20<<LCDIF_VDCTRL3_VERTICAL_WAIT_CNT_OFFSET;
  138. lcdif->hw_lcdif_vdctrl4 ^= LCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT_MASK;
  139. lcdif->hw_lcdif_vdctrl4 |= 320<<LCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT_OFFSET;
  140. //Note that DOTCLK_V_VALID_DATA_CNT is
  141. //implicitly assumed to be HW_LCDIF_TRANSFER_COUNT_V_COUNT
  142. BF_CS1(&lcdif->hw_lcdif_vdctrl4, LCDIF_VDCTRL4_SYNC_SIGNALS_ON, 1);
  143. BF_CS1(&lcdif->hw_lcdif_ctrl, LCDIF_CTRL_RUN, 1);
  144. // HW_LCDIF_CUR_BUF*/
  145. //printf("Enter + to exit ... \n");
  146. //memset(FRAMEBUFFER, 0, 640*480);
  147. int cursor_x=0;
  148. int cursor_y=32;
  149. char* greeting = "Welcome to Interim/IMX 0.0.1!";
  150. blit_string(greeting,29,0,16);
  151. while (true) {
  152. while (!tstc());
  153. /* consume input */
  154. char c = getc();
  155. blit_string(&c,1,cursor_x,cursor_y);
  156. cursor_x+=8;
  157. if (cursor_x>=640) {
  158. cursor_x=0;
  159. cursor_y+=8;
  160. }
  161. if (c==10 || c==13) {
  162. cursor_x=0;
  163. cursor_y+=8;
  164. }
  165. if (c=='+') {
  166. //printf ("\n\n");
  167. return(0);
  168. }
  169. }
  170. return (0);
  171. }
  172. static void mxs_lcd_init(GraphicDevice *panel,
  173. struct ctfb_res_modes *mode, int bpp)
  174. {
  175. struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE;
  176. uint32_t word_len = 0, bus_width = 0;
  177. uint8_t valid_data = 0;
  178. /* Kick in the LCDIF clock */
  179. mxs_set_lcdclk(mode->pixclock_khz); //PS2KHZ(mode->pixclock));
  180. /* Restart the LCDIF block */
  181. mxs_reset_block(&regs->hw_lcdif_ctrl_reg);
  182. struct mxs_pinctrl_regs *pin = (struct mxs_pinctrl_regs*)MXS_PINCTRL_BASE;
  183. // switch lcd hsync, vsync pins on
  184. pin->hw_pinctrl_muxsel3 ^= ((1<<16)|(1<<17)|(1<<18)|(1<<19));
  185. pin->hw_pinctrl_muxsel3 ^= ((1<<12)|(1<<13)|(1<<14)|(1<<15));
  186. // rgb pins (lcd data)
  187. pin->hw_pinctrl_muxsel2 ^= ((1<<0)|(1<<1)|(1<<2)|(1<<3));
  188. pin->hw_pinctrl_muxsel2 ^= ((1<<4)|(1<<5)|(1<<6)|(1<<7));
  189. pin->hw_pinctrl_muxsel2 ^= ((1<<8)|(1<<9)|(1<<10)|(1<<11));
  190. pin->hw_pinctrl_muxsel2 ^= ((1<<12)|(1<<13)|(1<<14)|(1<<15));
  191. switch (bpp) {
  192. case 24:
  193. word_len = LCDIF_CTRL_WORD_LENGTH_24BIT;
  194. bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_24BIT;
  195. valid_data = 0x7;
  196. break;
  197. case 18:
  198. word_len = LCDIF_CTRL_WORD_LENGTH_24BIT;
  199. bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_18BIT;
  200. valid_data = 0x7;
  201. break;
  202. case 16:
  203. word_len = LCDIF_CTRL_WORD_LENGTH_16BIT;
  204. bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_16BIT;
  205. valid_data = 0xf;
  206. break;
  207. case 8:
  208. word_len = LCDIF_CTRL_WORD_LENGTH_8BIT;
  209. bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_8BIT;
  210. valid_data = 0xf;
  211. break;
  212. }
  213. writel(bus_width | word_len | LCDIF_CTRL_DOTCLK_MODE |
  214. LCDIF_CTRL_BYPASS_COUNT | LCDIF_CTRL_LCDIF_MASTER,
  215. &regs->hw_lcdif_ctrl);
  216. writel(valid_data << LCDIF_CTRL1_BYTE_PACKING_FORMAT_OFFSET,
  217. &regs->hw_lcdif_ctrl1);
  218. //mxsfb_system_setup();
  219. writel((mode->yres << LCDIF_TRANSFER_COUNT_V_COUNT_OFFSET) | mode->xres,
  220. &regs->hw_lcdif_transfer_count);
  221. writel(LCDIF_VDCTRL0_ENABLE_PRESENT | LCDIF_VDCTRL0_ENABLE_POL |
  222. LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT |
  223. LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |
  224. mode->vsync_len, &regs->hw_lcdif_vdctrl0);
  225. writel(mode->upper_margin + mode->lower_margin +
  226. mode->vsync_len + mode->yres,
  227. &regs->hw_lcdif_vdctrl1);
  228. writel((mode->hsync_len << LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH_OFFSET) |
  229. (mode->left_margin + mode->right_margin +
  230. mode->hsync_len + mode->xres),
  231. &regs->hw_lcdif_vdctrl2);
  232. writel(((mode->left_margin + mode->hsync_len) <<
  233. LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT_OFFSET) |
  234. (mode->upper_margin + mode->vsync_len) | LCDIF_VDCTRL3_MUX_SYNC_SIGNALS,
  235. &regs->hw_lcdif_vdctrl3);
  236. writel((0 << LCDIF_VDCTRL4_DOTCLK_DLY_SEL_OFFSET) | mode->xres,
  237. &regs->hw_lcdif_vdctrl4);
  238. writel(panel->frameAdrs, &regs->hw_lcdif_cur_buf);
  239. writel(panel->frameAdrs, &regs->hw_lcdif_next_buf);
  240. /* Flush FIFO first */
  241. writel(LCDIF_CTRL1_FIFO_CLEAR, &regs->hw_lcdif_ctrl1_set);
  242. /* Sync signals ON */
  243. setbits_le32(&regs->hw_lcdif_vdctrl4, LCDIF_VDCTRL4_SYNC_SIGNALS_ON);
  244. /* FIFO cleared */
  245. writel(LCDIF_CTRL1_FIFO_CLEAR, &regs->hw_lcdif_ctrl1_clr);
  246. /* RUN! */
  247. writel(LCDIF_CTRL_RUN, &regs->hw_lcdif_ctrl_set);
  248. }
  249. void mxs_set_lcdclk(uint32_t freq)
  250. {
  251. struct mxs_clkctrl_regs *clkctrl_regs =
  252. (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
  253. uint32_t fp, x, k_rest, k_best, x_best, tk;
  254. int32_t k_best_l = 999, k_best_t = 0, x_best_l = 0xff, x_best_t = 0xff;
  255. if (freq == 0)
  256. return;
  257. #if defined(CONFIG_MX23)
  258. writel(CLKCTRL_CLKSEQ_BYPASS_PIX, &clkctrl_regs->hw_clkctrl_clkseq_clr);
  259. #elif defined(CONFIG_MX28)
  260. writel(CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF, &clkctrl_regs->hw_clkctrl_clkseq_clr);
  261. #endif
  262. /*
  263. * / 18 \ 1 1
  264. * freq kHz = | 480000000 Hz * -- | * --- * ------
  265. * \ x / k 1000
  266. *
  267. * 480000000 Hz 18
  268. * ------------ * --
  269. * freq kHz x
  270. * k = -------------------
  271. * 1000
  272. */
  273. fp = ((PLL_FREQ_KHZ * 1000) / freq) * 18;
  274. for (x = 18; x <= 35; x++) {
  275. tk = fp / x;
  276. if ((tk / 1000 == 0) || (tk / 1000 > 255))
  277. continue;
  278. k_rest = tk % 1000;
  279. if (k_rest < (k_best_l % 1000)) {
  280. k_best_l = tk;
  281. x_best_l = x;
  282. }
  283. if (k_rest > (k_best_t % 1000)) {
  284. k_best_t = tk;
  285. x_best_t = x;
  286. }
  287. }
  288. if (1000 - (k_best_t % 1000) > (k_best_l % 1000)) {
  289. k_best = k_best_l;
  290. x_best = x_best_l;
  291. } else {
  292. k_best = k_best_t;
  293. x_best = x_best_t;
  294. }
  295. k_best /= 1000;
  296. #if defined(CONFIG_MX23)
  297. writeb(CLKCTRL_FRAC_CLKGATE,
  298. &clkctrl_regs->hw_clkctrl_frac0_set[CLKCTRL_FRAC0_PIX]);
  299. writeb(CLKCTRL_FRAC_CLKGATE | (x_best & CLKCTRL_FRAC_FRAC_MASK),
  300. &clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_PIX]);
  301. writeb(CLKCTRL_FRAC_CLKGATE,
  302. &clkctrl_regs->hw_clkctrl_frac0_clr[CLKCTRL_FRAC0_PIX]);
  303. writel(CLKCTRL_PIX_CLKGATE,
  304. &clkctrl_regs->hw_clkctrl_pix_set);
  305. clrsetbits_le32(&clkctrl_regs->hw_clkctrl_pix,
  306. CLKCTRL_PIX_DIV_MASK | CLKCTRL_PIX_CLKGATE,
  307. k_best << CLKCTRL_PIX_DIV_OFFSET);
  308. while (readl(&clkctrl_regs->hw_clkctrl_pix) & CLKCTRL_PIX_BUSY)
  309. ;
  310. #elif defined(CONFIG_MX28)
  311. writeb(CLKCTRL_FRAC_CLKGATE,
  312. &clkctrl_regs->hw_clkctrl_frac1_set[CLKCTRL_FRAC1_PIX]);
  313. writeb(CLKCTRL_FRAC_CLKGATE | (x_best & CLKCTRL_FRAC_FRAC_MASK),
  314. &clkctrl_regs->hw_clkctrl_frac1[CLKCTRL_FRAC1_PIX]);
  315. writeb(CLKCTRL_FRAC_CLKGATE,
  316. &clkctrl_regs->hw_clkctrl_frac1_clr[CLKCTRL_FRAC1_PIX]);
  317. writel(CLKCTRL_DIS_LCDIF_CLKGATE,
  318. &clkctrl_regs->hw_clkctrl_lcdif_set);
  319. clrsetbits_le32(&clkctrl_regs->hw_clkctrl_lcdif,
  320. CLKCTRL_DIS_LCDIF_DIV_MASK | CLKCTRL_DIS_LCDIF_CLKGATE,
  321. k_best << CLKCTRL_DIS_LCDIF_DIV_OFFSET);
  322. while (readl(&clkctrl_regs->hw_clkctrl_lcdif) & CLKCTRL_DIS_LCDIF_BUSY)
  323. ;
  324. #endif
  325. }
  326. int mxs_wait_mask_set(struct mxs_register_32 *reg, uint32_t mask, unsigned
  327. int timeout)
  328. {
  329. while (--timeout) {
  330. if ((readl(&reg->reg) & mask) == mask)
  331. break;
  332. udelay(1);
  333. }
  334. return !timeout;
  335. }
  336. int mxs_wait_mask_clr(struct mxs_register_32 *reg, uint32_t mask, unsigned
  337. int timeout)
  338. {
  339. while (--timeout) {
  340. if ((readl(&reg->reg) & mask) == 0)
  341. break;
  342. udelay(1);
  343. }
  344. return !timeout;
  345. }
  346. int mxs_reset_block(struct mxs_register_32 *reg)
  347. {
  348. /* Clear SFTRST */
  349. writel(MXS_BLOCK_SFTRST, &reg->reg_clr);
  350. if (mxs_wait_mask_clr(reg, MXS_BLOCK_SFTRST, RESET_MAX_TIMEOUT))
  351. return 1;
  352. /* Clear CLKGATE */
  353. writel(MXS_BLOCK_CLKGATE, &reg->reg_clr);
  354. /* Set SFTRST */
  355. writel(MXS_BLOCK_SFTRST, &reg->reg_set);
  356. /* Wait for CLKGATE being set */
  357. if (mxs_wait_mask_set(reg, MXS_BLOCK_CLKGATE, RESET_MAX_TIMEOUT))
  358. return 1;
  359. /* Clear SFTRST */
  360. writel(MXS_BLOCK_SFTRST, &reg->reg_clr);
  361. if (mxs_wait_mask_clr(reg, MXS_BLOCK_SFTRST, RESET_MAX_TIMEOUT))
  362. return 1;
  363. /* Clear CLKGATE */
  364. writel(MXS_BLOCK_CLKGATE, &reg->reg_clr);
  365. if (mxs_wait_mask_clr(reg, MXS_BLOCK_CLKGATE, RESET_MAX_TIMEOUT))
  366. return 1;
  367. return 0;
  368. }
  369. void *memset(void *s, int c, size_t n)
  370. {
  371. unsigned char* p=s;
  372. while(n--)
  373. *p++ = (unsigned char)c;
  374. return s;
  375. }
  376. #include "font_c64.h"
  377. #define FONT FONT_C64
  378. void blit_string(char* str, int len, int x, int y) {
  379. uint8_t* dst = FRAMEBUFFER+y*FB_PITCH+x;
  380. int i = 0, line = 0;
  381. for (i=0; i<len; i++) {
  382. int offset = ((int)str[i]) * 8;
  383. for (line=0; line<8; line++) {
  384. uint8_t pixels = FONT[offset+line];
  385. *dst++ = (pixels&128)>0?0xff:0x00;
  386. *dst++ = (pixels&64)>0?0xff:0x00;
  387. *dst++ = (pixels&32)>0?0xff:0x00;
  388. *dst++ = (pixels&16)>0?0xff:0x00;
  389. *dst++ = (pixels&8)>0?0xff:0x00;
  390. *dst++ = (pixels&4)>0?0xff:0x00;
  391. *dst++ = (pixels&2)>0?0xff:0x00;
  392. *dst = (pixels&1)>0?0xff:0x00;
  393. dst+=FB_PITCH-7;
  394. }
  395. dst-=(FB_PITCH*8);
  396. dst+=8;
  397. }
  398. }