I.MX6 U-boot lvds display hacking
/***********************************************************************************
* 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, # @ skip reset vector
mov r2, # @ 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 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 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, # @ leave words for abort-stack
and sp, sp, #~ @ 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, # @ 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, , sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, , 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)() != ) {
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 - )) & ~(PAGE_SIZE - );
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 - )) & ~(PAGE_SIZE - );
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[]; |
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[]; |
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, ); |
} |
#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\n"); |
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 = ; 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 (); |
} |
|
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)) / ; lcd_init (lcd_base); /* LCD initialization */ ----------+
|
/* Device initialization */ |
memset (&lcddev, , sizeof (lcddev)); |
|
strcpy (lcddev.name, "lcd"); |
lcddev.ext = ; /* 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 == ) ? : rc; |
} |
|
cat common/lcd.c |
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 = ;
lcd_clear (NULL, , , NULL); /* dummy args */ -------------+
lcd_enable (); -------------*---------------+
| |
/* Initialize the console */ | |
console_col = ; | |
#ifdef CONFIG_LCD_INFO_BELOW_LOGO | |
console_row = + BMP_LOGO_HEIGHT / VIDEO_FONT_HEIGHT; | |
#else | |
console_row = ; /* leave 1 blank line below logo */ | |
#endif | |
| |
return ; | |
} +-------------------------------------------------+ |
| |
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, , , ); |
lcd_setcolreg (CONSOLE_COLOR_RED, 0xFF, , ); |
lcd_setcolreg (CONSOLE_COLOR_GREEN, , 0xFF, ); |
lcd_setcolreg (CONSOLE_COLOR_YELLOW, 0xFF, 0xFF, ); |
lcd_setcolreg (CONSOLE_COLOR_BLUE, , , 0xFF); |
lcd_setcolreg (CONSOLE_COLOR_MAGENTA, 0xFF, , 0xFF); |
lcd_setcolreg (CONSOLE_COLOR_CYAN, , 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 = ; | |
console_row = ; | |
| |
return (); | |
} | |
| |
cat common/lcd.c | |
static void *lcd_logo (void) <-----------+ |
{ |
#ifdef CONFIG_SPLASH_SCREEN |
char *s; |
ulong addr; |
static int do_splash = ; |
|
// get the "splashimage=0x30000000\0" |
if (do_splash && (s = getenv("splashimage")) != NULL) { |
int x = , y = ; |
do_splash = ; |
|
// get the image address |
addr = simple_strtoul (s, NULL, ); |
|
#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[] == 'm') |
x = BMP_ALIGN_CENTER; |
else |
x = simple_strtol (s, NULL, ); |
|
if ((s = strchr (s + , ',')) != NULL) { |
if (s[] == 'm') |
y = BMP_ALIGN_CENTER; |
else |
y = simple_strtol (s + , NULL, ); |
} |
} |
#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[]=='B') && |
(bmp->header.signature[]=='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 = ; |
y = ; |
|
|
if (lcd_display_bitmap (addr, x, y) == ) { -------------------+ |
return ((void *)lcd_base); | |
} | |
} | |
#endif /* CONFIG_SPLASH_SCREEN */ | |
| |
#ifdef CONFIG_LCD_LOGO | |
bitmap_plot (, ); | |
#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[]=='B') && |
(bmp->header.signature[]=='M'))) { |
printf ("Error: no valid bmp image at %lx\n", bmp_image); |
return ; |
} |
|
width = le32_to_cpu (bmp->header.width); |
height = le32_to_cpu (bmp->header.height); |
bmp_bpix = le16_to_cpu(bmp->header.bit_count); |
colors = << bmp_bpix; |
compression = le32_to_cpu (bmp->header.compression); |
|
bpix = NBITS(panel_info.vl_bpix); |
|
if ((bpix != ) && (bpix != ) && (bpix != ) && (bpix != )) { |
printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", |
bpix, bmp_bpix); |
return ; |
} |
|
#if defined(CONFIG_BMP_24BPP) |
/* We support displaying 24bpp BMPs on 16bpp LCDs */ |
if (bpix != bmp_bpix && (bmp_bpix != || bpix != ) && |
(bmp_bpix != || bpix != )) { |
#else |
/* We support displaying 8bpp BMPs on 16bpp LCDs */ |
if (bpix != bmp_bpix && (bmp_bpix != || bpix != )) { |
#endif |
printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", |
bpix, |
le16_to_cpu(bmp->header.bit_count)); |
return ; |
} |
|
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 == ) { |
#if defined(CONFIG_PXA250) |
cmap = (ushort *)fbi->palette; |
#elif defined(CONFIG_MPC823) |
cmap = (ushort *)&(cp->lcd_cmap[*sizeof(ushort)]); |
#elif !defined(CONFIG_ATMEL_LCD) |
cmap = panel_info.cmap; |
#endif |
cmap_base = cmap; |
|
/* Set color map */ |
for (i=; i<colors; ++i) { |
bmp_color_table_entry_t cte = bmp->color_table[i]; |
#if !defined(CONFIG_ATMEL_LCD) |
ushort colreg = |
( ((cte.red) << ) & 0xf800) | |
( ((cte.green) << ) & 0x07e0) | |
( ((cte.blue) >> ) & 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==) |
{ |
width = ((width + ) & ~) >> ; |
x = ((x + ) & ~) >> ; |
pwidth= ((pwidth + ) & ~) >> ; |
} |
#endif |
padded_line = (width&0x3) ? ((width&~0x3)+) : (width); |
#ifdef CONFIG_SPLASH_SCREEN_ALIGN |
if (x == BMP_ALIGN_CENTER) |
x = max(, (pwidth - width) / ); |
else if (x < ) |
x = max(, pwidth - width + x + ); |
|
if (y == BMP_ALIGN_CENTER) |
y = max(, (panel_info.vl_row - height) / ); |
else if (y < ) |
y = max(, panel_info.vl_row - height + y + ); |
#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 - ) * lcd_line_length + x * bpix / ); |
|
switch (bmp_bpix) { |
case : /* pass through */ |
case : |
if (bpix != ) |
byte_width = width; |
else |
byte_width = width * ; |
|
for (i = ; i < height; ++i) { |
WATCHDOG_RESET(); |
for (j = ; j < width; j++) { |
if (bpix != ) { |
#if defined(CONFIG_PXA250) || defined(CONFIG_ATMEL_LCD) |
*(fb++) = *(bmap++); |
#elif defined(CONFIG_MPC823) || defined(CONFIG_MCC200) |
*(fb++) = - *(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 : |
for (i = ; i < height; ++i) { |
WATCHDOG_RESET(); |
for (j = ; j < width; j++) { |
#if defined(CONFIG_ATMEL_LCD_BGR555) |
*(fb++) = ((bmap[] & 0x1f) << ) | |
(bmap[] & 0x03); |
*(fb++) = (bmap[] & 0xe0) | |
((bmap[] & 0x7c) >> ); |
bmap += ; |
#else |
*(fb++) = *(bmap++); |
*(fb++) = *(bmap++); |
#endif |
} |
bmap += (padded_line - width) * ; |
fb -= (width * + lcd_line_length); |
} |
break; |
#endif /* CONFIG_BMP_16BPP */ |
#if defined(CONFIG_BMP_24BPP) |
case : |
if (bpix != ) { |
printf("Error: %d bit/pixel mode," |
"but BMP has %d bit/pixel\n", |
bpix, bmp_bpix); |
break; |
} |
for (i = ; i < height; ++i) { |
WATCHDOG_RESET(); |
for (j = ; j < width; j++) { |
*(uint16_t *)fb = ((*(bmap + ) << ) & 0xf800) |
| ((*(bmap + ) << ) & 0x07e0) |
| ((*(bmap) >> ) & 0x001f); |
bmap += ; |
fb += sizeof(uint16_t) / sizeof(*fb); |
} |
bmap += (width - padded_line); |
fb -= ((*width) + lcd_line_length); |
} |
break; |
#endif /* CONFIG_BMP_24BPP */ |
default: |
break; |
}; |
|
return (); |
} |
#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, ); /*
* 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, , );
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 << );
writel(reg, CCM_BASE_ADDR + CLKCTL_CBCMR); reg = readl(CCM_BASE_ADDR + CLKCTL_CBCDR);
reg &= ~(0x1 << );
writel(reg, CCM_BASE_ADDR + CLKCTL_CBCDR); /*
* Check PERIPH_CLK_SEL_BUSY in
* MXC_CCM_CDHIPR register.
*/
do {
udelay();
reg = readl(CCM_BASE_ADDR + CLKCTL_CDHIPR);
} while (reg & (0x1 << )); /* mmdc_ch0_axi_clk */
/* divider */
reg = readl(CCM_BASE_ADDR + CLKCTL_CBCDR);
reg &= ~(0x7 << );
writel(reg, CCM_BASE_ADDR + CLKCTL_CBCDR); /*
* Check MMDC_CH0PODF_BUSY in
* MXC_CCM_CDHIPR register.
*/
do {
udelay();
reg = readl(CCM_BASE_ADDR + CLKCTL_CDHIPR);
} while (reg & (0x1 << )); /* ipu1_clk */
reg = readl(CCM_BASE_ADDR + CLKCTL_CSCDR3);
/* source */
reg &= ~(0x3 << );
/* divider */
reg &= ~(0x7 << );
reg |= (0x1 << );
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 << , 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 << );
reg |= (0x9 << );
writel(reg, CCM_BASE_ADDR + CLKCTL_CS2CDR);
/* divider */
reg = readl(CCM_BASE_ADDR + CLKCTL_CSCMR2);
reg |= (0x3 << );
writel(reg, CCM_BASE_ADDR + CLKCTL_CSCMR2); /* pll2_pfd_352M */
/* enable after ldb_dix_clk source is set */
writel(0x1 << , 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 << , ANATOP_BASE_ADDR + 0xF8);
writel(0x10 << , ANATOP_BASE_ADDR + 0xF4);
/* enable */
writel(0x1 << , ANATOP_BASE_ADDR + 0xF8); /* ipu1_clk */
reg = readl(CCM_BASE_ADDR + CLKCTL_CSCDR3);
/* source */
reg |= (0x3 << );
/* divider */
reg &= ~(0x7 << );
reg |= (0x1 << );
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 << , 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 << );
reg |= (0x9 << );
writel(reg, CCM_BASE_ADDR + CLKCTL_CS2CDR);
/* divider */
reg = readl(CCM_BASE_ADDR + CLKCTL_CSCMR2);
reg |= (0x3 << );
writel(reg, CCM_BASE_ADDR + CLKCTL_CSCMR2); /* pll2_pfd_352M */
/* enable after ldb_dix_clk source is set */
writel(0x1 << , 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 == ) {
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, ); |
if (ret) |
puts("LCD cannot be configured\n"); |
|
/* |
* 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 == ) |
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\n"); |
|
debug("Framebuffer at 0x%x\n", (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 = ; |
+----------------------------------+
/* |
* 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, , 0x80); |
ipu_disp_set_color_key(mxcfbi->ipu_ch, , ); |
strcpy(fbi->fix.id, "DISP3 BG"); |
|
g_dp_in_use = ; |
|
mxcfb_info[mxcfbi->ipu_di] = fbi; |
|
/* Need dummy values until real panel is configured */ |
fbi->var.xres = ; |
fbi->var.yres = ; |
/** |
* zengjf 2015-8-23 modify to 24bit display, it can't work well |
*/ |
fbi->var.bits_per_pixel = ; |
//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 * ; mxcfb_set_fix(fbi); /* alocate fb first */
if (mxcfb_map_video_memory(fbi) < )
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)) / ; | |
| |
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 ; | |
| |
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 = ;
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) < )
return -ENOMEM;
} setup_disp_channel1(fbi); memset(&sig_cfg, , sizeof(sig_cfg));
if (fbi->var.vmode & FB_VMODE_INTERLACED) {
sig_cfg.interlaced = ;
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. \n", out_pixel_fmt);
}
else
{
out_pixel_fmt = IPU_PIX_FMT_RGB666;
debug(" zengjf2 %d. \n", out_pixel_fmt);
}
}
if (fbi->var.vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
sig_cfg.odd_field_first = ;
if ((fbi->var.sync & FB_SYNC_EXT) || ext_clk_used)
sig_cfg.ext_clk = ;
if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT)
sig_cfg.Hsync_pol = ;
if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)
sig_cfg.Vsync_pol = ;
if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL))
sig_cfg.clk_pol = ;
if (fbi->var.sync & FB_SYNC_DATA_INVERT)
sig_cfg.data_pol = ;
if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT))
sig_cfg.enable_pol = ;
if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN)
sig_cfg.clkidle_en = ; debug("zengjf pixclock = %d \n", fbi->var.pixclock);
debug("pixclock = %ul Hz\n", (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL));
debug("bpp_to_pixfmt: %d\n", 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,
, sig_cfg) != ) {
puts("mxcfb: Error initializing panel.\n");
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 = ;
struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; mxc_fbi->cur_ipu_buf = ;
if (mxc_fbi->alpha_chan_en)
mxc_fbi->cur_ipu_alpha_buf = ; fbi->var.xoffset = fbi->var.yoffset = ; debug("%s: ipu_ch{%x} xres{%d} yres{%d} line_length{%d} smem_start{%lx} smem_end{%lx}\n",
__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, |
, ); |
if (retval) |
printf("ipu_init_channel_buffer error %d\n", retval); |
|
return retval; |
} |
|
cat drivers/video/mxc_ipuv3_fb.c |
static uint32_t bpp_to_pixfmt(struct fb_info *fbi) <-------------------+
{
uint32_t pixfmt = ; debug("bpp_to_pixfmt: %d\n", fbi->var.bits_per_pixel); if (fbi->var.nonstd)
return fbi->var.nonstd; switch (fbi->var.bits_per_pixel) {
case :
pixfmt = IPU_PIX_FMT_BGR24;
break;
case :
pixfmt = IPU_PIX_FMT_BGR32;
break;
case :
pixfmt = IPU_PIX_FMT_RGB565;
break;
}
return pixfmt;
}
I.MX6 U-boot lvds display hacking的更多相关文章
- I.MX6 Linux kernel LVDS backlight enable
/*************************************************************************** * I.MX6 Linux kernel LV ...
- I.MX6 ar1020 SPI device driver hacking
/************************************************************************************ * I.MX6 ar1020 ...
- I.MX6 Linux Serial Baud Rate hacking
/******************************************************************************** * I.MX6 Linux Seri ...
- I.MX6 Linux I2C device& driver hacking
/******************************************************************************************* * I.MX6 ...
- I.MX6 AD7606-4 device driver registe hacking
/********************************************************************** * I.MX6 AD7606-4 device driv ...
- I.MX6 change boot partition 1 to User area
/************************************************************************************ * I.MX6 change ...
- I.MX6 PHY fixup 调用流程 hacking
/********************************************************************************** * I.MX6 PHY fixu ...
- I.MX6 u-boot 2009 lvds hdmi lcd 补丁
/************************************************************************* * I.MX6 u-boot 2009 lvds ...
- I.MX6 lcd lvds hdmi bootargs
/********************************************************************* * I.MX6 lcd lvds hdmi bootarg ...
随机推荐
- Java用数据结构解决实现问题之数学问题
有趣的整数: 完数:如果一个数字恰好等于他的因子之和,就叫做完数,需求是求出10000以内的所有的完数. 解法:1.用n去除以1-n之间的所有的整数,将能整除的被除数保存到一个数组中,作为n的一个因子 ...
- 3vim编辑器
三种模式命令行模式任何模式下 ESC 进入编辑模式(INSERT)从命令模式下 a, i 等 进入可视化模式(VISUAL)(VISUAL BLOCK)命令模式下,敲 v 进入非编辑模式, ctrl ...
- SQL substring()函数
①substring()函数是个截取函数,不同的数据库语法有区别 MySQL: SUBSTR( ), SUBSTRING( ) Oracle: SUBSTR( ) SQL Server: SUBSTR ...
- SQL :模糊查询,转义字符
1. 查询table表name列包含 '_BCE' 的记录 select * from table where name like '_BCE%' ABCEDF _BCEFG _BCEDF 3 row ...
- web实现负载均衡的几种实现方式
摘要: 负载均衡(Load Balance)是集群技术(Cluster)的一种应用.负载均衡可以将工作任务分摊到多个处理单元,从而提高并发处理能力.目前最常见的负载均衡应用是Web负载均衡.根据实现的 ...
- bzoj1623 / P2909 [USACO08OPEN]牛的车Cow Cars
P2909 [USACO08OPEN]牛的车Cow Cars 显然的贪心. 按速度从小到大排序.然后找车最少的车道,查询是否能填充进去. #include<iostream> #inclu ...
- python3的一些练习题
下面是最近写的一些python3版本的一些练习题: 1.4+44+444+4444+.....+=? root@wadeson:~/study_for_python/homework# cat aa. ...
- BCG控件初步领略
BCGPVisualStudioGUIDemo 这个界面很不错呀,如果能够实现这种效果,能够解决系列问题 画图程序,这种界面非常先进.用于石材大板等非常优秀. email的效果 这种东西如果效果不错, ...
- 20145221 《Java程序设计》第七周学习总结
20145221 <Java程序设计>第七周学习总结 教材学习内容总结 第十二章部分 - Lambda 认识Lambda语法 Lambda去可以重复,符合DRY原则,而且Lambda表达式 ...
- QT+qtablewidget自定义表头【合并单元格】
1.把下列文件放在工程中[已上传到我的文件中] 2.代码 auto *headview = new HHeadViewClass(Qt::Horizontal, ui.tableWidget); he ...