MX51 supports three framebuffers: fb0, Fb1, and fb2.
/Dev/graphics/fb0,/dev/graphics/Fb1:
One is the primary framebuffer of the system and the other is the framebuffer output by tvout. By default, fb0 performs primary (that is, used to display the UI on the primary LCD ), fb1 performs secondary (that is, it is used to display data on the tvout output ).
Based on the actual physical connection in the project, if the primary LCD is connected to disp0, use the default settings. If the primary LCD is connected to disp1, add di1_primary to the kernel startup parameter.
/Dev/graphics/fb2:
Framebuffer used for overlay and video data display. It only supports bpp16 and the data format is yuv422. It can be synthesized in dp (display process) and data from mem_bg_sync, then it is sent to DC/di.
In mxc framebuffer probe, the first registered mxc framebuffer (fb0 or Fb1, this framebuffer serves as the framebuffer of the main LCD) uses mem_bg_sync, and the second registered mxc framebuffer (fb0 or Fb1, this framebuffer acts as the framebuffer used by tvout) uses mem_dc_sync, And the last registered framebuffer (fb2) uses mem_fg_sync.
Mem_bg_sync and mem_fg_sync are unique resources in mx5x IPU, which means that each channel can only be occupied by one of the framebuffer. In fact, the mem_dc_sync channel does not exist, but is used to directly connect to DC.
Note that the data in these framebuffer is not directly connected to the display device, and there may be a DP processing unit of MX51 IPU in the middle. DP Unit is responsible for processing the data from fb0 Fb1 fb2, including color conversion, layer merging, Gamma processing, and cursor processing are sent to DC-> Di-> display device.
FSL creates a FSL-Specific Property file fsl_disp_property under/sys/class/graphics/fbx/. The following is displayed on my machine:
# cat /sys/class/graphics/fb0/fsl_disp_property2-layer-fb-bg# cat /sys/class/graphics/fb1/fsl_disp_property1-layer-fb# cat /sys/class/graphics/fb2/fsl_disp_property2-layer-fb-fg
2-layer-fb-bg, indicating that fb0 uses the mem_bg_sync channel, merges data from the display process module of IPU and mem_fg_sync channel, and then sends the data to the DC.
1-layer-fb indicates that Fb1 uses the mem_dc_sync channel and directly connects to di (display interface) without passing through the display process module)
2-layber-fb-fg, indicating that fb2 uses the mem_fg_sync channel, merges data from the display process module of IPU and mem_bg_sync channel, and then sends the data to the DC.
You can also modify/sys/class/graphics/fb0/fsl_disp_property and/sys/class/graphics/Fb1/fsl_disp_property at the application layer by running the following command:
Echo 1-layer-fb>/sys/class/graphics/fb0/fsl_disp_property or ECHO 2-layer-fb-bg>/sys/class/graphics/Fb1/fsl_disp_property
Note that the two functions are the same, and fb0 and Fb1 exchange display channels. The purpose of FSL to introduce this attribute is to provide an interface for upper-layer applications, this allows the application layer to control whether fb0 Fb1 uses the mem_bg_sync or mem_dc_sync channel, which may be confusing. Why should the application layer select the fb0 Fb1 channel? First, we need to clarify the differences between mem_bg_sync and mem_dc_sync.
Imx51/imx53 IPU supports three types of display channels: mem_bg_sync, mem_fg_sync, and mem_dc_sync. framebuffer can only transmit data through one of the three channels, which are physically unique, only one framebuffer can be used at a time.
MEM_BG_SYNC:fb0 -> DP (full plane) -> DC -> DI0MEM_FG_SYNC:fb2 -> DP (partial plane) -> DC -> DI0MEM_DC_SYNC:fb1 -> DC -> DI1
Mem_bg_sync and mem_fg_sync are merged in DP. This function is called overlay.
Normally, the UI from fb0 and the video data from fb2 are merged in DP and sent to di0. Then we can see the effect of the UI floating on the video. However, when activating a dual-screen, you usually want to send the video data and UI to the back-screen (di1). The front screen only displays the UI. In this case, Fb1 needs to have mem_bg_sync. In this case, the display channels of Fb1 and fb0 need to be exchanged as follows:
MEM_BG_SYNC: fb1 -> DP (full plane) -> DC -> DI0MEM_FG_SYNC: fb2 -> DP (partial plane) -> DC -> DI1MEM_DC_SYNC: fb0 -> DC -> DI0
Channel Exchange Code
swap_disp_chan1673 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 else1695 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);
1682 ~ 1686 only fb0 and Fb1 are involved in the exchange of display channels. fb2 only uses mem_fg_sync
1705 call swap_channels to implement channel Switching
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 }
550 mxc_fbi_from has the same meaning as the word surface. Which fb_info do we want to exchange, that is, the exchange source?
555 ~ 558 the exchange only occurs between mem_dc_sync and mem_bg_sync. If the source FB channel is mem_bg_sync, we want to switch to mem_dc_sync, and vice versa.
560 ~ 567 find the framebuffer in which the target channel is located.
576 ~ 586 swap_mode: record the blank status of source and DEST framebuffer
571 ~ 574 first release the IPU channel interrupt Number of from and to, because the IPU channel interrupt number will also be exchanged with the Channel
589 ~ 599 I have no idea
601 ~ 613 _ swap_channels for exchange
614 ~ 620 if both fbi_from and fbi_to are in the blank state, you can directly switch the channel. Note that the channel interruption number must also be exchanged because the channel and channel interruption number are one-to-one.
626 ~ 642 apply for ipu irq again for fbi_from and fbi_to. Here IRQ is disable, but it doesn't matter. When the pan_display operation is performed, ipu irq will be available.
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 the function has three parameters: @ FBI is source, @ fbi_to is DEST, @ both_on is true, then @ fbi_to is unblank; false, then @ fbi_to is blank. Here @ FBI must be in the unblank state, otherwise this function will not be called.
512 ~ 515 destroy the @ fbi_to channel before switching
518 ~ 523 exchange channel and channel IRQ
525 ~ 528 after the switch is complete, set the disp channel parameters.
531 ~ 532 now, the channel number has been switched, but one more thing has not been done yet. We know which di the DC channel connects to in the control register dc_wr_ch_conf_x of DC channel, we want to switch the channel, but do not want to change the configuration to Di. Ipu_swap_channel switches the di configuration of two DC channels.