mx51支持三个framebuffer:fb0, fb1, fb2
/dev/graphics/fb0,/dev/graphics/fb1:
一个作为系统的主framebuffer,另外一个作为TVOUT输出的framebuffer,缺省情况下,fb0做primary(也就是用来在primary LCD显示UI),fb1做secondary(也就是用来在TVOUT输出上显示数据)。
根据项目中的实际物理连接情况,如果primary LCD连接到disp0,那么使用缺省设置,如果primary LCD连接到disp1,那么就要在kernel启动参数中加入di1_primary。
/dev/graphics/fb2 :
用来实现overlay叠加,显示video数据的framebuffer,仅支持bpp16,数据格式是yuv422,可以在DP(Display Process)和来自MEM_BG_SYNC的数据进行合成,再传送到DC/DI。
在mxc framebuffer probe 时,第一个注册的mxc framebuffer (fb0 或者fb1,这个framebuffer作为主LCD的framebuffer)使用MEM_BG_SYNC,第二个注册的mxc framebuffer(fb0或者fb1,这个framebuffer作为TVOUT使用的framebuffer)使用MEM_DC_SYNC,最后一个注册的framebuffer(fb2)使用MEM_FG_SYNC。
MEM_BG_SYNC 和MEM_FG_SYNC都是mx5x ipu中独一无二的资源,这意味着每个通道只能被其中一个framebuffer占用。事实上MEM_DC_SYNC这个通道并不存在,只是用来表示直接连接到DC
注意这几个framebuffer中的数据并不是直连显示设备,中间可能还有MX51 IPU的DP处理单元,DP Unit负责对fb0 fb1 fb2过来的数据进行处理,包括颜色转换,图层合并,gamma处理以及光标处理后发送给DC->DI->显示设备
fsl 在/sys/class/graphics/fbx/ 下创建了一个fsl特定的属性文件fsl_disp_property,在我的机器上显示如下
# cat /sys/class/graphics/fb0/fsl_disp_property 2-layer-fb-bg # cat /sys/class/graphics/fb1/fsl_disp_property 1-layer-fb # cat /sys/class/graphics/fb2/fsl_disp_property 2-layer-fb-fg
2-layer-fb-bg,表示fb0使用MEM_BG_SYNC通道,在IPU 的Display Process模块和MEM_FG_SYNC通道来的数据合并,再送入DC
1-layer-fb,表示fb1使用MEM_DC_SYNC通道,不经过Display Process模块,DC(Display Control)直接连接到DI(Display Interface)
2-layber-fb-fg,表示fb2使用MEM_FG_SYNC通道,在IPU的Display Process模块和MEM_BG_SYNC通道来的数据合并,再送入DC
应用层还可以修改/sys/class/graphics/fb0/fsl_disp_property和/sys/class/graphics/fb1/fsl_disp_property,使用如下命令:
echo 1-layer-fb > /sys/class/graphics/fb0/fsl_disp_property 或者 echo 2-layer-fb-bg > /sys/class/graphics/fb1/fsl_disp_property
注意,二者的作用是一样的,都会导致fb0和fb1交换显示通道,fsl引入这个属性的目的,就是给上层应用提供一个接口,使得应用层可以控制fb0 fb1使用MEM_BG_SYNC还是MEM_DC_SYNC通道,说到这里可能还是让人糊涂,为什么应用层要选择fb0 fb1的通道,这要先搞清楚MEM_BG_SYNC和MEM_DC_SYNC的区别。
imx51/imx53 IPU支持三种类型的显示通道:MEM_BG_SYNC, MEM_FG_SYNC和MEM_DC_SYNC,framebuffer只能通过三者之一传送数据,这些通道在物理上是唯一的,同一时间只能被一个framebuffer使用。
MEM_BG_SYNC:fb0 -> DP (full plane) -> DC -> DI0 MEM_FG_SYNC:fb2 -> DP (partial plane) -> DC -> DI0 MEM_DC_SYNC:fb1 -> DC -> DI1
正常情况下来自fb0的UI和来自fb2的video数据在DP合并后,送到DI0,我们就可以看到UI 浮在video上面的效果。但是当激活双屏的时候,一般希望video数据和UI 都送到后屏(也就是DI1),前屏只显示UI。这就需要fb1拥有MEM_BG_SYNC,此时需要交换fb1和fb0的显示通道如下:
MEM_BG_SYNC: fb1 -> DP (full plane) -> DC -> DI0 MEM_FG_SYNC: fb2 -> DP (partial plane) -> DC -> DI1 MEM_DC_SYNC: fb0 -> DC -> DI0
通道交换代码
swap_disp_chan 1673 static ssize_t swap_disp_chan(struct device *dev, 1674 struct device_attribute *attr, 1675 const char *buf, size_t count) 1676 { 1677 struct fb_info *info = dev_get_drvdata(dev); 1678 struct mxcfb_info *mxcfbi = (struct mxcfb_info *)info->par; 1679 struct mxcfb_info *fg_mxcfbi = NULL; 1680 1681 acquire_console_sem(); 1682 /* swap only happen between DP-BG and DC, while DP-FG disable */ 1683 if (((mxcfbi->ipu_ch == MEM_BG_SYNC) && 1684 (strstr(buf, "1-layer-fb") != NULL)) || 1685 ((mxcfbi->ipu_ch == MEM_DC_SYNC) && 1686 (strstr(buf, "2-layer-fb-bg") != NULL))) { 1687 int i; 1688 1689 for (i = 0; i < num_registered_fb; i++) { 1690 fg_mxcfbi = 1691 (struct mxcfb_info *)mxcfb_info[i]->par; 1692 if (fg_mxcfbi->ipu_ch == MEM_FG_SYNC) 1693 break; 1694 else 1695 fg_mxcfbi = NULL; 1696 } 1697 if (!fg_mxcfbi || 1698 fg_mxcfbi->cur_blank == FB_BLANK_UNBLANK) { 1699 dev_err(dev, 1700 "Can not switch while fb2(fb-fg) is on.\n"); 1701 release_console_sem(); 1702 return count; 1703 } 1704 1705 if (swap_channels(info) < 0) 1706 dev_err(dev, "Swap display channel failed.\n"); 1707 } 1708 1709 release_console_sem(); 1710 return count; 1711 } 1712 DEVICE_ATTR(fsl_disp_property, 644, show_disp_chan, swap_disp_chan);
swap_channels 545 static int swap_channels(struct fb_info *fbi) 546 { 547 int i; 548 int swap_mode; 549 ipu_channel_t ch_to; 550 struct mxcfb_info *mxc_fbi_from = (struct mxcfb_info *)fbi->par; 551 struct fb_info *fbi_to = NULL; 552 struct mxcfb_info *mxc_fbi_to; 553 554 /* what's the target channel? */ 555 if (mxc_fbi_from->ipu_ch == MEM_BG_SYNC) 556 ch_to = MEM_DC_SYNC; 557 else 558 ch_to = MEM_BG_SYNC; 559 560 for (i = 0; i < num_registered_fb; i++) { 561 mxc_fbi_to = 562 (struct mxcfb_info *)mxcfb_info[i]->par; 563 if (mxc_fbi_to->ipu_ch == ch_to) { 564 fbi_to = mxcfb_info[i]; 565 break; 566 } 567 } 568 if (fbi_to == NULL) 569 return -1; 570 571 ipu_clear_irq(mxc_fbi_from->ipu_ch_irq); 572 ipu_clear_irq(mxc_fbi_to->ipu_ch_irq); 573 ipu_free_irq(mxc_fbi_from->ipu_ch_irq, fbi); 574 ipu_free_irq(mxc_fbi_to->ipu_ch_irq, fbi_to); 575 576 if (mxc_fbi_from->cur_blank == FB_BLANK_UNBLANK) { 577 if (mxc_fbi_to->cur_blank == FB_BLANK_UNBLANK) 578 swap_mode = BOTH_ON; 579 else 580 swap_mode = SRC_ON; 581 } else { 582 if (mxc_fbi_to->cur_blank == FB_BLANK_UNBLANK) 583 swap_mode = TGT_ON; 584 else 585 swap_mode = BOTH_OFF; 586 } 587 588 /* tvout di-1: for DC use UYVY, for DP use RGB */ 589 if (mxc_fbi_from->ipu_di == 1 && ch_to == MEM_DC_SYNC) { 590 fbi->var.bits_per_pixel = 16; 591 fbi->var.nonstd = IPU_PIX_FMT_UYVY; 592 } else if (mxc_fbi_from->ipu_di == 1 && ch_to == MEM_BG_SYNC) { 593 fbi->var.nonstd = 0; 594 } else if (mxc_fbi_from->ipu_di == 0 && ch_to == MEM_DC_SYNC) { 595 fbi_to->var.nonstd = 0; 596 } else if (mxc_fbi_from->ipu_di == 0 && ch_to == MEM_BG_SYNC) { 597 fbi->var.bits_per_pixel = 16; 598 fbi->var.nonstd = IPU_PIX_FMT_UYVY; 599 } 600 601 switch (swap_mode) { 602 case BOTH_ON: 603 /* disable target->switch src->enable target */ 604 _swap_channels(fbi, fbi_to, true); 605 break; 606 case SRC_ON: 607 /* just switch src */ 608 _swap_channels(fbi, fbi_to, false); 609 break; 610 case TGT_ON: 611 /* just switch target */ 612 _swap_channels(fbi_to, fbi, false); 613 break; 614 case BOTH_OFF: 615 /* switch directly, no more need to do */ 616 mxc_fbi_to->ipu_ch = mxc_fbi_from->ipu_ch; 617 mxc_fbi_from->ipu_ch = ch_to; 618 i = mxc_fbi_from->ipu_ch_irq; 619 mxc_fbi_from->ipu_ch_irq = mxc_fbi_to->ipu_ch_irq; 620 mxc_fbi_to->ipu_ch_irq = i; 621 break; 622 default: 623 break; 624 } 625 626 if (ipu_request_irq(mxc_fbi_from->ipu_ch_irq, mxcfb_irq_handler, 0, 627 MXCFB_NAME, fbi) != 0) { 628 dev_err(fbi->device, "Error registering irq %d\n", 629 mxc_fbi_from->ipu_ch_irq); 630 return -EBUSY; 631 } 632 ipu_disable_irq(mxc_fbi_from->ipu_ch_irq); 633 if (ipu_request_irq(mxc_fbi_to->ipu_ch_irq, mxcfb_irq_handler, 0, 634 MXCFB_NAME, fbi_to) != 0) { 635 dev_err(fbi_to->device, "Error registering irq %d\n", 636 mxc_fbi_to->ipu_ch_irq); 637 return -EBUSY; 638 } 639 ipu_disable_irq(mxc_fbi_to->ipu_ch_irq); 640 641 return 0; 642 }
555~558 交换只发生在MEM_DC_SYNC和MEM_BG_SYNC之间,如果源fb的通道是MEM_BG_SYNC,那么我们希望交换为MEM_DC_SYNC,反之亦然。
560~567 找到目标通道坐在的framebuffer
576~586 swap_mode记录下source和dest framebuffer的blank状态
571~574 先释放from和to的ipu 通道中断号,因为IPU通道中断号也会随着通道一起交换
589~599 I have no idea
601~613 _swap_channels进行交换
614~620 如果fbi_from和fbi_to都处于BLANK状态,那么直接交换通道即可,注意通道中断号也需要交换,因为通道和通道中断号是一一对应的。
626~642 重新为fbi_from和fbi_to申请ipu irq,这里disable了irq,不过不要紧,当pan_display操作时,会使能ipu irq
swap_channels 504 static int _swap_channels(struct fb_info *fbi, 505 struct fb_info *fbi_to, bool both_on) 506 { 507 int retval, tmp; 508 ipu_channel_t old_ch; 509 struct mxcfb_info *mxc_fbi_from = (struct mxcfb_info *)fbi->par; 510 struct mxcfb_info *mxc_fbi_to = (struct mxcfb_info *)fbi_to->par; 511 512 if (both_on) { 513 ipu_disable_channel(mxc_fbi_to->ipu_ch, true); 514 ipu_uninit_channel(mxc_fbi_to->ipu_ch); 515 } 516 517 /* switch the mxc fbi parameters */ 518 old_ch = mxc_fbi_from->ipu_ch; 519 mxc_fbi_from->ipu_ch = mxc_fbi_to->ipu_ch; 520 mxc_fbi_to->ipu_ch = old_ch; 521 tmp = mxc_fbi_from->ipu_ch_irq; 522 mxc_fbi_from->ipu_ch_irq = mxc_fbi_to->ipu_ch_irq; 523 mxc_fbi_to->ipu_ch_irq = tmp; 524 525 _setup_disp_channel1(fbi); 526 retval = _setup_disp_channel2(fbi); 527 if (retval) 528 return retval; 529 530 /* switch between dp and dc, disable old idmac, enable new idmac */ 531 retval = ipu_swap_channel(old_ch, mxc_fbi_from->ipu_ch); 532 ipu_uninit_channel(old_ch); 533 534 if (both_on) { 535 _setup_disp_channel1(fbi_to); 536 retval = _setup_disp_channel2(fbi_to); 537 if (retval) 538 return retval; 539 ipu_enable_channel(mxc_fbi_to->ipu_ch); 540 } 541 542 return retval; 543 }
504~505 函数有三个参数,@fbi是source,@fbi_to是dest,@both_on 布尔量为true 则@fbi_to为UNBLANK状态;false,则@fbi_to为BLANK状态。这里@fbi一定为UNBLANK状态,否则不会调用这个函数。
512~515 在交换之前,先销毁掉@fbi_to的channel
518~523 交换二者的通道和通道irq
525~528 交换完成后,设置disp通道的参数。
531~532 现在,通道号已经交换完成,但是还有一件事没有完成,我们知道DC通道1,5的控制寄存器DC_WR_CH_CONF_x中,配置着这个DC通道连接到哪一个DI,我们希望交换通道,但是不希望更改到DI的配置。ipu_swap_channel交换两个DC通道的DI配置。