FreeCalypso > hg > freecalypso-tools
view target-utils/pirexplore/lcd.c @ 1010:1d144553a5d0
CHANGES: fc-host-tools-r21 about to be released
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Tue, 12 Dec 2023 07:45:56 +0000 |
parents | cb33d81f1386 |
children |
line wrap: on
line source
/* * Almost all of this Pirelli LCD black magic has been lifted from OsmocomBB. */ #include <sys/types.h> #include "types.h" #define GPIO_OUT_REG (*(volatile u16 *)0xFFFE4802) #define nCS4_ADDR0 (*(volatile u16 *)0x02800000) #define nCS4_ADDR2 (*(volatile u16 *)0x02800002) #define DELAY_1MS 13000 fb_spca_write(addr, data) { GPIO_OUT_REG &= 0xFF7F; nCS4_ADDR0 = addr; nCS4_ADDR2 = data; } void cmd_spca(argbulk) char *argbulk; { char *argv[3]; u_long addr, data; if (parse_args(argbulk, 2, 2, argv, 0) < 0) return; if (parse_hexarg(argv[0], 4, &addr) < 0) { printf("ERROR: arg1 must be a valid 16-bit hex value\n"); return; } if (parse_hexarg(argv[1], 4, &data) < 0) { printf("ERROR: arg2 must be a valid 16-bit hex value\n"); return; } fb_spca_write(addr, data); } void cmd_spcainit() { /* * Apparently we have to give it a reset pulse, then immediately * do the black magic register write sequence. */ GPIO_OUT_REG = 0x0000; GPIO_OUT_REG = 0x0012; /* * The non-understandable voodoo that follows has been copied from * OsmocomBB, and more recently has been updated to use true ms * delays instead of bogo-ms. OBB's original code used their * delay_ms() function, but the latter implements a delay in * bogo-milliseconds instead of true ms, with 1 bogo-ms equal to * 400 us in actual physical time. */ fb_spca_write(0x7e, 0x00); /* internal register access */ wait_ARM_cycles(DELAY_1MS * 4); /* 10 bogo-ms = 4 ms */ fb_spca_write(0x7a, 0x00); /* keep CPU in reset state */ wait_ARM_cycles(DELAY_1MS * 4); /* 10 bogo-ms = 4 ms */ fb_spca_write(0x7f, 0x00); /* select main page */ wait_ARM_cycles(DELAY_1MS * 2); /* 5 bogo-ms = 2 ms */ fb_spca_write(0x72, 0x07); /* don't reshape timing, 16 bit mode */ fb_spca_write(0x14, 0x03); fb_spca_write(0x7f, 0x00); /* select main page */ wait_ARM_cycles(DELAY_1MS * 2); /* 5 bogo-ms = 2 ms */ fb_spca_write(0x06, 0xff); fb_spca_write(0x7f, 0x09); fb_spca_write(0x19, 0x08); /* backlight: 0x08 is on, 0x0c is off */ fb_spca_write(0x23, 0x18); } enum s6b33b1x_cmdflag { CMD, DATA, END }; struct s6b33b1x_cmdlist { enum s6b33b1x_cmdflag is_cmd:8; /* 1: is a command, 0: is data, 2: end marker! */ u_char data; /* 8 bit to send to LC display */ }; static const struct s6b33b1x_cmdlist s6b33b1x_initdata[] = { { CMD, 0x26 }, /* CMD DCDC and AMP ON/OFF set */ { DATA, 0x00 }, /* DATA: everything off */ { CMD, 0x02 }, /* CMD Oscillation Mode Set */ { DATA, 0x00 }, /* DATA: oscillator off */ { CMD, 0x2c }, /* CMD Standby Mode off */ { CMD, 0x50 }, /* CMD Display off */ { CMD, 0x02 }, /* CMD Oscillation Mode Set */ { DATA, 0x01 }, /* DATA: oscillator on */ { CMD, 0x26 }, /* CMD DCDC and AMP ON/OFF set */ { DATA, 0x01 }, /* DATA: Booster 1 on */ { CMD, 0x26 }, /* CMD DCDC and AMP ON/OFF set */ { DATA, 0x09 }, /* DATA: Booster 1 on, OP-AMP on */ { CMD, 0x26 }, /* CMD DCDC and AMP ON/OFF set */ { DATA, 0x0b }, /* DATA: Booster 1 + 2 on, OP-AMP on */ { CMD, 0x26 }, /* CMD DCDC and AMP ON/OFF set */ { DATA, 0x0f }, /* DATA: Booster 1 + 2 + 3 on, OP-AMP on */ { CMD, 0x20 }, /* CMD DC-DC Select */ { DATA, 0x01 }, /* DATA: step up x1.5 */ { CMD, 0x24 }, /* CMD DCDC Clock Division Set */ { DATA, 0x0a }, /* DATA: fPCK = fOSC/6 */ { CMD, 0x2a }, /* CMD Contrast Control */ { DATA, 0x2d }, /* DATA: default contrast */ { CMD, 0x30 }, /* CMD Adressing mode set */ { DATA, 0x0b }, /* DATA: 65536 color mode */ { CMD, 0x10 }, /* CMD Driver output mode set */ { DATA, 0x03 }, /* DATA: Display duty: 1/132 */ { CMD, 0x34 }, /* CMD N-line inversion set */ { DATA, 0x88 }, /* DATA: inversion on, one frame, every 8 blocks */ { CMD, 0x40 }, /* CMD Entry mode set */ { DATA, 0x00 }, /* DATA: Y address counter mode */ { CMD, 0x28 }, /* CMD Temperature Compensation set */ { DATA, 0x01 }, /* DATA: slope -0.05%/degC */ { CMD, 0x32 }, /* CMD ROW vector mode set */ { DATA, 0x01 }, /* DATA: every 2 subgroup */ { CMD, 0x51 }, /* CMD Display on */ { END, 0x00 }, /* MARKER: end of list */ }; static void fb_s6b33b1x_send_cmdlist(p) struct s6b33b1x_cmdlist *p; { while(p->is_cmd != END) { nCS4_ADDR0 = p->data; p++; } } void cmd_lcdinit() { GPIO_OUT_REG |= 0x0080; fb_s6b33b1x_send_cmdlist(s6b33b1x_initdata); } set_lcd_addr_region(xstart, xend, ystart, yend) { GPIO_OUT_REG |= 0x0080; nCS4_ADDR0 = 0x42; nCS4_ADDR0 = ystart + 4; nCS4_ADDR0 = yend + 4; nCS4_ADDR0 = 0x43; nCS4_ADDR0 = xstart; nCS4_ADDR0 = xend; } void cmd_lcdfill(argbulk) char *argbulk; { int argc; char *argv[6]; u_long pixval; int xstart, xend, ystart, yend; int npix; if (parse_args(argbulk, 1, 5, argv, &argc) < 0) return; if (parse_hexarg(argv[0], 4, &pixval) < 0) { printf("ERROR: arg1 must be a valid 16-bit hex value\n"); return; } switch (argc) { case 1: xstart = ystart = 0; xend = yend = 127; break; case 5: xstart = atoi(argv[1]); if (xstart < 0 || xstart > 127) { range_err: printf("ERROR: coordinate arg out of range\n"); return; } xend = atoi(argv[2]); if (xend < 0 || xend > 127) goto range_err; ystart = atoi(argv[3]); if (ystart < 0 || ystart > 127) goto range_err; yend = atoi(argv[4]); if (yend < 0 || yend > 127) goto range_err; if (xend < xstart || yend < ystart) { printf("ERROR: negative range\n"); return; } break; default: printf("ERROR: wrong number of arguments\n"); return; } set_lcd_addr_region(xstart, xend, ystart, yend); npix = (xend + 1 - xstart) * (yend + 1 - ystart); while (npix--) nCS4_ADDR2 = pixval; } void cmd_lcdtest() { int i, j, k, p; /* * The result of this command should be 8 vertical bars * in the natural RGB order. */ set_lcd_addr_region(10, 89, 10, 89); for (i = 0; i < 80; i++) { for (j = 0; j < 8; j++) { p = 0; if (j & 4) p |= 0xF800; if (j & 2) p |= 0x07E0; if (j & 1) p |= 0x001F; for (k = 0; k < 10; k++) nCS4_ADDR2 = p; } } } void cmd_blit(argbulk) char *argbulk; { int argc; char *argv[6]; u16 imgbuf[16384]; size_t img_file_size; int xstart, ystart, width, height; int npix, i; if (parse_args(argbulk, 1, 5, argv, &argc) < 0) return; if (tiffs_read_into_ram(argv[0], imgbuf, 32768, &img_file_size) < 0) return; switch (argc) { case 1: xstart = ystart = 0; width = height = 128; break; case 3: xstart = ystart = 0; goto widthheight; case 5: xstart = atoi(argv[3]); if (xstart < 0 || xstart > 127) { range_err: printf("ERROR: coordinate arg out of range\n"); return; } ystart = atoi(argv[4]); if (ystart < 0 || ystart > 127) goto range_err; /* FALL THRU */ widthheight: width = atoi(argv[1]); if (width < 1 || width > 128) goto range_err; height = atoi(argv[2]); if (height < 1 || height > 128) goto range_err; if (xstart + width > 128) goto range_err; if (ystart + height > 128) goto range_err; break; default: printf("ERROR: wrong number of arguments\n"); return; } npix = width * height; if (img_file_size != npix * 2) { printf("ERROR: image file size (%u bytes) does not match WxH\n", img_file_size); return; } set_lcd_addr_region(xstart, xstart + width - 1, ystart, ystart + height - 1); /* the artwork images in Pirelli's FFS appear to be inverted */ for (i = 0; i < npix; i++) nCS4_ADDR2 = ~imgbuf[i]; }