/*********************************************************************************** * I.MX6 U-boot lvds display hacking * 声明: * 本文主要是为了跟踪I.MX6中的U-boot中显示部分代码,查看是否支持24bit显示。 * * 2015-10-8 晴 深圳 南山平山村 曾剑锋 **********************************************************************************/ cat cpu/arm_cortexa8/start.S .globl _start _start: b reset ---------------------------+ ldr pc, _undefined_instruction | ldr pc, _software_interrupt | ldr pc, _prefetch_abort | ldr pc, _data_abort | ldr pc, _not_used | ldr pc, _irq | ldr pc, _fiq | | _undefined_instruction: .word undefined_instruction | _software_interrupt: .word software_interrupt | _prefetch_abort: .word prefetch_abort | _data_abort: .word data_abort | _not_used: .word not_used | _irq: .word irq | _fiq: .word fiq | | cat cpu/arm_cortexa8/start.S | /* | * the actual reset code | */ | | reset: <--------------------------+ /* * set the cpu to SVC32 mode */ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr,r0 #if (CONFIG_OMAP34XX) /* Copy vectors to mask ROM indirect addr */ adr r0, _start @ r0 <- current position of code add r0, r0, #4 @ skip reset vector mov r2, #64 @ r2 <- size to copy add r2, r0, r2 @ r2 <- source end address mov r1, #SRAM_OFFSET0 @ build vect addr mov r3, #SRAM_OFFSET1 add r1, r1, r3 mov r3, #SRAM_OFFSET2 add r1, r1, r3 next: ldmia r0!, {r3 - r10} @ copy from source address [r0] stmia r1!, {r3 - r10} @ copy to target address [r1] cmp r0, r2 @ until source end address [r2] bne next @ loop until equal */ #if !defined(CONFIG_SYS_NAND_BOOT) && !defined(CONFIG_SYS_ONENAND_BOOT) /* No need to copy/exec the clock code - DPLL adjust already done * in NAND/oneNAND Boot. */ bl cpy_clk_code @ put dpll adjust code behind vectors #endif /* NAND Boot */ #endif /* the mask ROM code should have PLL and others stable */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit #endif #ifndef CONFIG_SKIP_RELOCATE_UBOOT relocate: @ relocate U-Boot to RAM adr r0, _start @ r0 <- current position of code ldr r1, _TEXT_BASE @ test if we run from flash or RAM cmp r0, r1 @ don't reloc during debug beq stack_setup ldr r2, _armboot_start ldr r3, _bss_start sub r2, r3, r2 @ r2 <- size of armboot add r2, r0, r2 @ r2 <- source end address copy_loop: @ copy 32 bytes at a time ldmia r0!, {r3 - r10} @ copy from source address [r0] stmia r1!, {r3 - r10} @ copy to target address [r1] cmp r0, r2 @ until source end addreee [r2] ble copy_loop #endif /* CONFIG_SKIP_RELOCATE_UBOOT */ stack_setup: ldr r0, _TEXT_BASE @ upper 128 KiB: relocated uboot sub r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE @ bdinfo #ifdef CONFIG_USE_IRQ sub r0, r0, #(CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ) #endif sub sp, r0, #12 @ leave 3 words for abort-stack and sp, sp, #~7 @ 8 byte alinged for (ldr/str)d /* Clear BSS (if any). Is below tx (watch load addr - need space) */ clear_bss: ldr r0, _bss_start @ find start of bss segment ldr r1, _bss_end @ stop here mov r2, #0x00000000 @ clear value clbss_l: str r2, [r0] @ clear BSS location cmp r0, r1 @ are we at the end yet add r0, r0, #4 @ increment clear index pointer bne clbss_l @ keep clearing till at end #ifdef CONFIG_ARCH_MMU bl board_mmu_init #endif ldr pc, _start_armboot @ jump to C code -------+ | _start_armboot: .word start_armboot ---------+ <------+ | cat lib_arm/board.c | void start_armboot (void) <--------+ { init_fnc_t **init_fnc_ptr; char *s; #if defined(CONFIG_VFD) || defined(CONFIG_LCD) unsigned long addr; #endif /* Pointer is writable since we allocated a register for it */ gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)); /* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__("": : :"memory"); memset ((void*)gd, 0, sizeof (gd_t)); gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); memset (gd->bd, 0, sizeof (bd_t)); gd->flags |= GD_FLG_RELOC; monitor_flash_len = _bss_start - _armboot_start; for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } } /* armboot_start is defined in the board-specific linker script */ mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN); #ifndef CONFIG_SYS_NO_FLASH /* configure available FLASH banks */ display_flash_config (flash_init ()); #endif /* CONFIG_SYS_NO_FLASH */ #ifdef CONFIG_VFD #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif /* * reserve memory for VFD display (always full pages) */ /* bss_end is defined in the board-specific linker script */ addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); vfd_setmem (addr); gd->fb_base = addr; #endif /* CONFIG_VFD */ #ifdef CONFIG_LCD /* board init may have inited fb_base */ if (!gd->fb_base) { #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif /* * reserve memory for LCD display (always full pages) */ /* bss_end is defined in the board-specific linker script */ addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); lcd_setmem (addr); gd->fb_base = addr; } #endif /* CONFIG_LCD */ #if defined(CONFIG_CMD_NAND) puts ("NAND: "); nand_init(); /* go init the NAND */ #endif #if defined(CONFIG_CMD_ONENAND) onenand_init(); #endif #ifdef CONFIG_HAS_DATAFLASH AT91F_DataflashInit(); dataflash_print_info(); #endif #ifdef CONFIG_GENERIC_MMC puts ("MMC: "); mmc_initialize (gd->bd); #endif /* initialize environment */ env_relocate (); #ifdef CONFIG_VFD /* must do this after the framebuffer is allocated */ drv_vfd_init(); #endif /* CONFIG_VFD */ #ifdef CONFIG_SERIAL_MULTI serial_initialize(); #endif /* IP Address */ gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); #if defined CONFIG_SPLASH_SCREEN && defined CONFIG_VIDEO_MX5 setup_splash_image(); #endif stdio_init (); /* get the devices list going. */ -------------------+ | jumptable_init (); | | #if defined(CONFIG_API) | /* Initialize API */ | api_init (); | #endif | | console_init_r (); /* fully init console as a device */ | | #if defined(CONFIG_ARCH_MISC_INIT) | /* miscellaneous arch dependent initialisations */ | arch_misc_init (); | #endif | #if defined(CONFIG_MISC_INIT_R) | /* miscellaneous platform dependent initialisations */ | misc_init_r (); | #endif | | /* enable exceptions */ | enable_interrupts (); | | /* Perform network card initialisation if necessary */ | #ifdef CONFIG_DRIVER_TI_EMAC | /* XXX: this needs to be moved to board init */ | extern void davinci_eth_set_mac_addr (const u_int8_t *addr); | if (getenv ("ethaddr")) { | uchar enetaddr[6]; | eth_getenv_enetaddr("ethaddr", enetaddr); | davinci_eth_set_mac_addr(enetaddr); | } | #endif | | #ifdef CONFIG_DRIVER_CS8900 | /* XXX: this needs to be moved to board init */ | cs8900_get_enetaddr (); | #endif | | #if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96) | /* XXX: this needs to be moved to board init */ | if (getenv ("ethaddr")) { | uchar enetaddr[6]; | eth_getenv_enetaddr("ethaddr", enetaddr); | smc_set_mac_addr(enetaddr); | } | #endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */ | | #if defined(CONFIG_ENC28J60_ETH) && !defined(CONFIG_ETHADDR) | extern void enc_set_mac_addr (void); | enc_set_mac_addr (); | #endif /* CONFIG_ENC28J60_ETH && !CONFIG_ETHADDR*/ | | /* Initialize from environment */ | if ((s = getenv ("loadaddr")) != NULL) { | load_addr = simple_strtoul (s, NULL, 16); | } | #if defined(CONFIG_CMD_NET) | if ((s = getenv ("bootfile")) != NULL) { | copy_filename (BootFile, s, sizeof (BootFile)); | } | #endif | | #ifdef BOARD_LATE_INIT | board_late_init (); | #endif | | #ifdef CONFIG_ANDROID_RECOVERY | check_recovery_mode(); | #endif | | #if defined(CONFIG_CMD_NET) | #if defined(CONFIG_NET_MULTI) | puts ("Net: "); | #endif | eth_initialize(gd->bd); | #if defined(CONFIG_RESET_PHY_R) | debug ("Reset Ethernet PHY "); | reset_phy(); | #endif | #endif | #ifdef CONFIG_FASTBOOT | check_fastboot_mode(); | #endif | /* main_loop() can return to retry autoboot, if so just run it again. */ | for (;;) { | main_loop (); | } | | /* NOTREACHED - no way out of command loop except booting */ | } | | cat common/stdio.c | int stdio_init (void) <-----------------------------------+ { #ifndef CONFIG_ARM /* already relocated for current ARM implementation */ ulong relocation_offset = gd->reloc_off; int i; /* relocate device name pointers */ for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) { stdio_names[i] = (char *) (((ulong) stdio_names[i]) + relocation_offset); } #endif /* Initialize the list */ INIT_LIST_HEAD(&(devs.list)); #ifdef CONFIG_ARM_DCC_MULTI drv_arm_dcc_init (); #endif #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); #endif #ifdef CONFIG_LCD drv_lcd_init (); ----------------------------------+ #endif | #if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) | drv_video_init (); | #endif | #ifdef CONFIG_KEYBOARD | drv_keyboard_init (); | #endif | #ifdef CONFIG_LOGBUFFER | drv_logbuff_init (); | #endif | drv_system_init (); | #ifdef CONFIG_SERIAL_MULTI | serial_stdio_init (); | #endif | #ifdef CONFIG_USB_TTY | drv_usbtty_init (); | #endif | #ifdef CONFIG_NETCONSOLE | drv_nc_init (); | #endif | #ifdef CONFIG_JTAG_CONSOLE | drv_jtag_console_init (); | #endif | | return (0); | } | | cat common/stdio.c | int drv_lcd_init (void) <-------------------------------+ { struct stdio_dev lcddev; int rc; lcd_base = (void *)(gd->fb_base); lcd_line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8; lcd_init (lcd_base); /* LCD initialization */ ----------+ | /* Device initialization */ | memset (&lcddev, 0, sizeof (lcddev)); | | strcpy (lcddev.name, "lcd"); | lcddev.ext = 0; /* No extensions */ | lcddev.flags = DEV_FLAGS_OUTPUT; /* Output only */ | lcddev.putc = lcd_putc; /* 'putc' function */ | lcddev.puts = lcd_puts; /* 'puts' function */ | | rc = stdio_register (&lcddev); | | return (rc == 0) ? 1 : rc; | } | | cat common/lcd.c | static int lcd_init (void *lcdbase) <-------------------+ { /* Initialize the lcd controller */ debug ("[LCD] Initializing LCD frambuffer at %p ", lcdbase); lcd_ctrl_init (lcdbase); lcd_is_enabled = 1; lcd_clear (NULL, 1, 1, NULL); /* dummy args */ -------------+ lcd_enable (); -------------*---------------+ | | /* Initialize the console */ | | console_col = 0; | | #ifdef CONFIG_LCD_INFO_BELOW_LOGO | | console_row = 7 + BMP_LOGO_HEIGHT / VIDEO_FONT_HEIGHT; | | #else | | console_row = 1; /* leave 1 blank line below logo */ | | #endif | | | | return 0; | | } +-------------------------------------------------+ | | | cat common/lcd.c V | static int lcd_clear (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) | { | #if LCD_BPP == LCD_MONOCHROME | /* Setting the palette */ | lcd_initcolregs(); | | #elif LCD_BPP == LCD_COLOR8 | /* Setting the palette */ | lcd_setcolreg (CONSOLE_COLOR_BLACK, 0, 0, 0); | lcd_setcolreg (CONSOLE_COLOR_RED, 0xFF, 0, 0); | lcd_setcolreg (CONSOLE_COLOR_GREEN, 0, 0xFF, 0); | lcd_setcolreg (CONSOLE_COLOR_YELLOW, 0xFF, 0xFF, 0); | lcd_setcolreg (CONSOLE_COLOR_BLUE, 0, 0, 0xFF); | lcd_setcolreg (CONSOLE_COLOR_MAGENTA, 0xFF, 0, 0xFF); | lcd_setcolreg (CONSOLE_COLOR_CYAN, 0, 0xFF, 0xFF); | lcd_setcolreg (CONSOLE_COLOR_GREY, 0xAA, 0xAA, 0xAA); | lcd_setcolreg (CONSOLE_COLOR_WHITE, 0xFF, 0xFF, 0xFF); | #endif | | #ifndef CONFIG_SYS_WHITE_ON_BLACK | lcd_setfgcolor (CONSOLE_COLOR_BLACK); | lcd_setbgcolor (CONSOLE_COLOR_WHITE); | #else | lcd_setfgcolor (CONSOLE_COLOR_WHITE); | lcd_setbgcolor (CONSOLE_COLOR_BLACK); | #endif /* CONFIG_SYS_WHITE_ON_BLACK */ | | #ifdef LCD_TEST_PATTERN | test_pattern(); | #else | /* set framebuffer to background color */ | memset ((char *)lcd_base, | COLOR_MASK(lcd_getbgcolor()), | lcd_line_length*panel_info.vl_row); | #endif | /* Paint the logo and retrieve LCD base address */ | debug ("[LCD] Drawing the logo... "); | lcd_console_address = lcd_logo (); --------+ | | | console_col = 0; | | console_row = 0; | | | | return (0); | | } | | | | cat common/lcd.c | | static void *lcd_logo (void) <-----------+ | { | #ifdef CONFIG_SPLASH_SCREEN | char *s; | ulong addr; | static int do_splash = 1; | | // get the "splashimage=0x30000000 " | if (do_splash && (s = getenv("splashimage")) != NULL) { | int x = 0, y = 0; | do_splash = 0; | | // get the image address | addr = simple_strtoul (s, NULL, 16); | | #ifdef CONFIG_SPLASH_SCREEN_ALIGN | // get the "splashpos=m,m " | if ((s = getenv ("splashpos")) != NULL) { | // the first position x and y, default was center | if (s[0] == 'm') | x = BMP_ALIGN_CENTER; | else | x = simple_strtol (s, NULL, 0); | | if ((s = strchr (s + 1, ',')) != NULL) { | if (s[1] == 'm') | y = BMP_ALIGN_CENTER; | else | y = simple_strtol (s + 1, NULL, 0); | } | } | #endif /* CONFIG_SPLASH_SCREEN_ALIGN */ | | #ifdef CONFIG_VIDEO_BMP_GZIP | // get the image struct | bmp_image_t *bmp = (bmp_image_t *)addr; | unsigned long len; | | if (!((bmp->header.signature[0]=='B') && | (bmp->header.signature[1]=='M'))) { | addr = (ulong)gunzip_bmp(addr, &len); | } | #endif | /** | * We currently offer uboot boot picture size is 800*480, | * so the picture do not need to be offset , | * just let x and y equal to zero that drawing picture at top left corner | * write by zengjf 2015/4/8 | */ | x = 0; | y = 0; | | | if (lcd_display_bitmap (addr, x, y) == 0) { -------------------+ | return ((void *)lcd_base); | | } | | } | | #endif /* CONFIG_SPLASH_SCREEN */ | | | | #ifdef CONFIG_LCD_LOGO | | bitmap_plot (0, 0); | | #endif /* CONFIG_LCD_LOGO */ | | | | #ifdef CONFIG_LCD_INFO | | console_col = LCD_INFO_X / VIDEO_FONT_WIDTH; | | console_row = LCD_INFO_Y / VIDEO_FONT_HEIGHT; | | lcd_show_board_info(); | | #endif /* CONFIG_LCD_INFO */ | | | | #if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO) | | return ((void *)((ulong)lcd_base + BMP_LOGO_HEIGHT * lcd_line_length)); | | #else | | return ((void *)lcd_base); | | #endif /* CONFIG_LCD_LOGO && !CONFIG_LCD_INFO_BELOW_LOGO */ | | } | | | | cat common/lcd.c | | int lcd_display_bitmap(ulong bmp_image, int x, int y) <------------------+ | { | #if !defined(CONFIG_MCC200) | ushort *cmap = NULL; | #endif | ushort *cmap_base = NULL; | ushort i, j; | uchar *fb; | bmp_image_t *bmp=(bmp_image_t *)bmp_image; | uchar *bmap; | ushort padded_line; | unsigned long width, height, byte_width; | unsigned long pwidth = panel_info.vl_col; | unsigned colors, bpix, bmp_bpix; | unsigned long compression; | #if defined(CONFIG_PXA250) | struct pxafb_info *fbi = &panel_info.pxa; | #elif defined(CONFIG_MPC823) | volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; | volatile cpm8xx_t *cp = &(immr->im_cpm); | #endif | | if (!((bmp->header.signature[0]=='B') && | (bmp->header.signature[1]=='M'))) { | printf ("Error: no valid bmp image at %lx ", bmp_image); | return 1; | } | | width = le32_to_cpu (bmp->header.width); | height = le32_to_cpu (bmp->header.height); | bmp_bpix = le16_to_cpu(bmp->header.bit_count); | colors = 1 << bmp_bpix; | compression = le32_to_cpu (bmp->header.compression); | | bpix = NBITS(panel_info.vl_bpix); | | if ((bpix != 1) && (bpix != 8) && (bpix != 16) && (bpix != 24)) { | printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel ", | bpix, bmp_bpix); | return 1; | } | | #if defined(CONFIG_BMP_24BPP) | /* We support displaying 24bpp BMPs on 16bpp LCDs */ | if (bpix != bmp_bpix && (bmp_bpix != 24 || bpix != 16) && | (bmp_bpix != 8 || bpix != 16)) { | #else | /* We support displaying 8bpp BMPs on 16bpp LCDs */ | if (bpix != bmp_bpix && (bmp_bpix != 8 || bpix != 16)) { | #endif | printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel ", | bpix, | le16_to_cpu(bmp->header.bit_count)); | return 1; | } | | debug ("Display-bmp: %d x %d with %d colors ", | (int)width, (int)height, (int)colors); | | #if !defined(CONFIG_MCC200) | /* MCC200 LCD doesn't need CMAP, supports 1bpp b&w only */ | if (bmp_bpix == 8) { | #if defined(CONFIG_PXA250) | cmap = (ushort *)fbi->palette; | #elif defined(CONFIG_MPC823) | cmap = (ushort *)&(cp->lcd_cmap[255*sizeof(ushort)]); | #elif !defined(CONFIG_ATMEL_LCD) | cmap = panel_info.cmap; | #endif | cmap_base = cmap; | | /* Set color map */ | for (i=0; i<colors; ++i) { | bmp_color_table_entry_t cte = bmp->color_table[i]; | #if !defined(CONFIG_ATMEL_LCD) | ushort colreg = | ( ((cte.red) << 8) & 0xf800) | | ( ((cte.green) << 3) & 0x07e0) | | ( ((cte.blue) >> 3) & 0x001f) ; | #ifdef CONFIG_SYS_INVERT_COLORS | *cmap = 0xffff - colreg; | #else | *cmap = colreg; | #endif | #if defined(CONFIG_MPC823) | cmap--; | #else | cmap++; | #endif | #else /* CONFIG_ATMEL_LCD */ | lcd_setcolreg(i, cte.red, cte.green, cte.blue); | #endif | } | } | #endif | | /* | * BMP format for Monochrome assumes that the state of a | * pixel is described on a per Bit basis, not per Byte. | * So, in case of Monochrome BMP we should align widths | * on a byte boundary and convert them from Bit to Byte | * units. | * Probably, PXA250 and MPC823 process 1bpp BMP images in | * their own ways, so make the converting to be MCC200 | * specific. | */ | #if defined(CONFIG_MCC200) | if (bpix==1) | { | width = ((width + 7) & ~7) >> 3; | x = ((x + 7) & ~7) >> 3; | pwidth= ((pwidth + 7) & ~7) >> 3; | } | #endif | padded_line = (width&0x3) ? ((width&~0x3)+4) : (width); | #ifdef CONFIG_SPLASH_SCREEN_ALIGN | if (x == BMP_ALIGN_CENTER) | x = max(0, (pwidth - width) / 2); | else if (x < 0) | x = max(0, pwidth - width + x + 1); | | if (y == BMP_ALIGN_CENTER) | y = max(0, (panel_info.vl_row - height) / 2); | else if (y < 0) | y = max(0, panel_info.vl_row - height + y + 1); | #endif /* CONFIG_SPLASH_SCREEN_ALIGN */ | | if ((x + width)>pwidth) | width = pwidth - x; | if ((y + height)>panel_info.vl_row) | height = panel_info.vl_row - y; | | bmap = (uchar *)bmp + le32_to_cpu (bmp->header.data_offset); | fb = (uchar *) (lcd_base + | (y + height - 1) * lcd_line_length + x * bpix / 8); | | switch (bmp_bpix) { | case 1: /* pass through */ | case 8: | if (bpix != 16) | byte_width = width; | else | byte_width = width * 2; | | for (i = 0; i < height; ++i) { | WATCHDOG_RESET(); | for (j = 0; j < width; j++) { | if (bpix != 16) { | #if defined(CONFIG_PXA250) || defined(CONFIG_ATMEL_LCD) | *(fb++) = *(bmap++); | #elif defined(CONFIG_MPC823) || defined(CONFIG_MCC200) | *(fb++) = 255 - *(bmap++); | #endif | } else { | *(uint16_t *)fb = cmap_base[*(bmap++)]; | fb += sizeof(uint16_t) / sizeof(*fb); | } | } | bmap += (width - padded_line); | fb -= (byte_width + lcd_line_length); | } | break; | | #if defined(CONFIG_BMP_16BPP) | case 16: | for (i = 0; i < height; ++i) { | WATCHDOG_RESET(); | for (j = 0; j < width; j++) { | #if defined(CONFIG_ATMEL_LCD_BGR555) | *(fb++) = ((bmap[0] & 0x1f) << 2) | | (bmap[1] & 0x03); | *(fb++) = (bmap[0] & 0xe0) | | ((bmap[1] & 0x7c) >> 2); | bmap += 2; | #else | *(fb++) = *(bmap++); | *(fb++) = *(bmap++); | #endif | } | bmap += (padded_line - width) * 2; | fb -= (width * 2 + lcd_line_length); | } | break; | #endif /* CONFIG_BMP_16BPP */ | #if defined(CONFIG_BMP_24BPP) | case 24: | if (bpix != 16) { | printf("Error: %d bit/pixel mode," | "but BMP has %d bit/pixel ", | bpix, bmp_bpix); | break; | } | for (i = 0; i < height; ++i) { | WATCHDOG_RESET(); | for (j = 0; j < width; j++) { | *(uint16_t *)fb = ((*(bmap + 2) << 8) & 0xf800) | | ((*(bmap + 1) << 3) & 0x07e0) | | ((*(bmap) >> 3) & 0x001f); | bmap += 3; | fb += sizeof(uint16_t) / sizeof(*fb); | } | bmap += (width - padded_line); | fb -= ((2*width) + lcd_line_length); | } | break; | #endif /* CONFIG_BMP_24BPP */ | default: | break; | }; | | return (0); | } | #endif | | cat board/freescale/mx6q_sabresd/mx6q_sabresd.c | ...... | #ifndef CONFIG_MXC_EPDC | #ifdef CONFIG_LCD | void lcd_enable(void) <------------------------------------------------+ { char *s; int ret; unsigned int reg; s = getenv("lvds_num"); di = simple_strtol(s, NULL, 10); /* * hw_rev 2: IPUV3DEX * hw_rev 3: IPUV3M * hw_rev 4: IPUV3H */ g_ipu_hw_rev = IPUV3_HW_REV_IPUV3H; /** * zengjf modify don't show uboot logo */ imx_pwm_config(pwm0, 25000, 50000); imx_pwm_enable(pwm0); #if defined CONFIG_MX6Q /* PWM backlight */ mxc_iomux_v3_setup_pad(MX6Q_PAD_SD1_DAT3__PWM1_PWMO); /* LVDS panel CABC_EN0 */ //mxc_iomux_v3_setup_pad(MX6Q_PAD_NANDF_CS2__GPIO_6_15); /* LVDS panel CABC_EN1 */ //mxc_iomux_v3_setup_pad(MX6Q_PAD_NANDF_CS3__GPIO_6_16); #elif defined CONFIG_MX6DL /* PWM backlight */ mxc_iomux_v3_setup_pad(MX6DL_PAD_SD1_DAT3__PWM1_PWMO); /* LVDS panel CABC_EN0 */ //mxc_iomux_v3_setup_pad(MX6DL_PAD_NANDF_CS2__GPIO_6_15); /* LVDS panel CABC_EN1 */ //mxc_iomux_v3_setup_pad(MX6DL_PAD_NANDF_CS3__GPIO_6_16); #endif /* * Set LVDS panel CABC_EN0 to low to disable * CABC function. This function will turn backlight * automatically according to display content, so * simply disable it to get rid of annoying unstable * backlight phenomena. */ /* reg = readl(GPIO6_BASE_ADDR + GPIO_GDIR); reg |= (1 << 15); writel(reg, GPIO6_BASE_ADDR + GPIO_GDIR); reg = readl(GPIO6_BASE_ADDR + GPIO_DR); reg &= ~(1 << 15); writel(reg, GPIO6_BASE_ADDR + GPIO_DR); */ /* * Set LVDS panel CABC_EN1 to low to disable * CABC function. */ /* reg = readl(GPIO6_BASE_ADDR + GPIO_GDIR); reg |= (1 << 16); writel(reg, GPIO6_BASE_ADDR + GPIO_GDIR); reg = readl(GPIO6_BASE_ADDR + GPIO_DR); reg &= ~(1 << 16); writel(reg, GPIO6_BASE_ADDR + GPIO_DR); */ /* Disable ipu1_clk/ipu1_di_clk_x/ldb_dix_clk. */ reg = readl(CCM_BASE_ADDR + CLKCTL_CCGR3); reg &= ~0xC033; writel(reg, CCM_BASE_ADDR + CLKCTL_CCGR3); #if defined CONFIG_MX6Q /* * Align IPU1 HSP clock and IPU1 DIx pixel clock * with kernel setting to avoid screen flick when * booting into kernel. Developer should change * the relevant setting if kernel setting changes. * IPU1 HSP clock tree: * osc_clk(24M)->pll2_528_bus_main_clk(528M)-> * periph_clk(528M)->mmdc_ch0_axi_clk(528M)-> * ipu1_clk(264M) */ /* pll2_528_bus_main_clk */ /* divider */ writel(0x1, ANATOP_BASE_ADDR + 0x34); /* periph_clk */ /* source */ reg = readl(CCM_BASE_ADDR + CLKCTL_CBCMR); reg &= ~(0x3 << 18); writel(reg, CCM_BASE_ADDR + CLKCTL_CBCMR); reg = readl(CCM_BASE_ADDR + CLKCTL_CBCDR); reg &= ~(0x1 << 25); writel(reg, CCM_BASE_ADDR + CLKCTL_CBCDR); /* * Check PERIPH_CLK_SEL_BUSY in * MXC_CCM_CDHIPR register. */ do { udelay(5); reg = readl(CCM_BASE_ADDR + CLKCTL_CDHIPR); } while (reg & (0x1 << 5)); /* mmdc_ch0_axi_clk */ /* divider */ reg = readl(CCM_BASE_ADDR + CLKCTL_CBCDR); reg &= ~(0x7 << 19); writel(reg, CCM_BASE_ADDR + CLKCTL_CBCDR); /* * Check MMDC_CH0PODF_BUSY in * MXC_CCM_CDHIPR register. */ do { udelay(5); reg = readl(CCM_BASE_ADDR + CLKCTL_CDHIPR); } while (reg & (0x1 << 4)); /* ipu1_clk */ reg = readl(CCM_BASE_ADDR + CLKCTL_CSCDR3); /* source */ reg &= ~(0x3 << 9); /* divider */ reg &= ~(0x7 << 11); reg |= (0x1 << 11); writel(reg, CCM_BASE_ADDR + CLKCTL_CSCDR3); /* * ipu1_pixel_clk_x clock tree: * osc_clk(24M)->pll2_528_bus_main_clk(528M)-> * pll2_pfd_352M(452.57M)->ldb_dix_clk(64.65M)-> * ipu1_di_clk_x(64.65M)->ipu1_pixel_clk_x(64.65M) */ /* pll2_pfd_352M */ /* disable */ writel(0x1 << 7, ANATOP_BASE_ADDR + 0x104); /* divider */ writel(0x3F, ANATOP_BASE_ADDR + 0x108); writel(0x15, ANATOP_BASE_ADDR + 0x104); /* ldb_dix_clk */ /* source */ reg = readl(CCM_BASE_ADDR + CLKCTL_CS2CDR); reg &= ~(0x3F << 9); reg |= (0x9 << 9); writel(reg, CCM_BASE_ADDR + CLKCTL_CS2CDR); /* divider */ reg = readl(CCM_BASE_ADDR + CLKCTL_CSCMR2); reg |= (0x3 << 10); writel(reg, CCM_BASE_ADDR + CLKCTL_CSCMR2); /* pll2_pfd_352M */ /* enable after ldb_dix_clk source is set */ writel(0x1 << 7, ANATOP_BASE_ADDR + 0x108); /* ipu1_di_clk_x */ /* source */ reg = readl(CCM_BASE_ADDR + CLKCTL_CHSCCDR); reg &= ~0xE07; reg |= 0x803; writel(reg, CCM_BASE_ADDR + CLKCTL_CHSCCDR); #elif defined CONFIG_MX6DL /* CONFIG_MX6Q */ /* * IPU1 HSP clock tree: * osc_clk(24M)->pll3_usb_otg_main_clk(480M)-> * pll3_pfd_540M(540M)->ipu1_clk(270M) */ /* pll3_usb_otg_main_clk */ /* divider */ writel(0x3, ANATOP_BASE_ADDR + 0x18); /* pll3_pfd_540M */ /* divider */ writel(0x3F << 8, ANATOP_BASE_ADDR + 0xF8); writel(0x10 << 8, ANATOP_BASE_ADDR + 0xF4); /* enable */ writel(0x1 << 15, ANATOP_BASE_ADDR + 0xF8); /* ipu1_clk */ reg = readl(CCM_BASE_ADDR + CLKCTL_CSCDR3); /* source */ reg |= (0x3 << 9); /* divider */ reg &= ~(0x7 << 11); reg |= (0x1 << 11); writel(reg, CCM_BASE_ADDR + CLKCTL_CSCDR3); /* * ipu1_pixel_clk_x clock tree: * osc_clk(24M)->pll2_528_bus_main_clk(528M)-> * pll2_pfd_352M(452.57M)->ldb_dix_clk(64.65M)-> * ipu1_di_clk_x(64.65M)->ipu1_pixel_clk_x(64.65M) */ /* pll2_528_bus_main_clk */ /* divider */ writel(0x1, ANATOP_BASE_ADDR + 0x34); /* pll2_pfd_352M */ /* disable */ writel(0x1 << 7, ANATOP_BASE_ADDR + 0x104); /* divider */ writel(0x3F, ANATOP_BASE_ADDR + 0x108); writel(0x15, ANATOP_BASE_ADDR + 0x104); /* ldb_dix_clk */ /* source */ reg = readl(CCM_BASE_ADDR + CLKCTL_CS2CDR); reg &= ~(0x3F << 9); reg |= (0x9 << 9); writel(reg, CCM_BASE_ADDR + CLKCTL_CS2CDR); /* divider */ reg = readl(CCM_BASE_ADDR + CLKCTL_CSCMR2); reg |= (0x3 << 10); writel(reg, CCM_BASE_ADDR + CLKCTL_CSCMR2); /* pll2_pfd_352M */ /* enable after ldb_dix_clk source is set */ writel(0x1 << 7, ANATOP_BASE_ADDR + 0x108); /* ipu1_di_clk_x */ /* source */ reg = readl(CCM_BASE_ADDR + CLKCTL_CHSCCDR); reg &= ~0xE07; reg |= 0x803; writel(reg, CCM_BASE_ADDR + CLKCTL_CHSCCDR); #endif /* CONFIG_MX6DL */ /* Enable ipu1/ipu1_dix/ldb_dix clocks. */ if (di == 1) { reg = readl(CCM_BASE_ADDR + CLKCTL_CCGR3); reg |= 0xC033; writel(reg, CCM_BASE_ADDR + CLKCTL_CCGR3); } else { reg = readl(CCM_BASE_ADDR + CLKCTL_CCGR3); reg |= 0x300F; writel(reg, CCM_BASE_ADDR + CLKCTL_CCGR3); } /** * zengjf 2015-7-23 modify to 24bit or 16bit * * #define IPU_PIX_FMT_BGR24 fourcc('B', 'G', 'R', '3') /*< 24 BGR-8-8-8 */ * #define IPU_PIX_FMT_RGB24 fourcc('R', 'G', 'B', '3') /*< 24 RGB-8-8-8 */ */ //ret = ipuv3_fb_init(&lvds_xga, di, IPU_PIX_FMT_RGB666, //ret = ipuv3_fb_init(&lvds_xga, di, IPU_PIX_FMT_RGB24, ret = ipuv3_fb_init(&lvds_xga, di, IPU_PIX_FMT_RGB24, ---------+ DI_PCLK_LDB, 65000000); | if (ret) | puts("LCD cannot be configured "); | | /* | * LVDS0 mux to IPU1 DI0. | * LVDS1 mux to IPU1 DI1. | */ | reg = readl(IOMUXC_BASE_ADDR + 0xC); | reg &= ~(0x000003C0); | reg |= 0x00000100; | writel(reg, IOMUXC_BASE_ADDR + 0xC); | | if (di == 1) | writel(0x40C, IOMUXC_BASE_ADDR + 0x8); | else | writel(0x201, IOMUXC_BASE_ADDR + 0x8); | } | #endif | | cat drivers/video/mxc_ipuv3_fb.c V int ipuv3_fb_init(struct fb_videomode *mode, int di, int interface_pix_fmt, ipu_di_clk_parent_t di_clk_parent, int di_clk_val) | { | int ret; | | ret = ipu_probe(di, di_clk_parent, di_clk_val); | if (ret) | puts("Error initializing IPU "); | | debug("Framebuffer at 0x%x ", (unsigned int)lcd_base); | ret = mxcfb_probe(interface_pix_fmt, mode, di); | | | return ret; | | } +-------------------------------+ | cat drivers/video/mxc_ipuv3_fb.c v static int mxcfb_probe(u32 interface_pix_fmt, struct fb_videomode *mode, int di) { | struct fb_info *fbi; | struct mxcfb_info *mxcfbi; | int ret = 0; | +----------------------------------+ /* | * Initialize FB structures | */ | fbi = mxcfb_init_fbinfo(); | if (!fbi) { | ret = -ENOMEM; | goto err0; | } | mxcfbi = (struct mxcfb_info *)fbi->par; | | if (!g_dp_in_use) { | mxcfbi->ipu_ch = MEM_BG_SYNC; | mxcfbi->blank = FB_BLANK_UNBLANK; | } else { | mxcfbi->ipu_ch = MEM_DC_SYNC; | mxcfbi->blank = FB_BLANK_POWERDOWN; | } | | mxcfbi->ipu_di = di; | | ipu_disp_set_global_alpha(mxcfbi->ipu_ch, 1, 0x80); | ipu_disp_set_color_key(mxcfbi->ipu_ch, 0, 0); | strcpy(fbi->fix.id, "DISP3 BG"); | | g_dp_in_use = 1; | | mxcfb_info[mxcfbi->ipu_di] = fbi; | | /* Need dummy values until real panel is configured */ | fbi->var.xres = 640; | fbi->var.yres = 480; | /** | * zengjf 2015-8-23 modify to 24bit display, it can't work well | */ | fbi->var.bits_per_pixel = 16; | //fbi->var.bits_per_pixel = 24; | | mxcfbi->ipu_di_pix_fmt = interface_pix_fmt; <----------------------+ fb_videomode_to_var(&fbi->var, mode); mxcfb_check_var(&fbi->var, fbi); /* Default Y virtual size is 2x panel size */ fbi->var.yres_virtual = fbi->var.yres * 2; mxcfb_set_fix(fbi); /* alocate fb first */ if (mxcfb_map_video_memory(fbi) < 0) return -ENOMEM; mxcfb_set_par(fbi); -------------------------------------+ | /* Setting panel_info for lcd */ | panel_info.vl_col = fbi->var.xres; | panel_info.vl_row = fbi->var.yres; | panel_info.vl_bpix = LCD_BPP; // this same can't wake well ---------+ | | | lcd_line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8; | | | | debug("MXC IPUV3 configured " | | "XRES = %d YRES = %d BitsXpixel = %d ", | | panel_info.vl_col, | | panel_info.vl_row, | | panel_info.vl_bpix); | | | | ipu_dump_registers(); | | | | return 0; | | | | err0: | | return ret; | | } | | | | cat include/configs/mx6dl_sabresd.h | | #define LCD_BPP LCD_COLOR16 <------------------------------+ | | | cat include/lcd.h | | #define LCD_MONOCHROME 0 +----------+ | #define LCD_COLOR2 1 | | #define LCD_COLOR4 2 | | #define LCD_COLOR8 3 | | #define LCD_COLOR16 4 <----------+ | | cat drivers/video/mxc_ipuv3_fb.c | static int mxcfb_set_par(struct fb_info *fbi) <----------------------+ { int retval = 0; u32 mem_len; ipu_di_signal_cfg_t sig_cfg; struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; uint32_t out_pixel_fmt; ipu_disable_channel(mxc_fbi->ipu_ch); ipu_uninit_channel(mxc_fbi->ipu_ch); mxcfb_set_fix(fbi); mem_len = fbi->var.yres_virtual * fbi->fix.line_length; if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) { if (fbi->fix.smem_start) mxcfb_unmap_video_memory(fbi); if (mxcfb_map_video_memory(fbi) < 0) return -ENOMEM; } setup_disp_channel1(fbi); memset(&sig_cfg, 0, sizeof(sig_cfg)); if (fbi->var.vmode & FB_VMODE_INTERLACED) { sig_cfg.interlaced = 1; out_pixel_fmt = IPU_PIX_FMT_YUV444; } else { if (mxc_fbi->ipu_di_pix_fmt) { out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt; debug(" zengjf1 %d. ", out_pixel_fmt); } else { out_pixel_fmt = IPU_PIX_FMT_RGB666; debug(" zengjf2 %d. ", out_pixel_fmt); } } if (fbi->var.vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */ sig_cfg.odd_field_first = 1; if ((fbi->var.sync & FB_SYNC_EXT) || ext_clk_used) sig_cfg.ext_clk = 1; if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) sig_cfg.Hsync_pol = 1; if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) sig_cfg.Vsync_pol = 1; if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL)) sig_cfg.clk_pol = 1; if (fbi->var.sync & FB_SYNC_DATA_INVERT) sig_cfg.data_pol = 1; if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT)) sig_cfg.enable_pol = 1; if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN) sig_cfg.clkidle_en = 1; debug("zengjf pixclock = %d ", fbi->var.pixclock); debug("pixclock = %ul Hz ", (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL)); debug("bpp_to_pixfmt: %d ", fbi->var.bits_per_pixel); if (ipu_init_sync_panel(mxc_fbi->ipu_di, (PICOS2KHZ(fbi->var.pixclock)) * 1000UL, fbi->var.xres, fbi->var.yres, out_pixel_fmt, fbi->var.left_margin, fbi->var.hsync_len, fbi->var.right_margin, fbi->var.upper_margin, fbi->var.vsync_len, fbi->var.lower_margin, 0, sig_cfg) != 0) { puts("mxcfb: Error initializing panel. "); return -EINVAL; } retval = setup_disp_channel2(fbi); --------------------+ if (retval) | return retval; | | if (mxc_fbi->blank == FB_BLANK_UNBLANK) | ipu_enable_channel(mxc_fbi->ipu_ch); | | return retval; | } | | cat drivers/video/mxc_ipuv3_fb.c | static int setup_disp_channel2(struct fb_info *fbi) <----------+ { int retval = 0; struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; mxc_fbi->cur_ipu_buf = 1; if (mxc_fbi->alpha_chan_en) mxc_fbi->cur_ipu_alpha_buf = 1; fbi->var.xoffset = fbi->var.yoffset = 0; debug("%s: ipu_ch{%x} xres{%d} yres{%d} line_length{%d} smem_start{%lx} smem_end{%lx} ", __func__, mxc_fbi->ipu_ch, fbi->var.xres, fbi->var.yres, fbi->fix.line_length, fbi->fix.smem_start, fbi->fix.smem_start + (fbi->fix.line_length * fbi->var.yres)); retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, bpp_to_pixfmt(fbi), --------------------+ fbi->var.xres, fbi->var.yres, | fbi->fix.line_length, | fbi->fix.smem_start + | (fbi->fix.line_length * fbi->var.yres), | fbi->fix.smem_start, | 0, 0); | if (retval) | printf("ipu_init_channel_buffer error %d ", retval); | | return retval; | } | | cat drivers/video/mxc_ipuv3_fb.c | static uint32_t bpp_to_pixfmt(struct fb_info *fbi) <-------------------+ { uint32_t pixfmt = 0; debug("bpp_to_pixfmt: %d ", fbi->var.bits_per_pixel); if (fbi->var.nonstd) return fbi->var.nonstd; switch (fbi->var.bits_per_pixel) { case 24: pixfmt = IPU_PIX_FMT_BGR24; break; case 32: pixfmt = IPU_PIX_FMT_BGR32; break; case 16: pixfmt = IPU_PIX_FMT_RGB565; break; } return pixfmt; }