本文记录imx6 uboot中关于lcd初始化的过程。
uboot中相关的文件:
cpu/arm_cortexa8/start.S
lib_arm/board.c
board/freescale/mx6q_sabresd/mx6q_sabresd.c
common/stdio.c
common/lcd.c
drivers/video/ipu_common.c
/* 汇编调用C语言 */./cpu/arm_cortexa8/start.S:ldr pc, _start_armboot @ jump to C code ------+ |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); | | | stdio_init (); /* get the devices list going. */ ------------------+ | | | jumptable_init (); | | | | ...... | | | | for (;;) { | | main_loop (); | | } | | | | /* NOTREACHED - no way out of command loop except booting */ | |} | | | |init_fnc_t *init_sequence[] = { <-------|---+#if defined(CONFIG_ARCH_CPU_INIT) | arch_cpu_init, /* basic arch cpu dependent setup */ |#endif | board_init, /* basic board dependent setup */ -------------+ |#if defined(CONFIG_USE_IRQ) | | interrupt_init, /* set up exceptions */ | |#endif | | timer_init, /* initialize timer */ | | env_init, /* initialize environment */ | | init_baudrate, /* initialze baudrate settings */ | | serial_init, /* serial communications setup */ | | console_init_f, /* stage 1 init of console */ | | display_banner, /* say that we are here */ | |#if defined(CONFIG_DISPLAY_CPUINFO) | | print_cpuinfo, /* display cpu info (and speed) */ | |#endif | |#if defined(CONFIG_DISPLAY_BOARDINFO) | | checkboard, /* display board info */ | |#endif | |#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) | | init_func_i2c, | |#endif | | dram_init, /* configure available RAM banks */ | |#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI) | | arm_pci_init, | |#endif | | display_dram_config, | | NULL, | |}; | | | |board/freescale/mx6q_sabresd/mx6q_sabresd.c | |int board_init(void) <------------------+ |{ |/* need set Power Supply Glitch to 0x41736166 |*and need clear Power supply Glitch Detect bit |* when POR or reboot or power on Otherwise system |*could not be power off anymore*/ | u32 reg; | writel(0x41736166, SNVS_BASE_ADDR + 0x64);/*set LPPGDR*/ | udelay(10); | reg = readl(SNVS_BASE_ADDR + 0x4c); | reg |= (1 << 3); | writel(reg, SNVS_BASE_ADDR + 0x4c);/*clear LPSR*/ | | mxc_iomux_v3_init((void *)IOMUXC_BASE_ADDR); | setup_boot_device(); | fsl_set_system_rev(); | | /* board id for linux */ | gd->bd->bi_arch_number = MACH_TYPE_MX6Q_SABRESD; | | /* address of boot parameters */ | gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; | | setup_uart(); |#ifdef CONFIG_DWC_AHSATA | if (cpu_is_mx6q()) | setup_sata(); |#endif | |#ifdef CONFIG_VIDEO_MX5 | /* Enable lvds power */ |#ifdef UBOOT_SHOW_LOGO | setup_lvds_poweron(); |#endif | panel_info_init(); | | gd->fb_base = CONFIG_FB_BASE; |#ifdef CONFIG_ARCH_MMU | gd->fb_base = ioremap_nocache(iomem_to_phys(gd->fb_base), 0); |#endif |#endif | |#ifdef CONFIG_NAND_GPMI |// setup_gpmi_nand(); |#endif | |#if defined(CONFIG_ENET_RMII) && !defined(CONFIG_DWC_AHSATA) | setup_fec(); |#endif | |#ifdef CONFIG_MXC_EPDC | setup_epdc(); |#endif | return 0; |} | |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); |} | |common/lcd.c |int drv_lcd_init (void) <-----------------------+{ struct stdio_dev lcddev; int rc; //framebuffer基地址 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); //将lcd设备添加到uboot的设备链表中 | | return (rc == 0) ? 1 : rc; |} | |#define NBITS(type) (sizeof(type) * 8) | |board/freescale/mx6q_sabresd/mx6q_sabresd.c |vidinfo_t panel_info = { | .vl_refresh = 85, | .vl_col = 800, | .vl_row = 600, | .vl_pixclock = 26666667, | .vl_left_margin = 8, | .vl_right_margin = 100, | .vl_upper_margin = 4, | .vl_lower_margin = 8, | .vl_hsync = 4, | .vl_vsync = 1, | .vl_sync = 0, | .vl_mode = 0, | .vl_flag = 0, | .vl_bpix = 3, | cmap:0, |}; |typedef struct vidinfo { | ushort vl_col; /* Number of columns (i.e. 640) */ | ushort vl_row; /* Number of rows (i.e. 480) */ | ushort vl_width; /* Width of display area in millimeters */ | ushort vl_height; /* Height of display area in millimeters */ | | /* LCD configuration register */ | u_char vl_clkp; /* Clock polarity */ | u_char vl_oep; /* Output Enable polarity */ | u_char vl_hsp; /* Horizontal Sync polarity */ | u_char vl_vsp; /* Vertical Sync polarity */ | u_char vl_dp; /* Data polarity */ | u_char vl_bpix; /* Bits per pixel, 0 = 1, 1 = 2, 2 = 4, 3 = 8, 4 = 16 */ | u_char vl_lbw; /* LCD Bus width, 0 = 4, 1 = 8 */ | u_char vl_splt; /* Split display, 0 = single-scan, 1 = dual-scan */ | u_char vl_clor; /* Color, 0 = mono, 1 = color */ | u_char vl_tft; /* 0 = passive, 1 = TFT */ | | /* Horizontal control register. Timing from data sheet */ | ushort vl_hpw; /* Horz sync pulse width */ | u_char vl_blw; /* Wait before of line */ | u_char vl_elw; /* Wait end of line */ | | /* Vertical control register. */ | u_char vl_vpw; /* Vertical sync pulse width */ | u_char vl_bfw; /* Wait before of frame */ | u_char vl_efw; /* Wait end of frame */ | | /* PXA LCD controller params */ | struct pxafb_info pxa; |} vidinfo_t; | |static int lcd_init (void *lcdbase) <--------------------+{ /* Initialize the lcd controller */ debug ("[LCD] Initializing LCD frambuffer at %p\n", 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; | |} | | | |void lcd_ctrl_init(void *lcdbase) | |{ | | //计算要使用的framebuffer大小 col*row*bpp/8 字节 | | u32 mem_len = panel_info.vl_col * | | panel_info.vl_row * | | NBITS(panel_info.vl_bpix) / 8; | | | | /* | | * We rely on lcdbase being a physical address, i.e., either MMU off, | | * or 1-to-1 mapping. Might want to add some virt2phys here. | | */ | | if (!lcdbase) | | return; | | //将framebuffer清0 | | memset(lcdbase, 0, mem_len); | |} | | | |common/lcd.c | |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...\n"); | lcd_console_address = lcd_logo (); --------+ | | | console_col = 0; | | console_row = 0; | | | | return (0); | |} | |//显示logo | |static void *lcd_logo (void) <------+ |{ |#ifdef CONFIG_SPLASH_SCREEN | char *s; | ulong addr; | static int do_splash = 1; | | // get the "splashimage=0x30000000\0" | if (do_splash && (s = getenv("splashimage")) != NULL) { | int x = 0, y = 0; | do_splash = 0; | | // get the image address, 地址转为16进制 | addr = simple_strtoul (s, NULL, 16); | |#ifdef CONFIG_SPLASH_SCREEN_ALIGN | // get the "splashpos=m,m\0" | 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 | | //显示logo,根据x,y坐标 | 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 */ | |} | | | |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\n", 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\n", | 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\n", | bpix, | le16_to_cpu(bmp->header.bit_count)); | return 1; | } | | debug ("Display-bmp: %d x %d with %d colors\n", | (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; icolor_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 | |#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\n", | 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 | |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;#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 imx_pwm_config(pwm0, 25000, 50000); imx_pwm_enable(pwm0); mxc_iomux_v3_setup_pad(MX6DL_PAD_SD1_DAT2__GPIO_1_19); reg = readl(GPIO1_BASE_ADDR + GPIO_GDIR); reg |= (1 << 19); writel(reg, GPIO1_BASE_ADDR + GPIO_GDIR); reg = readl(GPIO1_BASE_ADDR + GPIO_DR);#ifdef UBOOT_SHOW_LOGO reg |= (1 << 19);#else reg &= ~(1 << 19);#endif writel(reg, GPIO1_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);#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 */ // 20C_403C reg = readl(CCM_BASE_ADDR + CLKCTL_CSCDR3); /* source */ //ipu1_hsp_clk_sel reg |= (0x3 << 9); /* divider */ reg &= ~(0x7 << 11); reg |= (0x1 << 11); //540M/2 = 270M 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 */ writel(0x1, ANATOP_BASE_ADDR + 0x34); /* pll2_pfd_352M */ writel(0x1 << 7, ANATOP_BASE_ADDR + 0x104); /* divider */ writel(0x3F, ANATOP_BASE_ADDR + 0x108); //This field controls the fractional divide value. The resulting frequency shall be 480*18/PFD0_FRAC where 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); } ret = ipuv3_fb_init(&lvds_xga, di, IPU_PIX_FMT_RGB666, DI_PCLK_LDB, 65000000); //18 bit -----+ //ret = ipuv3_fb_init(&lvds_xga, di, IPU_PIX_FMT_RGB24, DI_PCLK_LDB, 65000000); //24 bit | | | | if (ret) | | | puts("LCD cannot be configured \n"); +--------------------+ | | | |#ifdef I2C_GPIO_EEPROM_PROCESS //WRITE process | | | //write singal byte | | |#if 1 | | | { | | | Load_config_from_mmc(); | | | } | | |#endif | | |#endif | | | | | | /* | | | * LVDS0 mux to IPU1 DI0. | | | * LVDS1 mux to IPU1 DI1. | | | */ | | |#ifdef UBOOT_SHOW_LOGO | | | reg = readl(IOMUXC_BASE_ADDR + 0xC); | | | reg &= ~(0x000003C0); | | | reg |= 0x00000100; | | | writel(reg, IOMUXC_BASE_ADDR + 0xC); | | |#endif | | | if (di == 1) | | | { | | | writel(0x40C, IOMUXC_BASE_ADDR + 0x8);//18BIT | | | //writel(0x48C, IOMUXC_BASE_ADDR + 0x8);//24BIT | | | } | | | else | | | { | | | writel(0x201, IOMUXC_BASE_ADDR + 0x8);//18BIT | | | //writel(0x221, IOMUXC_BASE_ADDR + 0x8);//24BIT | | | } | | | ...... | |} | | |#endif | | | | | | | | |include/ipu.h | | |typedef enum { | | | DI_PCLK_PLL3, | | | DI_PCLK_LDB, <-------------------+ | | DI_PCLK_TVE | |} ipu_di_clk_parent_t; | | V |#define IPU_PIX_FMT_RGB666 fourcc('R', 'G', 'B', '6') /*< 18 RGB-6-6-6 */ |#define IPU_PIX_FMT_RGB24 fourcc('R', 'G', 'B', '3') /*< 24 RGB-8-8-8 */ | |#define fourcc(a, b, c, d)\ | (((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24)) | |drivers/video/mxc_ipuv3_fb.c |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\n"); | | debug("Framebuffer at 0x%x\n", (unsigned int)lcd_base); | ret = mxcfb_probe(interface_pix_fmt, mode, di); --------------+ | | return ret; | |} | | | |drivers/video/ipu_common.c | |int ipu_probe(int di, ipu_di_clk_parent_t di_clk_parent, int di_clk_val) <--------+ |{ | unsigned long ipu_base; | |#if defined(CONFIG_MXC_HSC) | u32 temp; | u32 *reg_hsc_mcd = (u32 *)MIPI_HSC_BASE_ADDR; | u32 *reg_hsc_mxt_conf = (u32 *)(MIPI_HSC_BASE_ADDR + 0x800); | | __raw_writel(0xF00, reg_hsc_mcd); | | /* CSI mode reserved*/ | temp = __raw_readl(reg_hsc_mxt_conf); | __raw_writel(temp | 0x0FF, reg_hsc_mxt_conf); | | temp = __raw_readl(reg_hsc_mxt_conf); | __raw_writel(temp | 0x10000, reg_hsc_mxt_conf); |#endif | | ipu_base = IPU_CTRL_BASE_ADDR; | /* base fixup */ | if (g_ipu_hw_rev == IPUV3_HW_REV_IPUV3H) /* IPUv3H */ | ipu_base += IPUV3H_REG_BASE; | else if (g_ipu_hw_rev == IPUV3_HW_REV_IPUV3M) /* IPUv3M */ | ipu_base += IPUV3M_REG_BASE; | else /* IPUv3D, v3E, v3EX */ | ipu_base += IPUV3DEX_REG_BASE; | ipu_cpmem_base = (u32 *)(ipu_base + IPU_CPMEM_REG_BASE); | ipu_dc_tmpl_reg = (u32 *)(ipu_base + IPU_DC_TMPL_REG_BASE); | | g_pixel_clk[0] = &pixel_clk[0]; | g_pixel_clk[1] = &pixel_clk[1]; | | g_di_clk[0] = &di_clk[0]; | g_di_clk[1] = &di_clk[1]; | g_di_clk[di]->rate = di_clk_val; | | g_ipu_clk = &ipu_clk; | debug("ipu_clk = %u\n", clk_get_rate(g_ipu_clk)); | | ipu_reset(); | | if (di_clk_parent == DI_PCLK_LDB) { | clk_set_parent(g_pixel_clk[di], g_di_clk[di]); | } else { | clk_set_parent(g_pixel_clk[0], g_ipu_clk); | clk_set_parent(g_pixel_clk[1], g_ipu_clk); | } | | clk_enable(g_ipu_clk); | | __raw_writel(0x807FFFFF, IPU_MEM_RST); | while (__raw_readl(IPU_MEM_RST) & 0x80000000) | ; | | ipu_init_dc_mappings(); | | __raw_writel(0, IPU_INT_CTRL(5)); | __raw_writel(0, IPU_INT_CTRL(6)); | __raw_writel(0, IPU_INT_CTRL(9)); | __raw_writel(0, IPU_INT_CTRL(10)); | | /* DMFC Init */ | ipu_dmfc_init(DMFC_NORMAL, 1); | | /* Set sync refresh channels as high priority */ | __raw_writel(0x18800000L, IDMAC_CHA_PRI(0)); | | /* Set MCU_T to divide MCU access window into 2 */ | __raw_writel(0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN); | | clk_disable(g_ipu_clk); | | return 0; |} | |/* | * Probe routine for the framebuffer driver. It is called during the | * driver binding process. The following functions are performed in | * this routine: Framebuffer initialization, Memory allocation and | * mapping, Framebuffer registration, IPU initialization. | * | * @return Appropriate error code to the kernel common code | */ |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; 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; lcd_line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8; debug("MXC IPUV3 configured\n" "XRES = %d YRES = %d BitsXpixel = %d\n", panel_info.vl_col, panel_info.vl_row, panel_info.vl_bpix); ipu_dump_registers(); return 0;err0: return ret;}
Author
Tony Liu
2016-8-23, Shenzhen