Patch to support MPU 8080 LCD on iMX6UL and iMX7D

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Patch to support MPU 8080 LCD on iMX6UL and iMX7D

No ratings

Patch to support MPU 8080 LCD on iMX6UL and iMX7D

1. Description

    These patches are used to support MPU 8080 LCD on L3.14.52_1.1.0_GA BSP.
    They are based on ELCDIF hardware module, iMX6UL and iMX7D is the reference platform.

 

2. File List
-- 0001-Add-ST7789S-MPU-LCD-support-for-iMX6UL-board.patch
   Patch to support MPU display for iMX6UL, ST7789S 240*320 panel is the example.

 

-- 0002-Add-ST7735R-MPU-LCD-support-for-iMX7D-board.patch
   Patch to support MPU display for iMX7D, ST7735R 128*128 panel is the example.

 

-- readme.txt
   this file, please refer to it before use the patches

 

3. Requirement

- iMX6UL EVK board or iMX7D SabreSD board.

- L3.14.52_1.1.0_GA kernel.

 

4. How to use

-- Copy the patch files to kernel folder.
    $ cd ~/L3.14.52_GA1.1.0/build-imx7dsabresd-X11/tmp/work/imx7dsabresd-poky-linux-gnueabi/linux-imx/3.14.52-r0/git
    $ git apply ./0001-Add-ST7789S-MPU-LCD-support-for-iMX6UL-board.patch
    $ git apply ./0002-Add-ST7735R-MPU-LCD-support-for-iMX7D-board.patch

 

-- Build the new kernel image:
    $ cd ~/L3.14.52_GA1.1.0/build-imx7dsabresd-X11/tmp/work/imx7dsabresd-poky-linux-gnueabi/linux-imx/3.14.52-r0/git
    $ export CROSS_COMPILE=~/L3.14.52_GA1.1.0/build-imx7dsabresd-X11/tmp/sysroots/x86_64-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-
    $ export ARCH=arm
    $ make imx_v7_defconfig
    $ make zImage
    $ make dtbs

 

5. How to add a new MPU panel
    1) in dts file, such as imx6ul-14x14-evk-i80lcd.dts, update the panel name "lcd_panel",
       update the PINs in "pinctrl_lcdif_dat" and "pinctrl_lcdif_ctrl" for the new panel,
       the reset and rs PINs can be from GPIO pin, lcd_reset_gpio and lcd_rs_gpio.

&lcdif {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lcdif_dat
       &pinctrl_lcdif_ctrl>;
display = <&display0>;
status = "okay";

display0: display {
  mpu-mode;
  lcd_reset_gpio = <&gpio3 14 0>;
  lcd_panel = "ST7789S-QVGA";
};
};

 

    2) Reference to "mxsfb_st7789s_qvga.c", add a new panel driver code.

 

    3) Add the new panel support in Makefile and Kconfig under "drivers/video/mxc/"

 

    4) Add the new panel support in file "mxsfb.c" and "mxsfb.h"

 

    5) Add the new panel support in default kernel config file "imx_v7_defconfig"

 

Note: mpu_lcd_fb_test.tar.gz is the test application, for 8080 display, it is not sync display, so software need call ioctl to refresh the LCD.

 

 

2016-08-02: Add the uboot reference patch for iMX7D.

File: L3.14.52_Uboot_mpu_display.patch

 

Attachments
Comments

I am porting MPU 8080 LCD on IMX6UL evk.  This patch is very useful.  Do you have u-boot patch too?  Thank you.

Hi Qiang,

If application have any update framebuffer then need call ioctl to refresh the LCD manually.

How to let it refresh panel automatically?  Thank you.

The MPU 8080 display is async panel, so you only need refresh the panel when UI was changed.

Can use DMA chain to refresh panel continuously?  But IMX6 seems to not support MXS_DMA_CHANNEL_AHB_APBH_LCDIF?

Thanks.

Does this patch exist for a more recent kernel? And is the patch maintained in a git repository somewhere?

The default BSP doesn't support i80 display due to no such hardware on the reference board. The patch is maintained in this link, you can porting it for your own MPU display.

What changes need to be done to enable MPU interface in i.MX6 solo.?

Hi shree97, iMX6 solo is IPU based, not LCDIF based, the hardware is different.

Maybe you can reference to this one, it is for iMX51: https://community.nxp.com/docs/DOC-172940

Thanks for your reply.

Can you please point me to some reference driver which drives the LCD using GPIO's?

Sorry, we don't have reference driver with GPIO to generate the MPU display signals.

Thanks for quick reply.

Hi Li Qiang,

I apply your patch to kernel version imx_4.1.15_2.0.0_ga, and when I run driver I face below error:
 mxsfb 21c8000.lcdif: failed to find mxc display driver

And when I run test app, I face below error:

 mxsfb 21c8000.lcdif: mxsfb_mpu_wait_for_ready timeout!

Do you have any idea about these two error?

Thanks

I think you have't successfully added your panel to mxsfb.c

Thanks for your reply! I think the error of " mxsfb 21c8000.lcdif: failed to find mxc display driver" is because of below code did not get the "disp-dev" from device tree, and in your  device tree also has no the definition of "disp-dev", why do you not get this error?

ret = of_property_read_string(np, "disp-dev", &disp_dev);
if (!ret) {
memcpy(host->disp_dev, disp_dev, strlen(disp_dev));
/* Timing is from encoder driver */
goto put_display_node;
}

static void mxsfb_dispdrv_init(struct platform_device *pdev,
struct fb_info *fbi)
{
struct mxsfb_info *host = to_imxfb_host(fbi);
struct mxc_dispdrv_setting setting;
struct device *dev = &pdev->dev;
char disp_dev[32];

memset(&setting, 0x0, sizeof(setting));
setting.fbi = fbi;
memcpy(disp_dev, host->disp_dev, strlen(host->disp_dev));
disp_dev[strlen(host->disp_dev)] = '\0';

host->dispdrv = mxc_dispdrv_gethandle(disp_dev, &setting);
if (IS_ERR(host->dispdrv)) {
host->dispdrv = NULL;
dev_info(dev, "failed to find mxc display driver %s\n",
disp_dev);
} else {
dev_info(dev, "registered mxc display driver %s\n",
disp_dev);
}
}

Hello,

 

we need to port the Yocto Project BSP to a custom board based mainly on imx6ul evk.

 

Our board includes a 16bit TFT display with a SITRONIX ST7789V controller.

 

We are working with the following BSP version: fsl-yocto-L4.1.15_2.0.0-ga.

We tried to apply the patch to this kernel, but the LCD shows images on only one quarter of the total area, but the resolution is the same 240x320, and the colors appear not correctly.

The only modification with respect of the above patch was that in the  mxsfb_st7789s_qvga.c source files, because we have 16 bit (16 LCDIF_DATA pins) connected to the display.

static struct mpu_lcd_config lcd_config = {
.bus_mode = MPU_BUS_8080,
.interface_width = 16,
.panel_bpp = 16,
};

We also change the dts to use 16 bit LCD DATA bits:

pinctrl_lcdif_dat: lcdifdatgrp {
fsl,pins = <
MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79
MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79
MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x79
MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x79
MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x79
MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x79
MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x79
MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x79
MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x79
MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x79
MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x79
MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x79
MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x79
MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x79
MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x79
MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x79
>;
};

Have you, some ideas to fix this?

 

Thanks in advance for any helpful answer.

 

Best Regards,

Francesco.

Hi Francesco, although you are using the same controller st7789, but the initialization for each panel is not same, you need check with your panel vendor and update the code in mpu_st7789s_lcd_setup().

Thanks.

I updated the code according to panel vendor indications in this way:

int mpu_st7789s_lcd_setup(struct mxsfb_info * mxsfb)
{
if (mxsfb == NULL)
return -1;

/* Sleep out */
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x11);

//------------------------------display and color format setting--------------------------------//
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x36);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x3a);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x06);

//--------------------------------ST7789S Frame rate setting----------------------------------//
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xb2);zImage-sd
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0c);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0c);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x33);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x33);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xb7);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x35);
//---------------------------------ST7789S Power setting--------------------------------------//
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xbb);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x28);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xc0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x2c);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xc2);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x01);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xc3);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x10);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xc4);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x20);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xc6);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0f);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xd0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xa4);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xa1);

//--------------------------------ST7789S gamma setting---------------------------------------//
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xe0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xd0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x02);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x07);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0a);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x28);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x32);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x44);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x42);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x06);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0e);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x12);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x14);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x17);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xe1);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xd0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x02);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x07);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0a);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x28);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x31);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x54);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x47);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0e);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x1c);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x17);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x1b);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x1e);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x29);

//mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x38);

/* Memory write */
//mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x2C);

return 0;
}

I tried to change the bus_width and bits per pixel in the mxsfb_st7789s_qvga in two ways but in one case (16bbp 16 width) i obtained the same results as before, in the case 18bbp 16 width i obtained a grey image with wrong resolution.

I found the right configurations for the colors, but the test application fb_test.c, shows the RGB pattern on one quarter of the display, and then it is replicated in the others 3 quarter.

Now my file mxsfb_st7789s_qvga.c is as follows:

static struct fb_videomode st7789s_lcd_modedb[] = {
{
"ST7789S-QVGA", 60, 240, 320, 200000,
0, 0,
0, 0,
0, 0,
0,
FB_VMODE_NONINTERLACED,
0,
},
};

static struct mpu_lcd_config lcd_config = {
.bus_mode = MPU_BUS_8080,
.interface_width = 16,
.panel_bpp = 18,
};


void mpu_st7789s_get_lcd_videomode(struct fb_videomode **mode, int *size,
struct mpu_lcd_config **data)
{
*mode = &st7789s_lcd_modedb[0];
*size = ARRAY_SIZE(st7789s_lcd_modedb);
*data = &lcd_config;
}


int mpu_st7789s_lcd_setup(struct mxsfb_info * mxsfb)
{
if (mxsfb == NULL)
return -1;

/* Sleep out */
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x11);

//------------------------------display and color format setting--------------------------------//
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x36);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x3a);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x06);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xb0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xF2);

//--------------------------------ST7789S Frame rate setting----------------------------------//
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xb2);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0c);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0c);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x33);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x33);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xb7);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x35);
//---------------------------------ST7789S Power setting--------------------------------------//
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xbb);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x28);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xc0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x2c);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xc2);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x01);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xc3);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x10);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xc4);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x20);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xc6);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0f);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xd0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xa4);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xa1);

//--------------------------------ST7789S gamma setting---------------------------------------//
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xe0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xd0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x02);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x07);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0a);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x28);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x32);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x44);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x42);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x06);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0e);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x12);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x14);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x17);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xe1);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xd0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x02);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x07);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0a);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x28);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x31);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x54);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x47);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0e);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x1c);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x17);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x1b);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x1e);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x29);

/* Memory write */
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x2C);

return 0;
}


int mpu_st7789s_lcd_poweroff(struct mxsfb_info * mxsfb)
{
/* Display off */
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x28);

/* Sleep in */
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x10);

return 0;
}

Finally we solved our problem and the display driver works.

The right configuration was the last, with 18 bpp and 16 bit bus width, but to apply the patch correctly we had to download the  L3.14.52_1.1.0_GA NXP Kernel.

We applied the patch to the old kernel then, we simply copied the patched files, to the right paths, to the folder of the fsl-yocto-L4.1.15_2.0.0-ga after menuconfig command via bitbake.

Thank you, very much for the help.

Hi Sir,

My customer is targeting to port MPU 8080 LCD on i.MX7D with Android BSP.

1. Can you share me the schematic about Patch to MPU display on i.MX6UL with ST7789S panel and i.MX7D with ST7735R panel?? 

0001-Add-ST7789S-MPU-LCD-support-for-iMX6UL-board.patch
   Patch to support MPU display for iMX6UL, ST7789S 240*320 panel is the example.

-- 0002-Add-ST7735R-MPU-LCD-support-for-iMX7D-board.patch
   Patch to support MPU display for iMX7D, ST7735R 128*128 panel is the example.

2. Your SW patch is based Linux BSP. We would like migrate this to Android BSP. Any potential issue or comment??

Thanks and Best regards,

Carl

Hi Carl,

The hardware link is simple, you can reference to this link, https://community.nxp.com/thread/265022

The LCDIF is similar for iMX50 and iMX7D.

The patch is for kernel, so it is easy to porting to Android kernel. But I think you need take care of the screen refresh, the i80 display is not auto-refresh display.

Hi,

I'm using this patch for imx6ul and ili9488 smart based lcd, using 320x480 resolution 16 bit bus with 16bpp. It seems something is not right.

Picture is one 1/4 of display area, multiplied 4 times. Line length is 960 if  I debug code..odd.

Is this patch fully working for this kind of platform? 

Br, Juha

Try with the following modified sources.

I also added a mechanism on the driver to automatically refresh the display with a timer.

mxsfb.c

/*
* Copyright (C) 2010 Juergen Beisert, Pengutronix
*
* This code is based on:
* Author: Vitaly Wool <vital@embeddedalley.com>
*
* Copyright 2008-2015 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#define DRIVER_NAME "mxsfb"

/**
* @file
* @brief LCDIF driver for i.MX23 and i.MX28
*
* The LCDIF support four modes of operation
* - MPU interface (to drive smart displays) -> not supported yet
* - VSYNC interface (like MPU interface plus Vsync) -> not supported yet
* - Dotclock interface (to drive LC displays with RGB data and sync signals)
* - DVI (to drive ITU-R BT656) -> not supported yet
*
* This driver depends on a correct setup of the pins used for this purpose
* (platform specific).
*
* For the developer: Don't forget to set the data bus width to the display
* in the imx_fb_videomode structure. You will else end up with ugly colours.
* If you fight against jitter you can vary the clock delay. This is a feature
* of the i.MX28 and you can vary it between 2 ns ... 8 ns in 2 ns steps. Give
* the required value in the imx_fb_videomode structure.
*/

#include <linux/busfreq-imx.h>
#include <linux/console.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/pinctrl/consumer.h>
#include <linux/fb.h>
#include <linux/mxcfb.h>
#include <linux/regulator/consumer.h>
#include <video/of_display_timing.h>
#include <video/videomode.h>
#include <linux/uaccess.h>
#include <linux/timer.h>

#include "mxc/mxc_dispdrv.h"
#include "mxsfb.h"

struct mxsfb_info *global_host;
static int mxsfb_driver_ok = 0;
static struct timer_list refresh_timer;

static struct mpu_match_lcd mpu_lcd_db[] = {
#ifdef CONFIG_FB_MXS_ST7789S_QVGA
{
"ST7789S-QVGA",
{mpu_st7789s_get_lcd_videomode, mpu_st7789s_lcd_setup, mpu_st7789s_lcd_poweroff}
},
#endif
{
"", {NULL, NULL}
}
};

#define mxsfb_is_v3(host) (host->devdata->ipversion == 3)
#define mxsfb_is_v4(host) (host->devdata->ipversion == 4)

static const struct mxsfb_devdata mxsfb_devdata[] = {
[MXSFB_V3] = {
.transfer_count = LCDC_V3_TRANSFER_COUNT,
.data = LCDC_V3_DATA,
.cur_buf = LCDC_V3_CUR_BUF,
.next_buf = LCDC_V3_NEXT_BUF,
.debug0 = LCDC_V3_DEBUG0,
.hs_wdth_mask = 0xff,
.hs_wdth_shift = 24,
.ipversion = 3,
},
[MXSFB_V4] = {
.transfer_count = LCDC_V4_TRANSFER_COUNT,
.data = LCDC_V4_DATA,
.cur_buf = LCDC_V4_CUR_BUF,
.next_buf = LCDC_V4_NEXT_BUF,
.debug0 = LCDC_V4_DEBUG0,
.hs_wdth_mask = 0x3fff,
.hs_wdth_shift = 18,
.ipversion = 4,
},
};

#define to_imxfb_host(x) (container_of(x, struct mxsfb_info, fb_info))

static int mxsfb_map_videomem(struct fb_info *info);
static int mxsfb_unmap_videomem(struct fb_info *info);
static int mxsfb_set_par(struct fb_info *fb_info);

/* enable lcdif pix clock */
static inline void clk_enable_pix(struct mxsfb_info *host)
{
if (!host->clk_pix_enabled && (host->clk_pix != NULL)) {
clk_prepare_enable(host->clk_pix);
host->clk_pix_enabled = true;
}
}

/* disable lcdif pix clock */
static inline void clk_disable_pix(struct mxsfb_info *host)
{
if (host->clk_pix_enabled && (host->clk_pix != NULL)) {
clk_disable_unprepare(host->clk_pix);
host->clk_pix_enabled = false;
}
}

/* enable lcdif axi clock */
static inline void clk_enable_axi(struct mxsfb_info *host)
{
if (!host->clk_axi_enabled && (host->clk_axi != NULL)) {
clk_prepare_enable(host->clk_axi);
host->clk_axi_enabled = true;
}
}

/* disable lcdif axi clock */
static inline void clk_disable_axi(struct mxsfb_info *host)
{
if (host->clk_axi_enabled && (host->clk_axi != NULL)) {
clk_disable_unprepare(host->clk_axi);
host->clk_axi_enabled = false;
}
}

/* enable DISP axi clock */
static inline void clk_enable_disp_axi(struct mxsfb_info *host)
{
if (!host->clk_disp_axi_enabled && (host->clk_disp_axi != NULL)) {
clk_prepare_enable(host->clk_disp_axi);
host->clk_disp_axi_enabled = true;
}
}

/* disable DISP axi clock */
static inline void clk_disable_disp_axi(struct mxsfb_info *host)
{
if (host->clk_disp_axi_enabled && (host->clk_disp_axi != NULL)) {
clk_disable_unprepare(host->clk_disp_axi);
host->clk_disp_axi_enabled = false;
}
}

/* mask and shift depends on architecture */
static inline u32 set_hsync_pulse_width(struct mxsfb_info *host, unsigned val)
{
return (val & host->devdata->hs_wdth_mask) <<
host->devdata->hs_wdth_shift;
}

static inline u32 get_hsync_pulse_width(struct mxsfb_info *host, unsigned val)
{
return (val >> host->devdata->hs_wdth_shift) &
host->devdata->hs_wdth_mask;
}

static const struct fb_bitfield def_rgb565[] = {
[RED] = {
.offset = 11,
.length = 5,
},
[GREEN] = {
.offset = 5,
.length = 6,
},
[BLUE] = {
.offset = 0,
.length = 5,
},
[TRANSP] = { /* no support for transparency */
.length = 0,
}
};

static const struct fb_bitfield def_rgb666[] = {
[RED] = {
.offset = 16,
.length = 6,
},
[GREEN] = {
.offset = 8,
.length = 6,
},
[BLUE] = {
.offset = 0,
.length = 6,
},
[TRANSP] = { /* no support for transparency */
.length = 0,
}
};

static const struct fb_bitfield def_rgb888[] = {
[RED] = {
.offset = 16,
.length = 8,
},
[GREEN] = {
.offset = 8,
.length = 8,
},
[BLUE] = {
.offset = 0,
.length = 8,
},
[TRANSP] = { /* no support for transparency */
.length = 0,
}
};

#define bitfield_is_equal(f1, f2) (!memcmp(&(f1), &(f2), sizeof(f1)))

static inline bool pixfmt_is_equal(struct fb_var_screeninfo *var,
const struct fb_bitfield *f)
{
if (bitfield_is_equal(var->red, f[RED]) &&
bitfield_is_equal(var->green, f[GREEN]) &&
bitfield_is_equal(var->blue, f[BLUE]))
return true;

return false;
}

static inline unsigned chan_to_field(unsigned chan, struct fb_bitfield *bf)
{
chan &= 0xffff;
chan >>= 16 - bf->length;
return chan << bf->offset;
}

static int mxsfb_mpu_wait_for_ready(struct mxsfb_info *host)
{
unsigned int val;
int timeout = 0;

// Check for running
val = readl(host->base + LCDC_CTRL);
while(val & CTRL_RUN) {
mdelay(1);
timeout ++;
if (timeout >= 1000) {
dev_err(&host->pdev->dev, "mxsfb_mpu_wait_for_ready timeout!\n");
return -ETIME;
}
val = readl(host->base + LCDC_CTRL);
}

return 0;
}
unsigned int mxsfb_mpu_access(struct mxsfb_info *host, int mode, int rw, int data)
{
unsigned int val, wordlen, ret = 0;

if (mxsfb_mpu_wait_for_ready(host) != 0)
return 0;

writel(CTRL_MASTER,
host->base + LCDC_CTRL + REG_CLR);

writel(CTRL1_BYTE_PACKING_FORMAT_MASK,
host->base + LCDC_CTRL1 + REG_CLR);
val = readl(host->base + LCDC_CTRL);
wordlen = CTRL_GET_WORD_LENGTH(val);
writel(CTRL_WORD_LENGTH_MASK,
host->base + LCDC_CTRL + REG_CLR);

writel(CTRL_YCBCR422_INPUT |
CTRL_INPUT_DATA_SWIZZLE_MASK,
host->base + LCDC_CTRL + REG_CLR);

switch (host->mpu_lcd_sigs->interface_width)
{
case 8:
writel((0x1 << CTRL1_BYTE_PACKING_FORMAT_OFFSET),
host->base + LCDC_CTRL1 + REG_SET);
writel(CTRL_WORD_LENGTH_8BIT,
host->base + LCDC_CTRL + REG_SET);
break;
case 16:
writel((0x3 << CTRL1_BYTE_PACKING_FORMAT_OFFSET),
host->base + LCDC_CTRL1 + REG_SET);
writel(CTRL_WORD_LENGTH_16BIT,
host->base + LCDC_CTRL + REG_SET);
break;
}

val = readl(host->base + host->devdata->transfer_count);
val &= ~(TRANSFER_COUNT_H_COUNT_MASK |
TRANSFER_COUNT_V_COUNT_MASK);
val |= (1 << TRANSFER_COUNT_V_COUNT_OFFSET) |
(1 << TRANSFER_COUNT_H_COUNT_OFFSET);
writel(val, host->base + host->devdata->transfer_count);

if(mode == MPU_CMD)
{
if (host->mpu_lcd_sigs->lcd_rs_is_gpio)
gpio_set_value(host->mpu_lcd_sigs->lcd_rs_gpio, 0);
writel(CTRL_DATA_SELECT,
host->base + LCDC_CTRL + REG_CLR);
}
else
{
if (host->mpu_lcd_sigs->lcd_rs_is_gpio)
gpio_set_value(host->mpu_lcd_sigs->lcd_rs_gpio, 1);
writel(CTRL_DATA_SELECT,
host->base + LCDC_CTRL + REG_SET);
}

if(rw == MPU_READ)
{
writel(CTRL2_READ_MODE_NUM_PACKED_SUBWORDS_MASK,
host->base + LCDC_V4_CTRL2 + REG_CLR);
writel((0x1 << CTRL2_READ_MODE_NUM_PACKED_SUBWORDS_OFFSET),
host->base + LCDC_V4_CTRL2 + REG_SET);

writel(CTRL_READ_WRITEB,
host->base + LCDC_CTRL + REG_SET);
writel(CTRL_RUN,
host->base + LCDC_CTRL + REG_SET);
}
else
{
writel(CTRL_READ_WRITEB,
host->base + LCDC_CTRL + REG_CLR);
writel(CTRL_RUN,
host->base + LCDC_CTRL + REG_SET);

writel(data,
host->base + host->devdata->data);
}

val = readl(host->base + LCDC_CTRL);
while(val & CTRL_RUN)
{
if(rw == MPU_READ)
ret = readl(host->base + host->devdata->data);

val = readl(host->base + LCDC_CTRL);
}

writel(CTRL_MASTER,
host->base + LCDC_CTRL + REG_SET);

writel(CTRL_WORD_LENGTH_MASK,
host->base + LCDC_CTRL + REG_CLR);
writel((wordlen << CTRL_WORD_LENGTH_OFFSET),
host->base + LCDC_CTRL + REG_SET); // 32 bits valid data
writel(CTRL1_BYTE_PACKING_FORMAT_MASK,
host->base + LCDC_CTRL1 + REG_CLR);
writel((0xF << CTRL1_BYTE_PACKING_FORMAT_OFFSET),
host->base + LCDC_CTRL1 + REG_SET); // 32 bits valid data

writel(CTRL_MASTER,
host->base + LCDC_CTRL + REG_SET);

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 1\n");

// For idle, set LCD_RS to high
if (host->mpu_lcd_sigs->lcd_rs_is_gpio)
gpio_set_value(host->mpu_lcd_sigs->lcd_rs_gpio, 1);

return ret;
}

int mxsfb_mpu_refresh_panel(struct mxsfb_info *host)
{
if (mxsfb_mpu_wait_for_ready(host) != 0)
return -ETIME;

writel(CTRL_MASTER,
host->base + LCDC_CTRL + REG_SET);
writel(CTRL_RUN,
host->base + LCDC_CTRL + REG_SET);

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 2\n");

//mxsfb_driver_ok = 1;

return 0;
}

void refresh_timer_callback(unsigned long data)
{

switch(mxsfb_driver_ok) {
case 1:
mxsfb_mpu_refresh_panel(global_host);
mxsfb_driver_ok = 2;
printk(KERN_INFO "Display refresh driver starting...\n");
mod_timer(&refresh_timer, jiffies + msecs_to_jiffies(36000));
break;

case 2:
mxsfb_driver_ok = 3;
printk(KERN_INFO "Display refresh driver OK\n");

mod_timer(&refresh_timer, jiffies + msecs_to_jiffies(50));
break;

case 3:
mxsfb_mpu_refresh_panel(global_host);

/* Restarting the timer... */
mod_timer(&refresh_timer, jiffies + msecs_to_jiffies(50));
break;

default:
mod_timer(&refresh_timer, jiffies + msecs_to_jiffies(50));
break;
}

return;
}

void mxsfb_mpu_setup_refresh_data(struct mxsfb_info *host)
{
unsigned int val;

if (mxsfb_mpu_wait_for_ready(host) != 0)
return;

val = readl(host->base + host->devdata->transfer_count);
val &= ~(TRANSFER_COUNT_H_COUNT_MASK |
TRANSFER_COUNT_V_COUNT_MASK);
val |= (host->var.xres << TRANSFER_COUNT_H_COUNT_OFFSET) |
(host->var.yres << TRANSFER_COUNT_V_COUNT_OFFSET);
writel(val, host->base + host->devdata->transfer_count);

writel(CTRL_READ_WRITEB,
host->base + LCDC_CTRL + REG_CLR);
writel(CTRL_BYPASS_COUNT,
host->base + LCDC_CTRL + REG_CLR);
writel(CTRL_DATA_SELECT,
host->base + LCDC_CTRL + REG_SET);
if (host->mpu_lcd_sigs->lcd_rs_is_gpio)
gpio_set_value(host->mpu_lcd_sigs->lcd_rs_gpio, 1);

//2018_02_19
/*if (host->mpu_lcd_sigs->panel_bpp == 16) {
writel(CTRL_YCBCR422_INPUT |
(1 << CTRL_INPUT_DATA_SWIZZLE_OFFSET),
host->base + LCDC_CTRL + REG_SET);
}*/

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 3\n");


}

static void mxsfb_mpu_setup_interface(struct mxsfb_info *host)
{
writel(CTRL_RUN, host->base + LCDC_CTRL + REG_CLR);
writel(CTRL_MASTER, host->base + LCDC_CTRL + REG_CLR);

/* Setup the bus protocol */
if (host->mpu_lcd_sigs->bus_mode == MPU_BUS_8080)
writel(CTRL1_MODE86,
host->base + LCDC_CTRL1 + REG_CLR);
else
writel(CTRL1_MODE86,
host->base + LCDC_CTRL1 + REG_SET);

writel(CTRL1_BUSY_ENABLE,
host->base + LCDC_CTRL1 + REG_CLR);

/* Take display out of reset */
writel(CTRL1_RESET,
host->base + LCDC_CTRL1 + REG_SET);
if (host->mpu_lcd_sigs->lcd_reset_is_gpio)
gpio_set_value(host->mpu_lcd_sigs->lcd_reset_gpio, 1);

/* VSYNC is an input by default */
writel(VDCTRL0_VSYNC_OEB,
host->base + LCDC_VDCTRL0 + REG_SET);

/*
* Make sure we do a high-to-low transition to reset the panel.
* First make it low for 100 msec, hi for 10 msec, low for 10 msec,
* then hi.
*/
writel(CTRL1_RESET,
host->base + LCDC_CTRL1 + REG_CLR); /* low */
if (host->mpu_lcd_sigs->lcd_reset_is_gpio)
gpio_set_value(host->mpu_lcd_sigs->lcd_reset_gpio, 0);

msleep(100);

writel(CTRL1_RESET,
host->base + LCDC_CTRL1 + REG_SET); /* high */
if (host->mpu_lcd_sigs->lcd_reset_is_gpio)
gpio_set_value(host->mpu_lcd_sigs->lcd_reset_gpio, 1);

msleep(10);

writel(CTRL1_RESET,
host->base + LCDC_CTRL1 + REG_CLR); /* low */
if (host->mpu_lcd_sigs->lcd_reset_is_gpio)
gpio_set_value(host->mpu_lcd_sigs->lcd_reset_gpio, 0);

/* For some panel, Reset must be held low at least 30 uSec
* Therefore, we'll hold it low for about 10 mSec just to be sure.
* Then we'll wait 1 mSec afterwards.
*/
msleep(10);
writel(CTRL1_RESET,
host->base + LCDC_CTRL1 + REG_SET); /* high */
if (host->mpu_lcd_sigs->lcd_reset_is_gpio)
gpio_set_value(host->mpu_lcd_sigs->lcd_reset_gpio, 1);
msleep(1);

writel(CTRL_DATA_SHIFT_DIR,
host->base + LCDC_CTRL + REG_CLR);

writel(CTRL_SHIFT_NUM_BITS_MASK,
host->base + LCDC_CTRL + REG_CLR);

writel(CTRL2_OUTSTANDING_REQS_MASK,
host->base + LCDC_V4_CTRL2 + REG_CLR);
writel(CTRL2_OUTSTANDING_REQS_REQ_8,
host->base + LCDC_V4_CTRL2 + REG_SET);

/* Recover on underflow */
writel(CTRL1_RECOVERY_ON_UNDERFLOW,
host->base + LCDC_CTRL1 + REG_SET);

/* Configure the input pixel format */
writel(CTRL_YCBCR422_INPUT |
CTRL_WORD_LENGTH_MASK |
CTRL_INPUT_DATA_SWIZZLE_MASK |
CTRL_DATA_FORMAT_16_BIT |
CTRL_DATA_FORMAT_18_BIT |
CTRL_DATA_FORMAT_24_BIT,
host->base + LCDC_CTRL + REG_CLR);
writel(CTRL1_BYTE_PACKING_FORMAT_MASK,
host->base + LCDC_CTRL1 + REG_CLR);
switch (host->mpu_lcd_sigs->panel_bpp) {
case 16:
writel((0xF << CTRL1_BYTE_PACKING_FORMAT_OFFSET),
host->base + LCDC_CTRL1 + REG_SET);
writel(CTRL_WORD_LENGTH_16BIT |
(0 << CTRL_INPUT_DATA_SWIZZLE_OFFSET),
host->base + LCDC_CTRL + REG_SET);
break;
case 18:
writel((0xF << CTRL1_BYTE_PACKING_FORMAT_OFFSET),
host->base + LCDC_CTRL1 + REG_SET);
writel(CTRL_WORD_LENGTH_18BIT |
(0 << CTRL_INPUT_DATA_SWIZZLE_OFFSET),
host->base + LCDC_CTRL + REG_SET);
break;
case 24:
default:
//2018_02_19
writel((0xF << CTRL1_BYTE_PACKING_FORMAT_OFFSET),
host->base + LCDC_CTRL1 + REG_SET);
writel(CTRL_WORD_LENGTH_24BIT|
(0 << CTRL_INPUT_DATA_SWIZZLE_OFFSET),
host->base + LCDC_CTRL + REG_SET);
break;
}

/* Configure the output bus width */
writel(CTRL_LCD_DATABUS_WIDTH_MASK,
host->base + LCDC_CTRL + REG_CLR);
switch (host->mpu_lcd_sigs->interface_width) {
case 8:
writel(CTRL_LCD_DATABUS_WIDTH_8BIT,
host->base + LCDC_CTRL + REG_SET);
break;
case 16:
writel(CTRL_LCD_DATABUS_WIDTH_16BIT,
host->base + LCDC_CTRL + REG_SET);
break;
case 18:
writel(CTRL_LCD_DATABUS_WIDTH_18BIT,
host->base + LCDC_CTRL + REG_SET);
break;
case 24:
default:
writel(CTRL_LCD_DATABUS_WIDTH_24BIT,
host->base + LCDC_CTRL + REG_SET);
break;
}

/* Configure the MPU timing */
writel((1 << TIMING_CMD_HOLD_OFFSET) | (1 << TIMING_CMD_SETUP_OFFSET) |
(1 << TIMING_DATA_HOLD_OFFSET) | (1 << TIMING_DATA_SETUP_OFFSET),
host->base + LCDC_TIMING);

msleep(10);

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 4\n");


}

static irqreturn_t mxsfb_irq_handler(int irq, void *dev_id)
{
struct mxsfb_info *host = dev_id;
u32 ctrl1, enable, status, acked_status;

ctrl1 = readl(host->base + LCDC_CTRL1);
enable = (ctrl1 & CTRL1_IRQ_ENABLE_MASK) >> CTRL1_IRQ_ENABLE_SHIFT;
status = (ctrl1 & CTRL1_IRQ_STATUS_MASK) >> CTRL1_IRQ_STATUS_SHIFT;
acked_status = (enable & status) << CTRL1_IRQ_STATUS_SHIFT;

if ((acked_status & CTRL1_VSYNC_EDGE_IRQ) && host->wait4vsync) {
writel(CTRL1_VSYNC_EDGE_IRQ,
host->base + LCDC_CTRL1 + REG_CLR);
writel(CTRL1_VSYNC_EDGE_IRQ_EN,
host->base + LCDC_CTRL1 + REG_CLR);
host->wait4vsync = 0;
complete(&host->vsync_complete);
}

if (acked_status & CTRL1_CUR_FRAME_DONE_IRQ) {
writel(CTRL1_CUR_FRAME_DONE_IRQ,
host->base + LCDC_CTRL1 + REG_CLR);
writel(CTRL1_CUR_FRAME_DONE_IRQ_EN,
host->base + LCDC_CTRL1 + REG_CLR);
complete(&host->flip_complete);
}

if (acked_status & CTRL1_UNDERFLOW_IRQ)
writel(CTRL1_UNDERFLOW_IRQ, host->base + LCDC_CTRL1 + REG_CLR);

if (acked_status & CTRL1_OVERFLOW_IRQ)
writel(CTRL1_OVERFLOW_IRQ, host->base + LCDC_CTRL1 + REG_CLR);

return IRQ_HANDLED;

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 11\n");
}

static int mxsfb_check_var(struct fb_var_screeninfo *var,
struct fb_info *fb_info)
{
struct mxsfb_info *host = to_imxfb_host(fb_info);
const struct fb_bitfield *rgb = NULL;

if (var->xres < MIN_XRES)
var->xres = MIN_XRES;
if (var->yres < MIN_YRES)
var->yres = MIN_YRES;

if (var->xres_virtual > var->xres) {
dev_dbg(fb_info->device, "stride not supported\n");
return -EINVAL;
}

if (var->xres_virtual < var->xres)
var->xres_virtual = var->xres;
if (var->yres_virtual < var->yres)
var->yres_virtual = var->yres;

if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 16))
var->bits_per_pixel = 32;

switch (var->bits_per_pixel) {
case 16:
/* always expect RGB 565 */
rgb = def_rgb565;
break;
case 32:
if (host->is_mpu_lcd) {
rgb = def_rgb666;
break;
}

switch (host->ld_intf_width) {
case STMLCDIF_8BIT:
pr_debug("Unsupported LCD bus width mapping\n");
break;
case STMLCDIF_16BIT:
/* 24 bit to 18 bit mapping */
rgb = def_rgb666;
break;
case STMLCDIF_18BIT:
if (pixfmt_is_equal(var, def_rgb666))
/* 24 bit to 18 bit mapping */
rgb = def_rgb666;
else
rgb = def_rgb888;
break;
case STMLCDIF_24BIT:
/* real 24 bit */
rgb = def_rgb888;
break;
}
break;
default:
pr_debug("Unsupported colour depth: %u\n", var->bits_per_pixel);
return -EINVAL;
}

/*
* Copy the RGB parameters for this display
* from the machine specific parameters.
*/
var->red = rgb[RED];
var->green = rgb[GREEN];
var->blue = rgb[BLUE];
var->transp = rgb[TRANSP];

return 0;
}

static void mxsfb_enable_controller(struct fb_info *fb_info)
{
struct mxsfb_info *host = to_imxfb_host(fb_info);
u32 reg;
int ret;

dev_dbg(&host->pdev->dev, "%s\n", __func__);

if (host->dispdrv && host->dispdrv->drv->setup) {
ret = host->dispdrv->drv->setup(host->dispdrv, fb_info);
if (ret < 0) {
dev_err(&host->pdev->dev, "failed to setup"
"dispdrv:%s\n", host->dispdrv->drv->name);
return;
}
host->sync = fb_info->var.sync;
}

if (host->reg_lcd) {
ret = regulator_enable(host->reg_lcd);
if (ret) {
dev_err(&host->pdev->dev,
"lcd regulator enable failed: %d\n", ret);
return;
}
}

/* the pixel clock should be disabled before
* trying to set its clock rate successfully.
*/
clk_disable_pix(host);
ret = clk_set_rate(host->clk_pix,
PICOS2KHZ(fb_info->var.pixclock) * 1000U);
if (ret) {
dev_err(&host->pdev->dev,
"lcd pixel rate set failed: %d\n", ret);

if (host->reg_lcd) {
ret = regulator_disable(host->reg_lcd);
if (ret)
dev_err(&host->pdev->dev,
"lcd regulator disable failed: %d\n",
ret);
}
return;
}
clk_enable_pix(host);

if (!host->is_mpu_lcd) {
writel(CTRL2_OUTSTANDING_REQS_REQ_16,
host->base + LCDC_V4_CTRL2 + REG_SET);

/* if it was disabled, re-enable the mode again */
writel(CTRL_DOTCLK_MODE, host->base + LCDC_CTRL + REG_SET);

/* enable the SYNC signals first, then the DMA engine */
reg = readl(host->base + LCDC_VDCTRL4);
reg |= VDCTRL4_SYNC_SIGNALS_ON;
writel(reg, host->base + LCDC_VDCTRL4);

writel(CTRL_MASTER, host->base + LCDC_CTRL + REG_SET);
writel(CTRL_RUN, host->base + LCDC_CTRL + REG_SET);

/* Recovery on underflow */
writel(CTRL1_RECOVERY_ON_UNDERFLOW, host->base + LCDC_CTRL1 + REG_SET);
}

host->enabled = 1;

if (host->dispdrv && host->dispdrv->drv->enable) {
ret = host->dispdrv->drv->enable(host->dispdrv, fb_info);
if (ret < 0)
dev_err(&host->pdev->dev, "failed to enable "
"dispdrv:%s\n", host->dispdrv->drv->name);
}

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 5\n");


}

static void mxsfb_disable_controller(struct fb_info *fb_info)
{
struct mxsfb_info *host = to_imxfb_host(fb_info);
unsigned loop;
u32 reg;
int ret;

dev_dbg(&host->pdev->dev, "%s\n", __func__);

if (host->dispdrv && host->dispdrv->drv->disable)
host->dispdrv->drv->disable(host->dispdrv, fb_info);

/*
* Even if we disable the controller here, it will still continue
* until its FIFOs are running out of data
*/
writel(CTRL_DOTCLK_MODE, host->base + LCDC_CTRL + REG_CLR);

loop = 1000;
while (loop) {
reg = readl(host->base + LCDC_CTRL);
if (!(reg & CTRL_RUN))
break;
loop--;
}

writel(CTRL_MASTER, host->base + LCDC_CTRL + REG_CLR);

reg = readl(host->base + LCDC_VDCTRL4);
writel(reg & ~VDCTRL4_SYNC_SIGNALS_ON, host->base + LCDC_VDCTRL4);

host->enabled = 0;

if (host->reg_lcd) {
ret = regulator_disable(host->reg_lcd);
if (ret)
dev_err(&host->pdev->dev,
"lcd regulator disable failed: %d\n", ret);
}

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 6\n");


}

/**
This function compare the fb parameter see whether it was different
parameter for hardware, if it was different parameter, the hardware
will reinitialize. All will compared except x/y offset.
*/
static bool mxsfb_par_equal(struct fb_info *fbi, struct mxsfb_info *host)
{
/* Here we set the xoffset, yoffset to zero, and compare two
* var see have different or not. */
struct fb_var_screeninfo oldvar = host->var;
struct fb_var_screeninfo newvar = fbi->var;

if ((fbi->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW &&
fbi->var.activate & FB_ACTIVATE_FORCE)
return false;

oldvar.xoffset = newvar.xoffset = 0;
oldvar.yoffset = newvar.yoffset = 0;

return memcmp(&oldvar, &newvar, sizeof(struct fb_var_screeninfo)) == 0;
}

static int mxsfb_set_par(struct fb_info *fb_info)
{
struct mxsfb_info *host = to_imxfb_host(fb_info);
u32 ctrl, vdctrl0, vdctrl4;
int line_size, fb_size;
int reenable = 0;
static u32 equal_bypass = 0;

if (likely(equal_bypass > 1)) {
/* If parameter no change, don't reconfigure. */
if (mxsfb_par_equal(fb_info, host))
return 0;
} else
equal_bypass++;

dev_dbg(&host->pdev->dev, "%s\n", __func__);

/* If fb is in blank mode, it is
* unnecessary to really set par here.
* It can be delayed when unblank fb
*/
if (host->cur_blank != FB_BLANK_UNBLANK)
return 0;

line_size = fb_info->var.xres * (fb_info->var.bits_per_pixel >> 3);
fb_info->fix.line_length = line_size;
fb_size = fb_info->var.yres_virtual * line_size;

if (fb_size > fb_info->fix.smem_len) {
dev_err(&host->pdev->dev, "exceeds the fb buffer size limit!\n");
return -ENOMEM;
}

/*
* It seems, you can't re-program the controller if it is still running.
* This may lead into shifted pictures (FIFO issue?).
* So, first stop the controller and drain its FIFOs
*/
if (host->enabled && (!host->is_mpu_lcd)) {
reenable = 1;
mxsfb_disable_controller(fb_info);
}

if (host->is_mpu_lcd) {
if (host->enabled) {
mxsfb_mpu_setup_interface(host);
writel(fb_info->fix.smem_start +
fb_info->fix.line_length * fb_info->var.yoffset,
host->base + host->devdata->cur_buf);
writel(fb_info->fix.smem_start +
fb_info->fix.line_length * fb_info->var.yoffset,
host->base + host->devdata->next_buf);
host->mpu_lcd_functions->mpu_lcd_setup(host);
mxsfb_mpu_setup_refresh_data(host);
mxsfb_mpu_refresh_panel(host);
}
} else {
/* clear the FIFOs */
writel(CTRL1_FIFO_CLEAR, host->base + LCDC_CTRL1 + REG_SET);

ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER |
CTRL_SET_BUS_WIDTH(host->ld_intf_width);

switch (fb_info->var.bits_per_pixel) {
case 16:
dev_dbg(&host->pdev->dev, "Setting up RGB565 mode\n");
ctrl |= CTRL_SET_WORD_LENGTH(0);
writel(CTRL1_SET_BYTE_PACKAGING(0xf), host->base + LCDC_CTRL1);
break;
case 32:
dev_dbg(&host->pdev->dev, "Setting up RGB888/666 mode\n");
ctrl |= CTRL_SET_WORD_LENGTH(3);
switch (host->ld_intf_width) {
case STMLCDIF_8BIT:
dev_dbg(&host->pdev->dev,
"Unsupported LCD bus width mapping\n");
return -EINVAL;
case STMLCDIF_16BIT:
/* 24 bit to 18 bit mapping */
ctrl |= CTRL_DATA_FORMAT_24_BIT; /* ignore the upper 2 bits in
* each colour component
*/
break;
case STMLCDIF_18BIT:
if (pixfmt_is_equal(&fb_info->var, def_rgb666))
/* 24 bit to 18 bit mapping */
ctrl |= CTRL_DATA_FORMAT_24_BIT; /* ignore the upper 2 bits in
* each colour component
*/
break;
case STMLCDIF_24BIT:
/* real 24 bit */
break;
}
/* do not use packed pixels = one pixel per word instead */
writel(CTRL1_SET_BYTE_PACKAGING(0x7), host->base + LCDC_CTRL1);
break;
default:
dev_dbg(&host->pdev->dev, "Unhandled color depth of %u\n",
fb_info->var.bits_per_pixel);
return -EINVAL;
}

writel(ctrl, host->base + LCDC_CTRL);

writel(TRANSFER_COUNT_SET_VCOUNT(fb_info->var.yres) |
TRANSFER_COUNT_SET_HCOUNT(fb_info->var.xres),
host->base + host->devdata->transfer_count);

vdctrl0 = VDCTRL0_ENABLE_PRESENT | /* always in DOTCLOCK mode */
VDCTRL0_VSYNC_PERIOD_UNIT |
VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |
VDCTRL0_SET_VSYNC_PULSE_WIDTH(fb_info->var.vsync_len);
/* use the saved sync to avoid wrong sync information */
if (host->sync & FB_SYNC_HOR_HIGH_ACT)
vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH;
if (host->sync & FB_SYNC_VERT_HIGH_ACT)
vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH;
if (!(host->sync & FB_SYNC_OE_LOW_ACT))
vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH;
if (host->sync & FB_SYNC_CLK_LAT_FALL)
vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING;

writel(vdctrl0, host->base + LCDC_VDCTRL0);

/* frame length in lines */
writel(fb_info->var.upper_margin + fb_info->var.vsync_len +
fb_info->var.lower_margin + fb_info->var.yres,
host->base + LCDC_VDCTRL1);

/* line length in units of clocks or pixels */
writel(set_hsync_pulse_width(host, fb_info->var.hsync_len) |
VDCTRL2_SET_HSYNC_PERIOD(fb_info->var.left_margin +
fb_info->var.hsync_len + fb_info->var.right_margin +
fb_info->var.xres),
host->base + LCDC_VDCTRL2);

writel(SET_HOR_WAIT_CNT(fb_info->var.left_margin +
fb_info->var.hsync_len) |
SET_VERT_WAIT_CNT(fb_info->var.upper_margin +
fb_info->var.vsync_len),
host->base + LCDC_VDCTRL3);

vdctrl4 = SET_DOTCLK_H_VALID_DATA_CNT(fb_info->var.xres);
if (mxsfb_is_v4(host))
vdctrl4 |= VDCTRL4_SET_DOTCLK_DLY(host->dotclk_delay);
writel(vdctrl4, host->base + LCDC_VDCTRL4);

writel(fb_info->fix.smem_start +
fb_info->fix.line_length * fb_info->var.yoffset,
host->base + host->devdata->next_buf);
}

if (reenable && (!host->is_mpu_lcd))
mxsfb_enable_controller(fb_info);

/* Clear activate as not Reconfiguring framebuffer again */
if ((fb_info->var.activate & FB_ACTIVATE_FORCE) &&
(fb_info->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
fb_info->var.activate = FB_ACTIVATE_NOW;

host->var = fb_info->var;

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 7\n");


return 0;
}

static int mxsfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int transp, struct fb_info *fb_info)
{
unsigned int val;
int ret = -EINVAL;

/*
* If greyscale is true, then we convert the RGB value
* to greyscale no matter what visual we are using.
*/
if (fb_info->var.grayscale)
red = green = blue = (19595 * red + 38470 * green +
7471 * blue) >> 16;

switch (fb_info->fix.visual) {
case FB_VISUAL_TRUECOLOR:
/*
* 12 or 16-bit True Colour. We encode the RGB value
* according to the RGB bitfield information.
*/
if (regno < 16) {
u32 *pal = fb_info->pseudo_palette;

val = chan_to_field(red, &fb_info->var.red);
val |= chan_to_field(green, &fb_info->var.green);
val |= chan_to_field(blue, &fb_info->var.blue);

pal[regno] = val;
ret = 0;
}
break;

case FB_VISUAL_STATIC_PSEUDOCOLOR:
case FB_VISUAL_PSEUDOCOLOR:
break;
}

return ret;
}

static int mxsfb_wait_for_vsync(struct fb_info *fb_info)
{
struct mxsfb_info *host = to_imxfb_host(fb_info);
int ret = 0;

if (host->cur_blank != FB_BLANK_UNBLANK) {
dev_err(fb_info->device, "can't wait for VSYNC when fb "
"is blank\n");
return -EINVAL;
}

if (host->is_mpu_lcd) {
if (mxsfb_mpu_wait_for_ready(host) != 0)
return -ETIME;
} else {
init_completion(&host->vsync_complete);

host->wait4vsync = 1;
writel(CTRL1_VSYNC_EDGE_IRQ_EN,
host->base + LCDC_CTRL1 + REG_SET);
ret = wait_for_completion_interruptible_timeout(
&host->vsync_complete, 1 * HZ);
if (ret == 0) {
dev_err(fb_info->device,
"mxs wait for vsync timeout\n");
host->wait4vsync = 0;
ret = -ETIME;
} else if (ret > 0) {
ret = 0;
}
}

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 12\n");

return ret;
}

static int mxsfb_ioctl(struct fb_info *fb_info, unsigned int cmd,
unsigned long arg)
{
struct mxsfb_info *host = to_imxfb_host(fb_info);
int ret = -EINVAL;

switch (cmd) {
case MXCFB_WAIT_FOR_VSYNC:
ret = mxsfb_wait_for_vsync(fb_info);
break;
case MXCFB_MPU_REFRESH_PANEL:
ret = mxsfb_mpu_refresh_panel(host);
break;
default:
break;
}

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 8\n");


return ret;
}

static int mxsfb_blank(int blank, struct fb_info *fb_info)
{
struct mxsfb_info *host = to_imxfb_host(fb_info);

host->cur_blank = blank;

switch (blank) {
case FB_BLANK_POWERDOWN:
case FB_BLANK_VSYNC_SUSPEND:
case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_NORMAL:
if (host->enabled) {
if (host->is_mpu_lcd)
host->mpu_lcd_functions->mpu_lcd_poweroff(host);
mxsfb_disable_controller(fb_info);
pm_runtime_put_sync_suspend(&host->pdev->dev);
}

clk_disable_disp_axi(host);
clk_disable_axi(host);
clk_disable_pix(host);
break;

case FB_BLANK_UNBLANK:
fb_info->var.activate = (fb_info->var.activate & ~FB_ACTIVATE_MASK) |
FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;

clk_enable_pix(host);
clk_enable_axi(host);
clk_enable_disp_axi(host);

if (!host->enabled) {
pm_runtime_get_sync(&host->pdev->dev);

writel(0, host->base + LCDC_CTRL);
if (host->is_mpu_lcd) {
mxsfb_enable_controller(fb_info);
mxsfb_set_par(&host->fb_info);
} else {
mxsfb_set_par(&host->fb_info);
mxsfb_enable_controller(fb_info);
}
}
break;
}
return 0;
}

static int mxsfb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *fb_info)
{
int ret = 0;
struct mxsfb_info *host = to_imxfb_host(fb_info);
unsigned offset;

if (host->cur_blank != FB_BLANK_UNBLANK) {
dev_dbg(fb_info->device, "can't do pan display when fb "
"is blank\n");
return -EINVAL;
}

if (var->xoffset > 0) {
dev_dbg(fb_info->device, "x panning not supported\n");
return -EINVAL;
}

if ((var->yoffset + var->yres > var->yres_virtual)) {
dev_err(fb_info->device, "y panning exceeds\n");
return -EINVAL;
}

if (host->is_mpu_lcd) {
if (mxsfb_mpu_wait_for_ready(host) != 0)
return -ETIMEDOUT;

offset = fb_info->fix.line_length * var->yoffset;

writel(fb_info->fix.smem_start + offset,
host->base + host->devdata->next_buf);
writel(fb_info->fix.smem_start + offset,
host->base + host->devdata->cur_buf);

mxsfb_mpu_refresh_panel(host);
} else {
init_completion(&host->flip_complete);

offset = fb_info->fix.line_length * var->yoffset;

/* update on next VSYNC */
writel(fb_info->fix.smem_start + offset,
host->base + host->devdata->next_buf);

writel(CTRL1_CUR_FRAME_DONE_IRQ_EN,
host->base + LCDC_CTRL1 + REG_SET);

ret = wait_for_completion_timeout(&host->flip_complete, HZ / 2);
if (!ret) {
dev_err(fb_info->device,
"mxs wait for pan flip timeout\n");
return -ETIMEDOUT;
}
}

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 13\n");

return 0;
}

static int mxsfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
u32 len;
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;

if (offset < info->fix.smem_len) {
/* mapping framebuffer memory */
len = info->fix.smem_len - offset;
vma->vm_pgoff = (info->fix.smem_start + offset) >> PAGE_SHIFT;
} else
return -EINVAL;

len = PAGE_ALIGN(len);
if (vma->vm_end - vma->vm_start > len)
return -EINVAL;

/* make buffers bufferable */
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);

if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
dev_dbg(info->device, "mmap remap_pfn_range failed\n");
return -ENOBUFS;
}

return 0;
}

static struct fb_ops mxsfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = mxsfb_check_var,
.fb_set_par = mxsfb_set_par,
.fb_setcolreg = mxsfb_setcolreg,
.fb_ioctl = mxsfb_ioctl,
.fb_blank = mxsfb_blank,
.fb_pan_display = mxsfb_pan_display,
.fb_mmap = mxsfb_mmap,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};

static int mxsfb_restore_mode(struct mxsfb_info *host)
{
struct fb_info *fb_info = &host->fb_info;
unsigned line_count;
unsigned period;
unsigned long pa, fbsize;
int bits_per_pixel, ofs;
u32 transfer_count, vdctrl0, vdctrl2, vdctrl3, vdctrl4, ctrl;
struct fb_videomode vmode;

clk_enable_axi(host);
clk_enable_disp_axi(host);

/* Enable pixel clock earlier since in 7D
* the lcdif registers should be accessed
* when the pixel clock is enabled, otherwise
* the bus will be hang.
*/
clk_enable_pix(host);

if (! host->is_mpu_lcd) {
/* Only restore the mode when the controller is running */
ctrl = readl(host->base + LCDC_CTRL);
if (!(ctrl & CTRL_RUN))
return -EINVAL;

vdctrl0 = readl(host->base + LCDC_VDCTRL0);
vdctrl2 = readl(host->base + LCDC_VDCTRL2);
vdctrl3 = readl(host->base + LCDC_VDCTRL3);
vdctrl4 = readl(host->base + LCDC_VDCTRL4);

transfer_count = readl(host->base + host->devdata->transfer_count);

vmode.xres = TRANSFER_COUNT_GET_HCOUNT(transfer_count);
vmode.yres = TRANSFER_COUNT_GET_VCOUNT(transfer_count);

switch (CTRL_GET_WORD_LENGTH(ctrl)) {
case 0:
bits_per_pixel = 16;
break;
case 3:
bits_per_pixel = 32;
case 1:
default:
return -EINVAL;
}

fb_info->var.bits_per_pixel = bits_per_pixel;

vmode.pixclock = KHZ2PICOS(clk_get_rate(host->clk_pix) / 1000U);
vmode.hsync_len = get_hsync_pulse_width(host, vdctrl2);
vmode.left_margin = GET_HOR_WAIT_CNT(vdctrl3) - vmode.hsync_len;
vmode.right_margin = VDCTRL2_GET_HSYNC_PERIOD(vdctrl2) - vmode.hsync_len -
vmode.left_margin - vmode.xres;
vmode.vsync_len = VDCTRL0_GET_VSYNC_PULSE_WIDTH(vdctrl0);
period = readl(host->base + LCDC_VDCTRL1);
vmode.upper_margin = GET_VERT_WAIT_CNT(vdctrl3) - vmode.vsync_len;
vmode.lower_margin = period - vmode.vsync_len - vmode.upper_margin - vmode.yres;

vmode.vmode = FB_VMODE_NONINTERLACED;

vmode.sync = 0;
if (vdctrl0 & VDCTRL0_HSYNC_ACT_HIGH)
vmode.sync |= FB_SYNC_HOR_HIGH_ACT;
if (vdctrl0 & VDCTRL0_VSYNC_ACT_HIGH)
vmode.sync |= FB_SYNC_VERT_HIGH_ACT;

pr_debug("Reconstructed video mode:\n");
pr_debug("%dx%d, hsync: %u left: %u, right: %u, vsync: %u, upper: %u, lower: %u\n",
vmode.xres, vmode.yres,
vmode.hsync_len, vmode.left_margin, vmode.right_margin,
vmode.vsync_len, vmode.upper_margin, vmode.lower_margin);
pr_debug("pixclk: %ldkHz\n", PICOS2KHZ(vmode.pixclock));

fb_add_videomode(&vmode, &fb_info->modelist);

host->ld_intf_width = CTRL_GET_BUS_WIDTH(ctrl);
host->dotclk_delay = VDCTRL4_GET_DOTCLK_DLY(vdctrl4);

fb_info->fix.line_length = vmode.xres * (bits_per_pixel >> 3);

pa = readl(host->base + host->devdata->cur_buf);
fbsize = fb_info->fix.line_length * vmode.yres;
if (pa < fb_info->fix.smem_start)
return -EINVAL;
if (pa + fbsize > fb_info->fix.smem_start + fb_info->fix.smem_len)
return -EINVAL;
ofs = pa - fb_info->fix.smem_start;
if (ofs) {
memmove(fb_info->screen_base, fb_info->screen_base + ofs, fbsize);
writel(fb_info->fix.smem_start, host->base + host->devdata->next_buf);
}

line_count = fb_info->fix.smem_len / fb_info->fix.line_length;
fb_info->fix.ypanstep = 1;
fb_info->fix.ywrapstep = 1;

host->enabled = 1;
}

return 0;
}

static int mxsfb_init_fbinfo_dt(struct mxsfb_info *host)
{
struct fb_info *fb_info = &host->fb_info;
struct fb_var_screeninfo *var = &fb_info->var;
struct device *dev = &host->pdev->dev;
struct device_node *np = host->pdev->dev.of_node;
struct device_node *display_np;
struct device_node *timings_np;
struct display_timings *timings;
const char *disp_dev;
u32 width;
int i;
const char *lcd_panel;
int ret = 0;

host->id = of_alias_get_id(np, "lcdif");

display_np = of_parse_phandle(np, "display", 0);
if (!display_np) {
dev_err(dev, "failed to find display phandle\n");
return -ENOENT;
}

host->is_mpu_lcd = of_property_read_bool(display_np, "mpu-mode");
if (host->is_mpu_lcd) {
struct fb_videomode *mpu_lcd_modedb;
struct fb_videomode fb_vm;
int size;

ret = of_property_read_string(display_np, "lcd_panel", &lcd_panel);
if (ret) {
dev_err(dev, "failed to read of property lcd_panel\n");
goto put_display_node;
}

for (i = 0; i < ARRAY_SIZE(mpu_lcd_db); i++) {
if (!strcmp(lcd_panel, mpu_lcd_db[i].lcd_panel)) {
host->mpu_lcd_functions =
&mpu_lcd_db[i].lcd_callback;
break;
}
}
if (i == ARRAY_SIZE(mpu_lcd_db)) {
dev_err(dev, "failed to find supported lcd panel.\n");
ret = -EINVAL;
goto put_display_node;
}
host->mpu_lcd_functions->get_mpu_lcd_videomode(&mpu_lcd_modedb, &size,
&host->mpu_lcd_sigs);

memcpy(&fb_vm, mpu_lcd_modedb, sizeof(struct fb_videomode));
var->bits_per_pixel = host->mpu_lcd_sigs->panel_bpp;

switch (host->mpu_lcd_sigs->interface_width) {
case 8:
host->ld_intf_width = STMLCDIF_8BIT;
break;
case 16:
host->ld_intf_width = STMLCDIF_16BIT;
break;
case 18:
host->ld_intf_width = STMLCDIF_18BIT;
break;
case 24:
host->ld_intf_width = STMLCDIF_24BIT;
break;
default:
dev_err(dev, "invalid interface width value\n");
ret = -EINVAL;
goto put_display_node;
}

/* lcd reset gpio pin */
host->mpu_lcd_sigs->lcd_reset_is_gpio = 0;
host->mpu_lcd_sigs->lcd_reset_gpio = of_get_named_gpio(display_np, "lcd_reset_gpio", 0);
if (gpio_is_valid(host->mpu_lcd_sigs->lcd_reset_gpio)) {
dev_info(dev, "find lcd reset gpio pin.\n");
if (devm_gpio_request_one(dev, host->mpu_lcd_sigs->lcd_reset_gpio, GPIOF_OUT_INIT_HIGH, "lcd_reset") >= 0)
host->mpu_lcd_sigs->lcd_reset_is_gpio = 1;
}

/* lcd rs gpio pin */
host->mpu_lcd_sigs->lcd_rs_is_gpio = 0;
host->mpu_lcd_sigs->lcd_rs_gpio = of_get_named_gpio(display_np, "lcd_rs_gpio", 0);
if (gpio_is_valid(host->mpu_lcd_sigs->lcd_rs_gpio)) {
dev_info(dev, "find lcd rs gpio pin.\n");
if (devm_gpio_request_one(dev, host->mpu_lcd_sigs->lcd_rs_is_gpio, GPIOF_OUT_INIT_HIGH, "lcd_rs") >= 0)
host->mpu_lcd_sigs->lcd_rs_is_gpio = 1;
}

fb_add_videomode(&fb_vm, &fb_info->modelist);
goto put_display_node;
} else {
ret = of_property_read_u32(display_np, "bus-width", &width);
if (ret < 0) {
dev_err(dev, "failed to get property bus-width\n");
goto put_display_node;
}

switch (width) {
case 8:
host->ld_intf_width = STMLCDIF_8BIT;
break;
case 16:
host->ld_intf_width = STMLCDIF_16BIT;
break;
case 18:
host->ld_intf_width = STMLCDIF_18BIT;
break;
case 24:
host->ld_intf_width = STMLCDIF_24BIT;
break;
default:
dev_err(dev, "invalid bus-width value\n");
ret = -EINVAL;
goto put_display_node;
}

ret = of_property_read_u32(display_np, "bits-per-pixel",
&var->bits_per_pixel);
if (ret < 0) {
dev_err(dev, "failed to get property bits-per-pixel\n");
goto put_display_node;
}

ret = of_property_read_string(np, "disp-dev", &disp_dev);
if (!ret) {
memcpy(host->disp_dev, disp_dev, strlen(disp_dev));
/* Timing is from encoder driver */
goto put_display_node;
}

timings = of_get_display_timings(display_np);
if (!timings) {
dev_err(dev, "failed to get display timings\n");
ret = -ENOENT;
goto put_display_node;
}

timings_np = of_find_node_by_name(display_np,
"display-timings");
if (!timings_np) {
dev_err(dev, "failed to find display-timings node\n");
ret = -ENOENT;
goto put_display_node;
}

for (i = 0; i < of_get_child_count(timings_np); i++) {
struct videomode vm;
struct fb_videomode fb_vm;

ret = videomode_from_timings(timings, &vm, i);
if (ret < 0)
goto put_timings_node;
ret = fb_videomode_from_videomode(&vm, &fb_vm);
if (ret < 0)
goto put_timings_node;

if (!(vm.flags & DISPLAY_FLAGS_DE_HIGH))
fb_vm.sync |= FB_SYNC_OE_LOW_ACT;
if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
fb_vm.sync |= FB_SYNC_CLK_LAT_FALL;
fb_add_videomode(&fb_vm, &fb_info->modelist);
}
}
put_timings_node:
of_node_put(timings_np);
put_display_node:
of_node_put(display_np);
return ret;
}

static int mxsfb_init_fbinfo(struct mxsfb_info *host)
{
struct fb_info *fb_info = &host->fb_info;
struct fb_var_screeninfo *var = &fb_info->var;
struct fb_modelist *modelist;
int ret;

fb_info->fbops = &mxsfb_ops;
fb_info->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST;
fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
fb_info->fix.ypanstep = 1;
fb_info->fix.ywrapstep = 1;
fb_info->fix.visual = FB_VISUAL_TRUECOLOR,
fb_info->fix.accel = FB_ACCEL_NONE;

ret = mxsfb_init_fbinfo_dt(host);
if (ret)
return ret;

if (host->id < 0)
sprintf(fb_info->fix.id, "mxs-lcdif");
else
sprintf(fb_info->fix.id, "mxs-lcdif%d", host->id);

if (!list_empty(&fb_info->modelist)) {
/* first video mode in the modelist as default video mode */
modelist = list_first_entry(&fb_info->modelist,
struct fb_modelist, list);
fb_videomode_to_var(var, &modelist->mode);
}
/* save the sync value getting from dtb */
host->sync = fb_info->var.sync;

var->nonstd = 0;
var->activate = FB_ACTIVATE_NOW;
var->accel_flags = 0;
var->vmode = FB_VMODE_NONINTERLACED;

/* init the color fields */
mxsfb_check_var(var, fb_info);

fb_info->fix.line_length =
fb_info->var.xres * (fb_info->var.bits_per_pixel >> 3);
fb_info->fix.smem_len = SZ_32M;

/* Memory allocation for framebuffer */
if (mxsfb_map_videomem(fb_info) < 0)
return -ENOMEM;

if (mxsfb_restore_mode(host))
memset((char *)fb_info->screen_base, 0, fb_info->fix.smem_len);

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 15\n");

return 0;
}

static void mxsfb_dispdrv_init(struct platform_device *pdev,
struct fb_info *fbi)
{
struct mxsfb_info *host = to_imxfb_host(fbi);
struct mxc_dispdrv_setting setting;
struct device *dev = &pdev->dev;
char disp_dev[32];

memset(&setting, 0x0, sizeof(setting));
setting.fbi = fbi;
memcpy(disp_dev, host->disp_dev, strlen(host->disp_dev));
disp_dev[strlen(host->disp_dev)] = '\0';

host->dispdrv = mxc_dispdrv_gethandle(disp_dev, &setting);
if (IS_ERR(host->dispdrv)) {
host->dispdrv = NULL;
/*dev_info(dev, "failed to find mxc display driver %s\n",
disp_dev);*/
} else {
dev_info(dev, "registered mxc display driver %s\n",
disp_dev);
}
}

static void mxsfb_free_videomem(struct mxsfb_info *host)
{
struct fb_info *fb_info = &host->fb_info;

mxsfb_unmap_videomem(fb_info);

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 16\n");
}

/*!
* Allocates the DRAM memory for the frame buffer. This buffer is remapped
* into a non-cached, non-buffered, memory region to allow palette and pixel
* writes to occur without flushing the cache. Once this area is remapped,
* all virtual memory access to the video memory should occur at the new region.
*
* @param fbi framebuffer information pointer
*
* @return Error code indicating success or failure
*/
static int mxsfb_map_videomem(struct fb_info *fbi)
{
if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length)
fbi->fix.smem_len = fbi->var.yres_virtual *
fbi->fix.line_length;

fbi->screen_base = dma_alloc_writecombine(fbi->device,
fbi->fix.smem_len,
(dma_addr_t *)&fbi->fix.smem_start,
GFP_DMA | GFP_KERNEL);
if (fbi->screen_base == 0) {
dev_err(fbi->device, "Unable to allocate framebuffer memory\n");
fbi->fix.smem_len = 0;
fbi->fix.smem_start = 0;
return -EBUSY;
}

dev_dbg(fbi->device, "allocated fb @ paddr=0x%08X, size=%d.\n",
(uint32_t) fbi->fix.smem_start, fbi->fix.smem_len);

fbi->screen_size = fbi->fix.smem_len;

/* Clear the screen */
memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);

return 0;
}

/*!
* De-allocates the DRAM memory for the frame buffer.
*
* @param fbi framebuffer information pointer
*
* @return Error code indicating success or failure
*/
static int mxsfb_unmap_videomem(struct fb_info *fbi)
{
dma_free_writecombine(fbi->device, fbi->fix.smem_len,
fbi->screen_base, fbi->fix.smem_start);
fbi->screen_base = 0;
fbi->fix.smem_start = 0;
fbi->fix.smem_len = 0;
return 0;
}

static struct platform_device_id mxsfb_devtype[] = {
{
.name = "imx23-fb",
.driver_data = MXSFB_V3,
}, {
.name = "imx28-fb",
.driver_data = MXSFB_V4,
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(platform, mxsfb_devtype);

static const struct of_device_id mxsfb_dt_ids[] = {
{ .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], },
{ .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mxsfb_dt_ids);

static int mxsfb_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(mxsfb_dt_ids, &pdev->dev);
struct resource *res;
struct mxsfb_info *host;
struct fb_info *fb_info;
struct pinctrl *pinctrl;
int irq = platform_get_irq(pdev, 0);
int ret;

if (of_id)
pdev->id_entry = of_id->data;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Cannot get memory IO resource\n");
return -ENODEV;
}

fb_info = framebuffer_alloc(sizeof(struct mxsfb_info), &pdev->dev);
if (!fb_info) {
dev_err(&pdev->dev, "Failed to allocate fbdev\n");
return -ENOMEM;
}

host = to_imxfb_host(fb_info);

global_host = to_imxfb_host(fb_info);
/* MBE REGISTER REFRESH TIMER */
/* setup your timer to call refresh_timer_callback */
setup_timer(&refresh_timer, refresh_timer_callback, 0);
/* setup timer interval to XXX msecs */
mod_timer(&refresh_timer, jiffies + msecs_to_jiffies(1000));

ret = devm_request_irq(&pdev->dev, irq, mxsfb_irq_handler, 0,
dev_name(&pdev->dev), host);
if (ret) {
dev_err(&pdev->dev, "request_irq (%d) failed with error %d\n",
irq, ret);
return -ENODEV;
}

host->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(host->base)) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = PTR_ERR(host->base);
goto fb_release;
}

host->pdev = pdev;
platform_set_drvdata(pdev, host);

host->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];

host->clk_pix = devm_clk_get(&host->pdev->dev, "pix");
if (IS_ERR(host->clk_pix)) {
host->clk_pix = NULL;
ret = PTR_ERR(host->clk_pix);
goto fb_release;
}

host->clk_axi = devm_clk_get(&host->pdev->dev, "axi");
if (IS_ERR(host->clk_axi)) {
host->clk_axi = NULL;
ret = PTR_ERR(host->clk_axi);
goto fb_release;
}

host->clk_disp_axi = devm_clk_get(&host->pdev->dev, "disp_axi");
if (IS_ERR(host->clk_disp_axi)) {
host->clk_disp_axi = NULL;
ret = PTR_ERR(host->clk_disp_axi);
goto fb_release;
}

host->reg_lcd = devm_regulator_get(&pdev->dev, "lcd");
if (IS_ERR(host->reg_lcd))
host->reg_lcd = NULL;

fb_info->pseudo_palette = devm_kzalloc(&pdev->dev, sizeof(u32) * 16,
GFP_KERNEL);
if (!fb_info->pseudo_palette) {
ret = -ENOMEM;
goto fb_release;
}

INIT_LIST_HEAD(&fb_info->modelist);

pm_runtime_enable(&host->pdev->dev);

ret = mxsfb_init_fbinfo(host);
if (ret != 0)
goto fb_pm_runtime_disable;

mxsfb_dispdrv_init(pdev, fb_info);

if (!host->dispdrv) {
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
ret = PTR_ERR(pinctrl);
goto fb_pm_runtime_disable;
}
}

platform_set_drvdata(pdev, fb_info);

if (!host->enabled) {
writel(0, host->base + LCDC_CTRL);
if (host->is_mpu_lcd) {
mxsfb_enable_controller(fb_info);
mxsfb_set_par(&host->fb_info);
mxsfb_driver_ok = 1;
} else {
mxsfb_set_par(&host->fb_info);
mxsfb_enable_controller(fb_info);
}
pm_runtime_get_sync(&host->pdev->dev);
}

ret = register_framebuffer(fb_info);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to register framebuffer\n");
goto fb_destroy;
}

console_lock();
ret = fb_blank(fb_info, FB_BLANK_UNBLANK);
console_unlock();
if (ret < 0) {
dev_err(&pdev->dev, "Failed to unblank framebuffer\n");
goto fb_unregister;
}

dev_info(&pdev->dev, "initialized\n");

return 0;

fb_unregister:
unregister_framebuffer(fb_info);
fb_destroy:
if (host->enabled)
clk_disable_unprepare(host->clk_pix);
fb_destroy_modelist(&fb_info->modelist);
fb_pm_runtime_disable:
pm_runtime_disable(&host->pdev->dev);
fb_release:
framebuffer_release(fb_info);

/* FT_DEBUG */
//printf("Write to framebuffer occurred 1\n");
//dev_err(&host->pdev->dev, "Write to framebuffer occurred 17\n");

return ret;
}

static int mxsfb_remove(struct platform_device *pdev)
{
struct fb_info *fb_info = platform_get_drvdata(pdev);
struct mxsfb_info *host = to_imxfb_host(fb_info);

if (host->enabled)
mxsfb_disable_controller(fb_info);

/* MBE UNREGISTER REFRESH TIMER */
del_timer(&refresh_timer);

pm_runtime_disable(&host->pdev->dev);
unregister_framebuffer(fb_info);
mxsfb_free_videomem(host);

framebuffer_release(fb_info);

platform_set_drvdata(pdev, NULL);

return 0;
}

static void mxsfb_shutdown(struct platform_device *pdev)
{
struct fb_info *fb_info = platform_get_drvdata(pdev);
struct mxsfb_info *host = to_imxfb_host(fb_info);

/*
* Force stop the LCD controller as keeping it running during reboot
* might interfere with the BootROM's boot mode pads sampling.
*/
if (host->cur_blank == FB_BLANK_UNBLANK) {
writel(CTRL_RUN, host->base + LCDC_CTRL + REG_CLR);
writel(CTRL_MASTER, host->base + LCDC_CTRL + REG_CLR);
if (host->is_mpu_lcd)
host->mpu_lcd_functions->mpu_lcd_poweroff(host);
}
}

#ifdef CONFIG_PM_RUNTIME
static int mxsfb_runtime_suspend(struct device *dev)
{
release_bus_freq(BUS_FREQ_HIGH);
dev_dbg(dev, "mxsfb busfreq high release.\n");

return 0;
}

static int mxsfb_runtime_resume(struct device *dev)
{
request_bus_freq(BUS_FREQ_HIGH);
dev_dbg(dev, "mxsfb busfreq high request.\n");

return 0;
}
#else
#define mxsfb_runtime_suspend NULL
#define mxsfb_runtime_resume NULL
#endif

#ifdef CONFIG_PM
static int mxsfb_suspend(struct device *pdev)
{
struct fb_info *fb_info = dev_get_drvdata(pdev);
struct mxsfb_info *host = to_imxfb_host(fb_info);
int saved_blank;

console_lock();
fb_set_suspend(fb_info, 1);
saved_blank = host->cur_blank;
mxsfb_blank(FB_BLANK_POWERDOWN, fb_info);
host->restore_blank = saved_blank;
console_unlock();

pinctrl_pm_select_sleep_state(pdev);

return 0;
}

static int mxsfb_resume(struct device *pdev)
{
struct fb_info *fb_info = dev_get_drvdata(pdev);
struct mxsfb_info *host = to_imxfb_host(fb_info);

mxsfb_resume(pdev);

console_lock();
mxsfb_blank(host->restore_blank, fb_info);
fb_set_suspend(fb_info, 0);
console_unlock();

return 0;
}
#else
#define mxsfb_suspend NULL
#define mxsfb_resume NULL
#endif

static const struct dev_pm_ops mxsfb_pm_ops = {
SET_RUNTIME_PM_OPS(mxsfb_runtime_suspend, mxsfb_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(mxsfb_suspend, mxsfb_resume)
};

static struct platform_driver mxsfb_driver = {
.probe = mxsfb_probe,
.remove = mxsfb_remove,
.shutdown = mxsfb_shutdown,
.id_table = mxsfb_devtype,
.driver = {
.name = DRIVER_NAME,
.of_match_table = mxsfb_dt_ids,
.pm = &mxsfb_pm_ops,
},
};

module_platform_driver(mxsfb_driver);

MODULE_DESCRIPTION("Freescale mxs framebuffer driver");
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
MODULE_LICENSE("GPL");

mxsfb.h

/*
* Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.

* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.

* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef __MXSFB_H__
#define __MXSFB_H__

#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/mxcfb.h>

#define REG_SET 4
#define REG_CLR 8

#define LCDC_CTRL 0x00
#define LCDC_CTRL1 0x10
#define LCDC_V4_CTRL2 0x20
#define LCDC_V3_TRANSFER_COUNT 0x20
#define LCDC_V4_TRANSFER_COUNT 0x30
#define LCDC_V4_CUR_BUF 0x40
#define LCDC_V4_NEXT_BUF 0x50
#define LCDC_V3_CUR_BUF 0x30
#define LCDC_V3_NEXT_BUF 0x40
#define LCDC_TIMING 0x60
#define LCDC_VDCTRL0 0x70
#define LCDC_VDCTRL1 0x80
#define LCDC_VDCTRL2 0x90
#define LCDC_VDCTRL3 0xa0
#define LCDC_VDCTRL4 0xb0
#define LCDC_DVICTRL0 0xc0
#define LCDC_DVICTRL1 0xd0
#define LCDC_DVICTRL2 0xe0
#define LCDC_DVICTRL3 0xf0
#define LCDC_DVICTRL4 0x100
#define LCDC_V4_DATA 0x180
#define LCDC_V3_DATA 0x1b0
#define LCDC_V4_DEBUG0 0x1d0
#define LCDC_V3_DEBUG0 0x1f0

#define CTRL_SFTRST (1 << 31)
#define CTRL_CLKGATE (1 << 30)
#define CTRL_YCBCR422_INPUT (1 << 29)
#define CTRL_READ_WRITEB (1 << 28)
#define CTRL_WAIT_FOR_VSYNC_EDGE (1 << 27)
#define CTRL_DATA_SHIFT_DIR (1 << 26)
#define CTRL_SHIFT_NUM_BITS_MASK (0x1f << 21)
#define CTRL_SHIFT_NUM_BITS_OFFSET 21
#define CTRL_DVI_MODE (1 << 20)
#define CTRL_BYPASS_COUNT (1 << 19)
#define CTRL_VSYNC_MODE (1 << 18)
#define CTRL_DOTCLK_MODE (1 << 17)
#define CTRL_DATA_SELECT (1 << 16)
#define CTRL_INPUT_DATA_SWIZZLE_MASK (0x3 << 14)
#define CTRL_INPUT_DATA_SWIZZLE_OFFSET 14
#define CTRL_CSC_DATA_SWIZZLE_MASK (0x3 << 12)
#define CTRL_CSC_DATA_SWIZZLE_OFFSET 12
#define CTRL_LCD_DATABUS_WIDTH_MASK (0x3 << 10)
#define CTRL_LCD_DATABUS_WIDTH_OFFSET 10
#define CTRL_LCD_DATABUS_WIDTH_16BIT (0 << 10)
#define CTRL_LCD_DATABUS_WIDTH_8BIT (1 << 10)
#define CTRL_LCD_DATABUS_WIDTH_18BIT (2 << 10)
#define CTRL_LCD_DATABUS_WIDTH_24BIT (3 << 10)
#define CTRL_SET_BUS_WIDTH(x) (((x) & 0x3) << 10)
#define CTRL_GET_BUS_WIDTH(x) (((x) >> 10) & 0x3)
#define CTRL_WORD_LENGTH_MASK (0x3 << 8)
#define CTRL_WORD_LENGTH_OFFSET 8
#define CTRL_WORD_LENGTH_16BIT (0 << 8)
#define CTRL_WORD_LENGTH_8BIT (1 << 8)
#define CTRL_WORD_LENGTH_18BIT (2 << 8)
#define CTRL_WORD_LENGTH_24BIT (3 << 8)
#define CTRL_SET_WORD_LENGTH(x) (((x) & 0x3) << 8)
#define CTRL_GET_WORD_LENGTH(x) (((x) >> 8) & 0x3)
#define CTRL_RGB_TO_YCBCR422_CSC (1 << 7)
#define CTRL_MASTER (1 << 5)
#define CTRL_DATA_FORMAT_16_BIT (1 << 3)
#define CTRL_DATA_FORMAT_18_BIT (1 << 2)
#define CTRL_DATA_FORMAT_24_BIT (1 << 1)
#define CTRL_RUN (1 << 0)

#define CTRL1_COMBINE_MPU_WR_STRB (1 << 27)
#define CTRL1_BM_ERROR_IRQ_EN (1 << 26)
#define CTRL1_BM_ERROR_IRQ (1 << 25)
#define CTRL1_RECOVERY_ON_UNDERFLOW (1 << 24)
#define CTRL1_INTERLACE_FIELDS (1 << 23)
#define CTRL1_START_INTERLACE_FROM_SECOND_FIELD (1 << 22)
#define CTRL1_FIFO_CLEAR (1 << 21)
#define CTRL1_IRQ_ON_ALTERNATE_FIELDS (1 << 20)
#define CTRL1_BYTE_PACKING_FORMAT_MASK (0xf << 16)
#define CTRL1_BYTE_PACKING_FORMAT_OFFSET 16
#define CTRL1_SET_BYTE_PACKAGING(x) (((x) & 0xf) << 16)
#define CTRL1_GET_BYTE_PACKAGING(x) (((x) >> 16) & 0xf)
#define CTRL1_OVERFLOW_IRQ_EN (1 << 15)
#define CTRL1_UNDERFLOW_IRQ_EN (1 << 14)
#define CTRL1_CUR_FRAME_DONE_IRQ_EN (1 << 13)
#define CTRL1_VSYNC_EDGE_IRQ_EN (1 << 12)
#define CTRL1_OVERFLOW_IRQ (1 << 11)
#define CTRL1_UNDERFLOW_IRQ (1 << 10)
#define CTRL1_CUR_FRAME_DONE_IRQ (1 << 9)
#define CTRL1_VSYNC_EDGE_IRQ (1 << 8)
#define CTRL1_BUSY_ENABLE (1 << 2)
#define CTRL1_MODE86 (1 << 1)
#define CTRL1_RESET (1 << 0)
#define CTRL1_IRQ_ENABLE_MASK (CTRL1_OVERFLOW_IRQ_EN | \
CTRL1_UNDERFLOW_IRQ_EN | \
CTRL1_CUR_FRAME_DONE_IRQ_EN | \
CTRL1_VSYNC_EDGE_IRQ_EN)
#define CTRL1_IRQ_ENABLE_SHIFT 12
#define CTRL1_IRQ_STATUS_MASK (CTRL1_OVERFLOW_IRQ | \
CTRL1_UNDERFLOW_IRQ | \
CTRL1_CUR_FRAME_DONE_IRQ | \
CTRL1_VSYNC_EDGE_IRQ)
#define CTRL1_IRQ_STATUS_SHIFT 8

#define CTRL2_OUTSTANDING_REQS_MASK (0x7 << 21)
#define CTRL2_OUTSTANDING_REQS_OFFSET 21
#define CTRL2_OUTSTANDING_REQS_REQ_1 (0x0 << 21)
#define CTRL2_OUTSTANDING_REQS_REQ_2 (0x1 << 21)
#define CTRL2_OUTSTANDING_REQS_REQ_4 (0x2 << 21)
#define CTRL2_OUTSTANDING_REQS_REQ_8 (0x3 << 21)
#define CTRL2_OUTSTANDING_REQS_REQ_16 (0x4 << 21)
#define CTRL2_BURST_LEN_8 (1 << 20)
#define CTRL2_ODD_LINE_PATTERN_MASK (0x7 << 16)
#define CTRL2_ODD_LINE_PATTERN_OFFSET 16
#define CTRL2_ODD_LINE_PATTERN_RGB (0x0 << 16)
#define CTRL2_ODD_LINE_PATTERN_RBG (0x1 << 16)
#define CTRL2_ODD_LINE_PATTERN_GBR (0x2 << 16)
#define CTRL2_ODD_LINE_PATTERN_GRB (0x3 << 16)
#define CTRL2_ODD_LINE_PATTERN_BRG (0x4 << 16)
#define CTRL2_ODD_LINE_PATTERN_BGR (0x5 << 16)
#define CTRL2_EVEN_LINE_PATTERN_MASK (0x7 << 12)
#define CTRL2_EVEN_LINE_PATTERN_OFFSET 12
#define CTRL2_EVEN_LINE_PATTERN_RGB (0x0 << 12)
#define CTRL2_EVEN_LINE_PATTERN_RBG (0x1 << 12)
#define CTRL2_EVEN_LINE_PATTERN_GBR (0x2 << 12)
#define CTRL2_EVEN_LINE_PATTERN_GRB (0x3 << 12)
#define CTRL2_EVEN_LINE_PATTERN_BRG (0x4 << 12)
#define CTRL2_EVEN_LINE_PATTERN_BGR (0x5 << 12)
#define CTRL2_READ_PACK_DIR (1 << 10)
#define CTRL2_READ_MODE_OUTPUT_IN_RGB_FORMAT (1 << 9)
#define CTRL2_READ_MODE_6_BIT_INPUT (1 << 8)
#define CTRL2_READ_MODE_NUM_PACKED_SUBWORDS_MASK (0x7 << 4)
#define CTRL2_READ_MODE_NUM_PACKED_SUBWORDS_OFFSET 4
#define CTRL2_INITIAL_DUMMY_READ_MASK (0x7 << 1)
#define CTRL2_INITIAL_DUMMY_READ_OFFSET 1

#define TRANSFER_COUNT_V_COUNT_MASK (0xffff << 16)
#define TRANSFER_COUNT_V_COUNT_OFFSET 16
#define TRANSFER_COUNT_H_COUNT_MASK (0xffff << 0)
#define TRANSFER_COUNT_H_COUNT_OFFSET 0
#define TRANSFER_COUNT_SET_VCOUNT(x) (((x) & 0xffff) << 16)
#define TRANSFER_COUNT_GET_VCOUNT(x) (((x) >> 16) & 0xffff)
#define TRANSFER_COUNT_SET_HCOUNT(x) ((x) & 0xffff)
#define TRANSFER_COUNT_GET_HCOUNT(x) ((x) & 0xffff)

#define CUR_BUF_ADDR_MASK 0xffffffff
#define CUR_BUF_ADDR_OFFSET 0

#define NEXT_BUF_ADDR_MASK 0xffffffff
#define NEXT_BUF_ADDR_OFFSET 0

#define TIMING_CMD_HOLD_MASK (0xff << 24)
#define TIMING_CMD_HOLD_OFFSET 24
#define TIMING_CMD_SETUP_MASK (0xff << 16)
#define TIMING_CMD_SETUP_OFFSET 16
#define TIMING_DATA_HOLD_MASK (0xff << 8)
#define TIMING_DATA_HOLD_OFFSET 8
#define TIMING_DATA_SETUP_MASK (0xff << 0)
#define TIMING_DATA_SETUP_OFFSET 0

#define VDCTRL0_VSYNC_OEB (1 << 29)
#define VDCTRL0_ENABLE_PRESENT (1 << 28)
#define VDCTRL0_VSYNC_ACT_HIGH (1 << 27)
#define VDCTRL0_HSYNC_ACT_HIGH (1 << 26)
#define VDCTRL0_DOTCLK_ACT_FALLING (1 << 25)
#define VDCTRL0_ENABLE_ACT_HIGH (1 << 24)
#define VDCTRL0_VSYNC_PERIOD_UNIT (1 << 21)
#define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT (1 << 20)
#define VDCTRL0_HALF_LINE (1 << 19)
#define VDCTRL0_HALF_LINE_MODE (1 << 18)
#define VDCTRL0_SET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff)
#define VDCTRL0_GET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff)

#define VDCTRL2_SET_HSYNC_PERIOD(x) ((x) & 0x3ffff)
#define VDCTRL2_GET_HSYNC_PERIOD(x) ((x) & 0x3ffff)

#define VDCTRL3_MUX_SYNC_SIGNALS (1 << 29)
#define VDCTRL3_VSYNC_ONLY (1 << 28)
#define SET_HOR_WAIT_CNT(x) (((x) & 0xfff) << 16)
#define GET_HOR_WAIT_CNT(x) (((x) >> 16) & 0xfff)
#define SET_VERT_WAIT_CNT(x) ((x) & 0xffff)
#define GET_VERT_WAIT_CNT(x) ((x) & 0xffff)

#define VDCTRL4_SET_DOTCLK_DLY(x) (((x) & 0x7) << 29) /* v4 only */
#define VDCTRL4_GET_DOTCLK_DLY(x) (((x) >> 29) & 0x7) /* v4 only */
#define VDCTRL4_SYNC_SIGNALS_ON (1 << 18)
#define SET_DOTCLK_H_VALID_DATA_CNT(x) ((x) & 0x3ffff)

#define DEBUG0_HSYNC (1 < 26)
#define DEBUG0_VSYNC (1 < 25)

#define MIN_XRES 120
#define MIN_YRES 120

#define RED 0
#define GREEN 1
#define BLUE 2
#define TRANSP 3

#define STMLCDIF_8BIT 1 /** pixel data bus to the display is of 8 bit width */
#define STMLCDIF_16BIT 0 /** pixel data bus to the display is of 16 bit width */
#define STMLCDIF_18BIT 2 /** pixel data bus to the display is of 18 bit width */
#define STMLCDIF_24BIT 3 /** pixel data bus to the display is of 24 bit width */

#define FB_SYNC_OE_LOW_ACT 0x80000000
#define FB_SYNC_CLK_LAT_FALL 0x40000000

enum mxsfb_devtype {
MXSFB_V3,
MXSFB_V4,
};

enum {
MPU_DATA,
MPU_CMD,
};

enum {
MPU_READ,
MPU_WRITE,
};

enum {
MPU_BUS_8080,
MPU_BUS_6800,
};

struct mpu_lcd_config {
u32 bus_mode;
u32 interface_width;
u32 panel_bpp;
bool lcd_reset_is_gpio;
int lcd_reset_gpio;
bool lcd_rs_is_gpio;
int lcd_rs_gpio;
};

/* CPU dependent register offsets */
struct mxsfb_devdata {
unsigned transfer_count;
unsigned data;
unsigned cur_buf;
unsigned next_buf;
unsigned debug0;
unsigned hs_wdth_mask;
unsigned hs_wdth_shift;
unsigned ipversion;
};

struct mxsfb_info;

struct mpu_lcd_callback {
/* callback for lcd panel operation */
void (*get_mpu_lcd_videomode)(struct fb_videomode **, int *,
struct mpu_lcd_config **);
int (*mpu_lcd_setup)(struct mxsfb_info *);
int (*mpu_lcd_poweroff)(struct mxsfb_info *);
};

struct mpu_match_lcd {
char *lcd_panel;
struct mpu_lcd_callback lcd_callback;
};

struct mxsfb_info {
struct fb_info fb_info;
struct platform_device *pdev;
struct clk *clk_pix;
struct clk *clk_axi;
struct clk *clk_disp_axi;
bool clk_pix_enabled;
bool clk_axi_enabled;
bool clk_disp_axi_enabled;
void __iomem *base; /* registers */
u32 sync; /* record display timing polarities */
unsigned allocated_size;
int enabled;
unsigned ld_intf_width;
unsigned dotclk_delay;
const struct mxsfb_devdata *devdata;
struct regulator *reg_lcd;
bool wait4vsync;
struct completion vsync_complete;
struct completion flip_complete;
int cur_blank;
int restore_blank;
char disp_dev[32];
struct mxc_dispdrv_handle *dispdrv;
int id;
struct fb_var_screeninfo var;
bool is_mpu_lcd;
struct mpu_lcd_config * mpu_lcd_sigs;
struct mpu_lcd_callback * mpu_lcd_functions;
};

unsigned int mxsfb_mpu_access(struct mxsfb_info *host, int mode, int rw, int data);

#ifdef CONFIG_FB_MXS_ST7789S_QVGA
void mpu_st7789s_get_lcd_videomode(struct fb_videomode **mode, int *size,
struct mpu_lcd_config **data);
int mpu_st7789s_lcd_setup(struct mxsfb_info * mxsfb);
int mpu_st7789s_lcd_poweroff(struct mxsfb_info * mxsfb);
#endif

#endif

mxsfb_st7789s_qvga.c

/*
* Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.

* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.

* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <linux/types.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>

#include "../mxsfb.h"

static struct fb_videomode st7789s_lcd_modedb[] = {
{
"ST7789S-QVGA", //const char *name; /* optional */
60, //u32 refresh; /* optional */
240, //u32 xres; //rotate 0
320, //u32 yres; //rotate 0
// 320, //u32 xres; //rotate 90
// 240, //u32 yres; //rotate 90
200000,//200000, //u32 pixclock;
0, //u32 left_margin;
0, //u32 right_margin;
0, //u32 upper_margin;
0, //u32 lower_margin;
0, //u32 hsync_len;
0, //u32 vsync_len;
0, //u32 sync;
FB_VMODE_NONINTERLACED, //u32 vmode; FB_VMODE_NONINTERLACED,
0, //u32 flag;
},
};

static struct mpu_lcd_config lcd_config = {
.bus_mode = MPU_BUS_8080,
.interface_width = 16,
.panel_bpp = 16,//18,
};
void mpu_st7789s_get_lcd_videomode(struct fb_videomode **mode, int *size,
struct mpu_lcd_config **data)
{
*mode = &st7789s_lcd_modedb[0];
*size = ARRAY_SIZE(st7789s_lcd_modedb);
*data = &lcd_config;
}

int mpu_st7789s_lcd_setup(struct mxsfb_info * mxsfb)
{
if (mxsfb == NULL)
return -1;

//msleep(120);

/* Sleep out */
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x11);

msleep(120);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x36);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00); //rotate 0
//mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xA0); //rotate 90

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x3A);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x05);

/*mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xB0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00);
//mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xF0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xD4);*/

//rotate0;
/*mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x3A); //rotate 90
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00); //rotate 90
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00); //rotate 90
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x01); //rotate 90
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x3F); //rotate 90

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x3B); //rotate 90
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00); //rotate 90
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00); //rotate 90
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00); //rotate 90
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xEF); //rotate 90*/

/* Analog setting */
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xB2);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0C);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0C);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x33);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x33);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xB7);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x35);

/* Power settings */
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xBB);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x28);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xC0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x2C);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xC2);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x01);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xC3);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x10);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xC4);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x20);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xC6);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0F);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xD0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xA4);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xA1);


/* Gamma setting */
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xE0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xD0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x02);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x07);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0A);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x28);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x32);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x44);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x42);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x06);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0E);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x12);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x14);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x17);

mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0xE1);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0xD0);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x00);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x02);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x07);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0A);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x28);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x31);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x54);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x47);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x0E);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x1C);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x17);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x1B);
mxsfb_mpu_access(mxsfb, MPU_DATA, MPU_WRITE, 0x1E);

/* Display on */
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x29);

/* Memory write */
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x2C);

return 0;
}

int mpu_st7789s_lcd_poweroff(struct mxsfb_info * mxsfb)
{
//msleep(120);

/* Display off */
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x28);

/* Sleep in */
mxsfb_mpu_access(mxsfb, MPU_CMD, MPU_WRITE, 0x10);

msleep(120);

return 0;
}

on your .dts there must be something like...

&lcdif {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lcdif_dat
&pinctrl_lcdif_ctrl>;
display = <&display0>;
status = "okay";

display0: display {
mpu-mode;
lcd_reset_gpio = <&gpio5 6 0>;
lcd_panel = "ST7789S-QVGA";
};
};

Thanx!

I also found that on void mxsfb_mpu_setup_refresh_data(struct mxsfb_info *host)

must be commented out:

#if 0
if (host->mpu_lcd_sigs->panel_bpp == 16) {
writel(CTRL_YCBCR422_INPUT |
(1 << CTRL_INPUT_DATA_SWIZZLE_OFFSET),
host->base + LCDC_CTRL + REG_SET);
}
#endif

after this lcd is ok and working.

Next I try to change panel from async to sync (using vsync and hsync) on a fly

Br,JPO

Hi Francesco,

I have followed your guidelines and adjusted the related source codes however I faced a problem during boot-up. The problem is that during the boot-up process, iMX6ULL freezes, i.e. it says "Starting Kernel..." and does not boot. I found out that the display configuration in the device tree causes the problem, see below lines

&lcdif {
   pinctrl-names = "default";
   pinctrl-0 = <&pinctrl_lcdif_dat
                     &pinctrl_lcdif_ctrl>;
   display = <&display0>;
   status = "okay";

   display0: display {
               mpu-mode;  /* When I comment this part, iMX6ULL completes the boot process */
               lcd_reset_gpio = <&gpio5 6 0>;
               lcd_panel = "ST7789S-QVGA";
    };
};

Have you faced such a problem? If so could you help me on the issue?

Kernel Version I am using is  fsl-yocto-L4.1.15_2.0.0-ga

I would appreciate if anyone can help me on the issue,

Thanks and best regards.

Qiang_FSL‌ dingfeng ebiz_ws_prod

Hi Qiang,

I faced the same problem as ding feng, I followed the steps you mentioned for applying the patch however I could not make my TFT work. Do you have any ideas why I am getting this error, "failed to find mxc display driver".

Thanks

I think the error "failed to find mxc display driver" can be ignored. It is not related to the MPU display driver.

You can check if the MPU panel setup code is called or not, and check the MPU interface signals.

Qiang_FSL

mxsfb 21c8000.lcdif: mxsfb_mpu_wait_for_ready timeout!

What about this error? when I try to run the test program I get the same error, do you have any ideas? Thanks

This is just long shot but I fixed similar problem when using mpu mode of lcd w uboot and kernel::

https://community.nxp.com/thread/485727 

My kernel is still different, 4.9.11

In my experience, the lcdif stuff is *very* picky about how it is initialized and if you do it wrong, the system will freeze. One thing I noticed was that the LCD stuff was often broken by U-boot setting up and displaying a splash screen as though there were a parallel interface LCD connected. Make sure you have CONFIG_VIDEO turned off in U-boot.

I recently had a need to use an MPU panel on the iMX7d and I am running the latest release, 4.9.11.

I've updated the patches for the imx_4.9.11_1.0.0_ga branch and fixed a few bugs:

Patch 1 and Patch 2 are updated versions of the patches originally attached to this post.

Patch 3 fixes the rs-gpio devicetree argument

Patch 4 fixes the infinite recursive loop in the msxfb_resume function

I also included two feature additions that I made:

Patch 5 adds a timer to automatically refresh the panel at ~30fps so you don't have to do the ioctl

Patch 6 fixes an issue I had where the panel did not work after boot until after the screen was blanked and then unblanked

The automatic panel refresh patch is most definitely not ideal. You will likely see tearing because it does not wait for the best time to update the panel. It is also probably not the best way to implement that type of thing in the kernel. The CPU hit does appear to be negligible though.

Patch 6 is also probably not the ideal fix for this issue. All it does is add a call to mxsfb_blank(FB_BLANK_NORMAL) to the probe function before it unblanks the panel. This may negatively affect parallel interface panels, I did not test that. It may also cause the screen to flicker during boot.

These two fixes were quick fixes that were sufficient for me so I do not plan to work on them any more (I'm only temporarily using an MPU panel for development but not release). Feel free to use them or not, they're available for your use at your own risk.

Patch List:

0001-Add-ST7789S-MPU-LCD-support-for-iMX6UL-board.patch
0002-Add-ST7735R-MPU-LCD-support-for-iMX7D-board.patch
0003-mxsfb-mxsfb_init-Fix-lcd_rs_gpio.patch
0004-mxsfb-mxsfb_resume-Remove-infinite-recursive-loop.patch
0005-mxsfb-Add-automatic-refresh-timer-for-mpu-panels.patch
0006-mxsfb-Fix-broken-screen-on-boot.patch

Hi Joe,

thank you for your comments, I will try the patches and your recommendations. Hope it works, thanks again!

Hi Joe,

I have tried your suggestions however still I could not make the TFT work. I wonder what were the specs of your TFT display and which root file system you have used. For example my TFT is 16-bit wide data interface, 18bpp, 320x480 resolution and I am using a custom root file system. Kernel version is 4.1.15_2.0.0_ga

I have checked that the CONFIG_VIDEO is not defined in the U-Boot source. The processor boots but when I run the unit test for the framebuffer, the processor freezes, by the way I get such a display

IMG_8675.JPG

I will try to implement your patches into 4.9.11 kernel to check if it works.

Finally the display is working. The problem was related to the init code of the TFT, make sure you send the correct initialization code to the TFT panel. Also make sure the hardware configuration for interface selection (MPU, RGB, SPI, etc.) is correct. 

If you see multiple images at the same time on the display, correct the TFT resolution variables from the driver code itself. Probably the x and y resolutions is entered in the reverse order.

Good luck!

Hi all,

First, thanks for your work :smileyhappy:

I'm trying to use your driver on IMX7D but I've some issue with the LCD_RS signal.

I use the LCD_RESET pin. From the manual it seem that according to the mode, the LCD_RESET is used as a RESET or in MPU mode, as RS pin but the signal is not as it must be. Using a logic analyzer I can see that the signal is wrong.

Does the LCD_RS is correctly handled on your board? or are you using a common gpio for this signal because the controler does not handle it correctly?

Thks.

Version history
Last update:
‎05-30-2016 10:06 PM
Updated by: