驱动编写

该章节请参考【20.lcd】工程

修改设备树

自带的设备树关于fb的总共有两个文件arch\arm\boot\dts\目录下imx6ull.dtsiimx6ull-14x14-evk.dts,我们只需修改imx6ull-14x14-evk.dts即可。

如果不想破坏原有的设备树文件可以拷贝一份arch\arm\boot\dts\目录下的imx6ull-14x14-evk.dts重新命名例如:imx6ull-14x14-xym.dts,然后修改arch\arm\boot\dts\Makefile文件,新增自己的设备树文件即可,如下:这样在make dtbs的时候就会生成imx6ull-14x14-xym.dtb文件。

../../_images/image-20210913192345169.png

现在我们修改设备树如下步骤:

  1. arch\arm\boot\dts\imx6ull.dtsi:这里不做修改,说明如下:

    ../../_images/image-20210913190416530.png

  2. arch\arm\boot\dts\imx6ull-14x14-evk.dts:禁能自带的lcdif和背光PWM,需要修改的地方如下:

    ../../_images/image-20210913190651839.png

    ../../_images/image-20210913190845542.png

  3. LCD 引脚和背光:需要修改的地方如下

    ../../_images/image-20210913191403400.pngimage-20210913191403400

  4. 新增自己的fb节点如下:在根节点下添加如下:

    ../../_images/image-20210913192100803.png

添加好后完整文件如下:

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

/dts-v1/;

#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"

/ {
	model = "Freescale i.MX6 ULL 14x14 EVK Board";
	compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

	chosen {
		stdout-path = &uart1;
	};

	memory {
		reg = <0x80000000 0x20000000>;
	};

	reserved-memory {
		#address-cells = <1>;
		#size-cells = <1>;
		ranges;

		linux,cma {
			compatible = "shared-dma-pool";
			reusable;
			size = <0x14000000>;
			linux,cma-default;
		};
	};

	backlight {
		compatible = "pwm-backlight";
		pwms = <&pwm1 0 5000000>;
		brightness-levels = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100>;
		default-brightness-level = <50>;
		status = "okay";
	};
		
	framebuffer-mylcd {
			compatible = "flyrobot,lcd_drv"; 	// 设备树节点名字
			reg = <0x021c8000 0x4000>; 			// 基地值
	        pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_lcdif_dat 	// 使用pinctrl设置数据线控制线和背光的gpio属性
		     			 &pinctrl_lcdif_ctrl
		     			 &pinctrl_backlight>;
			backlight-gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;// 默认打开背光

			clocks = <&clks IMX6UL_CLK_LCDIF_PIX>, // LCD控制器所需的时钟,这里使用时钟子系统进行控制,pix和axi
                     <&clks IMX6UL_CLK_LCDIF_APB>;
            clock-names = "pix", "axi";
            
	        display = <&displayA>;
			status = "okay";
			displayA: display@A {
				bits-per-pixel = <32>;  // 一个像素多少个bit 这里使用888 模式一共32bit
				bus-width = <24>;		// 数据线宽度,这里我们硬件上使用了24根数据线
				
				display-timings {
					native-mode = <&timingA>;

					timingA: timing0_800x480 {
						mode_name = "TFT50AB";
						clock-frequency = <50000000>;// 像素时钟
						hactive = <800>;
						vactive = <480>;

						hfront-porch = <22>;  // 行前肩
						hback-porch = <46>;   // 行后肩
						hsync-len = <20>;     // 行脉宽
						
						vback-porch = <23>;  // 场后肩
						vfront-porch = <22>; // 场前肩
						vsync-len = <3>;     // 场脉宽

						hsync-active = <0>; 	// 行数据线有效极性
						vsync-active = <0>;		// 场数据线有效极性
						de-active = <1>;		// de数据线有效极性
						pixelclk-active = <0>;  // 时钟数据线有效极性
					};

				};
			};            
					
		};

	pxp_v4l2 {
		compatible = "fsl,imx6ul-pxp-v4l2", "fsl,imx6sx-pxp-v4l2", "fsl,imx6sl-pxp-v4l2";
		status = "okay";
	};

	regulators {
		compatible = "simple-bus";
		#address-cells = <1>;
		#size-cells = <0>;

		reg_can_3v3: regulator@0 {
			compatible = "regulator-fixed";
			reg = <0>;
			regulator-name = "can-3v3";
			regulator-min-microvolt = <3300000>;
			regulator-max-microvolt = <3300000>;
			/*gpios = <&gpio_spi 3 GPIO_ACTIVE_LOW>;*/
		};

		reg_wifi_vmmc: regulator@1 {
			compatible = "regulator-fixed";
			regulator-name = "WIFI_3V3";
			regulator-min-microvolt = <3300000>;
			regulator-max-microvolt = <3300000>;
			gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>;
			enable-active-high;
		};
		reg_vref_3v3: regulator@2 {
			compatible = "regulator-fixed";
			regulator-name = "vref-3v3";
			regulator-min-microvolt = <3300000>;
			regulator-max-microvolt = <3300000>;
		};
/*
		reg_gpio_dvfs: regulator-gpio {
			compatible = "regulator-gpio";
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_dvfs>;
			regulator-min-microvolt = <1300000>;
			regulator-max-microvolt = <1400000>;
			regulator-name = "gpio_dvfs";
			regulator-type = "voltage";
			gpios = <&gpio5 3 GPIO_ACTIVE_HIGH>;
			states = <1300000 0x1 1400000 0x0>;
		};

		reg_bt: regulator-bt-reg {
			compatible = "regulator-gpio";
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_dvfs>;
			regulator-min-microvolt = <1300000>;
			regulator-max-microvolt = <1400000>;
			regulator-name = "gpio_dvfs";
			regulator-type = "voltage";
			gpios = <&gpio5 3 GPIO_ACTIVE_HIGH>;
			states = <1300000 0x1 1400000 0x0>;
		};*/
	};
/****************************************修改的地�?***************************************************/
/*
	leds {
		compatible = "gpio-leds";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_led>;

		led0: cpu {
			label = "cpu";
			gpios = <&gpio5 3 GPIO_ACTIVE_LOW>;
			default-state = "on";
			linux,default-trigger = "heartbeat";
		};
	};
*/
	xym_led {
		compatible    = "xym-led";
		pinctrl-names = "default";
		pinctrl-0     = <&pinctrl_led>;
		led-gpio = <&gpio5 3 GPIO_ACTIVE_LOW>; 
	};
/****************************************修改的地�?***************************************************/

	pwm_leds{
		compatible = "pwm-leds";
		status = "okay";

		red {
			label = "red";
			active-low;
			pwms = <&pwm3 0 5000>;
			max-brightness = <255>;
		};

		blue {
			label =  "blue";
			active-low;
			pwms = <&pwm7 0 5000>;
			max-brightness = <255>;
		};

		green {
			label = "green";
			active-low;
			pwms = <&pwm8 0 5000>;
			max-brightness = <255>;
		};
	};

	ir_recv:ir-receiver {
			compatible = "gpio-ir-receiver";
			#gpio-cells = <1>;
			gpios = <&gpio1 21 GPIO_ACTIVE_LOW>;
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_remote_control>;
			linux,rc-map-name = "rc-lirc";
			status = "okay";
	};

	gpio-keys {
			compatible = "gpio-keys";
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_gpio_keys>;

			Key0{
				label = "Key 0";
				gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>;
				linux,code = <KEY_0>;
		};
	};

	sound {
		compatible = "fsl,imx6ul-evk-wm8960",
			   "fsl,imx-audio-wm8960";
		model = "wm8960-audio";
		cpu-dai = <&sai2>;
		audio-codec = <&codec>;
		asrc-controller = <&asrc>;
		codec-master;
		gpr = <&gpr 4 0x100000 0x100000>;
		/*
		 * hp-det = <hp-det-pin hp-det-polarity>;
		 * hp-det-pin: JD1 JD2  or JD3
		 * hp-det-polarity = 0: hp detect high for headphone
		 * hp-det-polarity = 1: hp detect high for speaker
		 */
		hp-det = <3 1>;
		/*hp-det-gpios = <&gpio5 4 0>;
		mic-det-gpios = <&gpio5 4 0>;*/
		audio-routing =
			"Headphone Jack", "HP_L",
			"Headphone Jack", "HP_R",
			"Ext Spk", "SPK_LP",
			"Ext Spk", "SPK_LN",
			"Ext Spk", "SPK_RP",
			"Ext Spk", "SPK_RN",
			"LINPUT2", "Mic Jack",
			"LINPUT3", "Mic Jack",
			"RINPUT1", "Main MIC",
			"RINPUT2", "Main MIC",
			"Mic Jack", "MICB",
			"Main MIC", "MICB",
			"CPU-Playback", "ASRC-Playback",
			"Playback", "CPU-Playback",
			"ASRC-Capture", "CPU-Capture",
			"CPU-Capture", "Capture";
			status = "okay";
	};

	spi4 {
		compatible = "spi-gpio";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_spi4>;
		pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
		status = "okay";
		gpio-sck = <&gpio5 11 0>;
		gpio-mosi = <&gpio5 10 0>;
		cs-gpios = <&gpio5 7 0>;
		num-chipselects = <1>;
		#address-cells = <1>;
		#size-cells = <0>;

		gpio_spi: gpio_spi@0 {
			compatible = "fairchild,74hc595";
			gpio-controller;
			#gpio-cells = <2>;
			reg = <0>;
			registers-number = <1>;
			registers-default = /bits/ 8 <0x57>;
			spi-max-frequency = <100000>;
		};
	};

	sound-spdif {
		compatible = "fsl,imx-audio-spdif";
		model = "imx-spdif";
		spdif-controller = <&spdif>;
		spdif-in;
		spdif-out;
	};

	sii902x_reset: sii902x-reset {
		compatible = "gpio-reset";
		reset-gpios = <&gpio_spi 0 GPIO_ACTIVE_LOW>;
		reset-delay-us = <100000>;
		#reset-cells = <0>;
	};

	ds18b20{
		compatible = "w1-gpio";
		gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
		linux,open-drain;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_onewire>;
		status = "disable";
	};

	dht11{
		compatible = "dht11";
		gpios = <&gpio1 2 GPIO_ACTIVE_LOW>;
		linux,open-drain;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_onewire>;
		status = "okay";
	};
};

&cpu0 {
	arm-supply = <&reg_arm>;
	soc-supply = <&reg_soc>;
	clock-frequency = <800000000>;
	/*dc-supply = <&reg_gpio_dvfs>;*/
};

&clks {
	assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
	assigned-clock-rates = <786432000>;
};

&fec1 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_enet1>;
	phy-mode = "rmii";
	phy-handle = <&ethphy0>;
	status = "okay";
};

&fec2 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_enet2>;
	phy-mode = "rmii";
	phy-handle = <&ethphy1>;
	status = "okay";

	mdio {
		#address-cells = <1>;
		#size-cells = <0>;

		ethphy0: ethernet-phy@2 {
			compatible = "ethernet-phy-ieee802.3-c22";
			reg = <2>;
		};

		ethphy1: ethernet-phy@1 {
			compatible = "ethernet-phy-ieee802.3-c22";
			reg = <1>;
		};
	};
};

&flexcan1 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_flexcan1>;
	/*xceiver-supply = <&reg_can_3v3>;*/
	status = "okay";
};
/*
&flexcan2 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_flexcan2>;
	/*xceiver-supply = <&reg_can_3v3>;
	status = "okay";    /*can & UART2 can not use as same time
};*/

&gpc {
	fsl,cpu_pupscr_sw2iso = <0x1>;
	fsl,cpu_pupscr_sw = <0x0>;
	fsl,cpu_pdnscr_iso2sw = <0x1>;
	fsl,cpu_pdnscr_iso = <0x1>;
	fsl,ldo-bypass = <0>; /* DCDC, ldo-enable */
};

&i2c1 {
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c1>;
	status = "okay";

	mpu6050@68 {
		compatible = "mpu6050,INV_MPU6050";
		reg = <0x68>;
		position = <1>;
	};

	gt9xx@5d {
		compatible = "goodix,gt9xx";
		reg = <0x5d>;
		status = "okay";
		interrupt-parent = <&gpio5>;
		interrupts = <9 IRQ_TYPE_EDGE_FALLING>;
		pinctrl-names = "pinctrl_tsc_reset","pinctrl_tsc_irq";
		pinctrl-0 = <&pinctrl_tsc_reset>;
		pinctrl-1 = <&pinctrl_tsc_irq>;
		/*pinctrl-names = "default", "int-output-low", "int-output-high", "int-input";
		pinctrl-0 = <&ts_int_default>;
		pinctrl-1 = <&ts_int_output_low>;
		pinctrl-2 = <&ts_int_output_high>;
		pinctrl-3 = <&ts_int_input>;
		*/
		reset-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>;
		irq-gpios = <&gpio5 9 IRQ_TYPE_EDGE_FALLING>;
		irq-flags = <2>;		/*1:rising 2: falling*/

		touchscreen-max-id = <1>;
		touchscreen-size-x = <800>;
		touchscreen-size-y = <480>;
		touchscreen-max-w = <1024>;
		touchscreen-max-p = <1024>;
		/*touchscreen-key-map = <172>, <158>;*/ /*KEY_HOMEPAGE, KEY_BACK*/

		goodix,type-a-report = <0>;
		goodix,driver-send-cfg = <0>;
		goodix,create-wr-node = <1>;
		goodix,wakeup-with-reset = <0>;
		goodix,resume-in-workqueue = <0>;
		goodix,int-sync = <1>;
		goodix,swap-x2y = <0>;
		goodix,esd-protect = <0>;
		goodix,pen-suppress-finger = <0>;
		goodix,auto-update = <0>;
		goodix,auto-update-cfg = <0>;
		goodix,power-off-sleep = <0>;

		/*7*/
		goodix,cfg-group0 = [
		00 20 03 E0 01 05 0D 00 01 08
		28 0F 50 32 03 05 00 00 00 00
		00 00 00 00 00 00 00 8A 2A 0C
		45 47 0C 08 00 00 00 02 02 2D
		00 00 00 00 00 03 64 32 00 00
		00 28 64 94 C5 02 07 00 00 04
		9C 2C 00 8F 34 00 84 3F 00 7C
		4C 00 77 5B 00 77 00 00 00 00
		00 00 00 00 00 00 00 00 00 00
		00 00 00 00 00 00 00 00 00 00
		00 00 00 00 00 00 00 00 00 00
		00 00 18 16 14 12 10 0E 0C 0A
		08 06 04 02 FF FF 00 00 00 00
		00 00 00 00 00 00 00 00 00 00
		00 00 16 18 1C 1D 1E 1F 20 21
		22 24 13 12 10 0F 0A 08 06 04
		02 00 FF FF FF FF FF FF 00 00
		00 00 00 00 00 00 00 00 00 00
		00 00 00 00 24 01
		];

		/*4.3*/
		goodix,cfg-group1 = [
		97 E0 01 10 01 05 0D 00 01 00
		00 05 5A 46 53 11 00 00 11 11
		14 14 14 22 0A 04 00 00 00 00
		00 00 53 00 14 00 00 84 00 00
		3C 00 00 64 1E 28 87 27 08 32
		34 05 0D 20 33 60 11 02 24 00
		00 64 80 80 14 02 00 00 54 89
		68 85 6D 82 72 80 76 7D 7B 7B
		00 00 00 00 00 00 00 F0 50 3C
		FF FF 07 00 00 00 02 14 14 03
		04 00 21 64 0A 00 00 00 00 00
		00 00 00 00 00 00 00 00 00 00
		00 00 00 00 00 00 00 00 00 00
		32 20 50 3C 3C 00 00 00 00 00
		0D 06 0C 05 0B 04 0A 03 FF FF
		FF FF FF FF 00 01 02 03 04 05
		06 07 08 09 0A 0B 0C 0D FF FF
		FF FF FF FF FF FF FF FF FF FF
		00 00 00 00 00 00 00 00 00 00
		00 00 00 00 3C 00 05 1E 00 02
		2A 1E 19 14 02 00 03 0A 05 00
		00 00 00 00 00 00 01 FF FF 86
		22 03 00 00 33 00 0F 00 00 00
		50 3C 50 00 00 00 00 2A 01
		];

		/*5*/
		goodix,cfg-group2 = [
		00 20 03 E0 01 05 3C 00 01 08
		28 0C 50 32 03 05 00 00 00 00
		00 00 00 17 19 1E 14 8B 2B 0D
		33 35 0C 08 00 00 00 9A 03 11
		00 01 00 00 00 00 00 32 00 00
		00 20 58 94 C5 02 00 00 00 04
		B0 23 00 93 2B 00 7B 35 00 69
		41 00 5B 4F 00 5B 00 00 00 00
		00 00 00 00 00 00 00 00 00 00
		00 00 00 00 00 00 00 00 00 00
		00 00 00 00 00 00 00 00 00 00
		00 00 02 04 06 08 0A 0C 0E 10
		12 14 16 18 1A FF 00 00 00 00
		00 00 00 00 00 00 00 00 00 00
		00 00 00 02 04 06 08 0A 0C 0F
		10 12 13 16 18 1C 1D 1E 1F 20
		21 22 24 26 FF FF FF FF 00 00
		00 FF FF FF FF FF FF FF FF FF
		FF FF FF FF 48 01
		];

	};
	/*
	mag3110@0e {
		compatible = "fsl,mag3110";
		reg = <0x0e>;
		position = <2>;
	};

	fxls8471@1e {
		compatible = "fsl,fxls8471";
		reg = <0x1e>;
		position = <0>;
		interrupt-parent = <&gpio5>;
		interrupts = <0 8>;
	};*/
};

&i2c2 {
	clock_frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c2>;
	status = "okay";

	codec: wm8960@1a {
		compatible = "wlf,wm8960";
		reg = <0x1a>;
		clocks = <&clks IMX6UL_CLK_SAI2>;
		clock-names = "mclk";
		wlf,shared-lrclk;
	};

	sii902x: sii902x@39 {
		compatible = "SiI,sii902x";
		pinctrl-names = "default";
		reset-names="sii902x";
		pinctrl-0 = <&pinctrl_sii902x>;
		resets = <&sii902x_reset>;
		interrupt-parent = <&gpio1>;
		interrupts = <18 IRQ_TYPE_EDGE_FALLING>;
		mode_str ="1360x768M@60";
		bits-per-pixel = <16>;
		reg = <0x39>;
		status = "okay";    /* HDMI 使能 */
	};

	ov5640: ov5640@3c {
		compatible = "ovti,ov5640";
		reg = <0x3c>;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_csi1>;
		clocks = <&clks IMX6UL_CLK_CSI>;
		clock-names = "csi_mclk";
		pwn-gpios = <&gpio_spi 6 1>;
		rst-gpios = <&gpio_spi 5 0>;
		csi_id = <0>;
		mclk = <24000000>;
		mclk_source = <0>;
		status = "disable";    /* 摄像头使�?*/
		port {
			ov5640_ep: endpoint {
				remote-endpoint = <&csi1_ep>;
			};
		};
	};
};

&csi {
	status = "disable";  /* 摄像头总线使能 */

	port {
		csi1_ep: endpoint {
			remote-endpoint = <&ov5640_ep>;
		};
	};
};

&iomuxc {
	pinctrl-names = "default";
	imx6ul-evk {
		pinctrl_gpmi_nand: gpmi-nand {
			fsl,pins = <
				MX6UL_PAD_NAND_CLE__RAWNAND_CLE         0xb0b1
				MX6UL_PAD_NAND_ALE__RAWNAND_ALE         0xb0b1
				MX6UL_PAD_NAND_WP_B__RAWNAND_WP_B       0xb0b1
				MX6UL_PAD_NAND_READY_B__RAWNAND_READY_B 0xb000
				MX6UL_PAD_NAND_CE0_B__RAWNAND_CE0_B     0xb0b1
				MX6UL_PAD_NAND_CE1_B__RAWNAND_CE1_B     0xb0b1
				MX6UL_PAD_NAND_RE_B__RAWNAND_RE_B       0xb0b1
				MX6UL_PAD_NAND_WE_B__RAWNAND_WE_B       0xb0b1
				MX6UL_PAD_NAND_DATA00__RAWNAND_DATA00   0xb0b1
				MX6UL_PAD_NAND_DATA01__RAWNAND_DATA01   0xb0b1
				MX6UL_PAD_NAND_DATA02__RAWNAND_DATA02   0xb0b1
				MX6UL_PAD_NAND_DATA03__RAWNAND_DATA03   0xb0b1
				MX6UL_PAD_NAND_DATA04__RAWNAND_DATA04   0xb0b1
				MX6UL_PAD_NAND_DATA05__RAWNAND_DATA05   0xb0b1
				MX6UL_PAD_NAND_DATA06__RAWNAND_DATA06   0xb0b1
				MX6UL_PAD_NAND_DATA07__RAWNAND_DATA07   0xb0b1
			>;
		};
		pinctrl_remote_control: remote_control {
			fsl,pins = <
				MX6UL_PAD_UART2_RX_DATA__GPIO1_IO21	0x000010B1 /* remote control        add by pengjie 20190703*/
			>;
		};

		pinctrl_csi1: csi1grp {
			fsl,pins = <
				MX6UL_PAD_CSI_MCLK__CSI_MCLK		0x1b088
				MX6UL_PAD_CSI_PIXCLK__CSI_PIXCLK	0x1b088
				MX6UL_PAD_CSI_VSYNC__CSI_VSYNC		0x1b088
				MX6UL_PAD_CSI_HSYNC__CSI_HSYNC		0x1b088
				MX6UL_PAD_CSI_DATA00__CSI_DATA02	0x1b088
				MX6UL_PAD_CSI_DATA01__CSI_DATA03	0x1b088
				MX6UL_PAD_CSI_DATA02__CSI_DATA04	0x1b088
				MX6UL_PAD_CSI_DATA03__CSI_DATA05	0x1b088
				MX6UL_PAD_CSI_DATA04__CSI_DATA06	0x1b088
				MX6UL_PAD_CSI_DATA05__CSI_DATA07	0x1b088
				MX6UL_PAD_CSI_DATA06__CSI_DATA08	0x1b088
				MX6UL_PAD_CSI_DATA07__CSI_DATA09	0x1b088
			>;
		};

		pinctrl_enet1: enet1grp {
			fsl,pins = <
				MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN	0x1b0b0
				MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER	0x1b0b0
				MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00	0x1b0b0
				MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01	0x1b0b0
				MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN	0x1b0b0
				MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00	0x1b0b0
				MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01	0x1b0b0
				MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1	0x4001b031
			>;
		};

		pinctrl_enet2: enet2grp {
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO07__ENET2_MDC		0x1b0b0
				MX6UL_PAD_GPIO1_IO06__ENET2_MDIO	0x1b0b0
				MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN	0x1b0b0
				MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER	0x1b0b0
				MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00	0x1b0b0
				MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01	0x1b0b0
				MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN	0x1b0b0
				MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00	0x1b0b0
				MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01	0x1b0b0
				MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2	0x4001b031
			>;
		};

		pinctrl_flexcan1: flexcan1grp{
			fsl,pins = <
				MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX	0x1b020
				MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX	0x1b020
			>;
		};

		pinctrl_flexcan2: flexcan2grp{
			fsl,pins = <
				MX6UL_PAD_UART2_RTS_B__FLEXCAN2_RX	0x1b020
				MX6UL_PAD_UART2_CTS_B__FLEXCAN2_TX	0x1b020
			>;
		};

		pinctrl_i2c1: i2c1grp {
			fsl,pins = <
				MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
				MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
			>;
		};

		pinctrl_i2c2: i2c2grp {
			fsl,pins = <
				MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x4001b8b0
				MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0
			>;
		};

		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
				MX6UL_PAD_LCD_DATA16__LCDIF_DATA16  0x79
				MX6UL_PAD_LCD_DATA17__LCDIF_DATA17  0x79
				MX6UL_PAD_LCD_DATA18__LCDIF_DATA18  0x79
				MX6UL_PAD_LCD_DATA19__LCDIF_DATA19  0x79
				MX6UL_PAD_LCD_DATA20__LCDIF_DATA20  0x79
				MX6UL_PAD_LCD_DATA21__LCDIF_DATA21  0x79
				MX6UL_PAD_LCD_DATA22__LCDIF_DATA22  0x79
				MX6UL_PAD_LCD_DATA23__LCDIF_DATA23  0x79
			>;
		};

		pinctrl_lcdif_ctrl: lcdifctrlgrp {
			fsl,pins = <
				MX6UL_PAD_LCD_CLK__LCDIF_CLK	    0x79
				MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE  0x79
				MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC    0x79
				MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC    0x79
			>;
		};
		
		pinctrl_backlight: blgrp{
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO08__GPIO1_IO08    0x000010B0 // 新增自己的背光控制引脚
			>;
		};
		
		pinctrl_pwm1: pwm1grp {
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO08__PWM1_OUT   0x000010B1
			>;
		};

		pinctrl_pwm3: pwm3grp {
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO04__PWM3_OUT   0x000010B1
			>;
		};

		pinctrl_pwm7: pwm7grp {
			fsl,pins = <
				MX6UL_PAD_CSI_VSYNC__PWM7_OUT   0x000010B1
			>;
		};

		pinctrl_pwm8: pwm8grp {
			fsl,pins = <
				MX6UL_PAD_CSI_HSYNC__PWM8_OUT   0x000010B1
			>;
		};

		pinctrl_qspi: qspigrp {
			fsl,pins = <
				MX6UL_PAD_NAND_WP_B__QSPI_A_SCLK      0x70a1
				MX6UL_PAD_NAND_READY_B__QSPI_A_DATA00 0x70a1
				MX6UL_PAD_NAND_CE0_B__QSPI_A_DATA01   0x70a1
				MX6UL_PAD_NAND_CE1_B__QSPI_A_DATA02   0x70a1
				MX6UL_PAD_NAND_CLE__QSPI_A_DATA03     0x70a1
				MX6UL_PAD_NAND_DQS__QSPI_A_SS0_B      0x70a1
			>;
		};

		pinctrl_sai2: sai2grp {
			fsl,pins = <
				MX6UL_PAD_JTAG_TDI__SAI2_TX_BCLK	0x17088
				MX6UL_PAD_JTAG_TDO__SAI2_TX_SYNC	0x17088
				MX6UL_PAD_JTAG_TRST_B__SAI2_TX_DATA	0x11088
				MX6UL_PAD_JTAG_TCK__SAI2_RX_DATA	0x11088
				MX6UL_PAD_JTAG_TMS__SAI2_MCLK		0x17088
			>;
		};

		pinctrl_spdif: spdifgrp {
				fsl,pins = <
				MX6UL_PAD_JTAG_MOD__SPDIF_OUT		0x1b0b0
				MX6UL_PAD_GPIO1_IO09__SPDIF_IN		0x1b0b0
			>;
		};

		pinctrl_uart1: uart1grp {
			fsl,pins = <
				MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
				MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
			>;
		};

		pinctrl_uart2: uart2grp {
			fsl,pins = <
				MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX	0x1b0b1
				MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX	0x1b0b1
			>;
		};

		pinctrl_uart3: uart3grp {
			fsl,pins = <
				MX6UL_PAD_UART3_TX_DATA__UART3_DCE_TX	0x1b0b1
				MX6UL_PAD_UART3_RX_DATA__UART3_DCE_RX	0x1b0b1
			>;
		};

		pinctrl_uart2dte: uart2dtegrp {
			fsl,pins = <
				MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX	0x1b0b1
				MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX	0x1b0b1
				/*MX6UL_PAD_UART3_RX_DATA__UART2_DCE_RTS	0x1b0b1
				MX6UL_PAD_UART3_TX_DATA__UART2_DCE_CTS	0x1b0b1    pengjie 20190622*/
				MX6UL_PAD_UART2_CTS_B__UART2_DCE_CTS	0x1b0b1
				MX6UL_PAD_UART2_RTS_B__UART2_DCE_RTS	0x1b0b1
			>;
		};
		pinctrl_485: 485grp {
			fsl,pins = <
				MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX	0x1b0b1
				MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX	0x1b0b1
				MX6UL_PAD_UART2_CTS_B__UART2_DCE_CTS	0x1b0b1 /* RS485 RE/DE */
			>;
		};
		pinctrl_usdhc1: usdhc1grp {
			fsl,pins = <
				MX6UL_PAD_SD1_CMD__USDHC1_CMD     0x17059
				MX6UL_PAD_SD1_CLK__USDHC1_CLK     0x10071
				MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059
				MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059
				MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059
				MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059
			>;
		};

		pinctrl_usdhc1_100mhz: usdhc1grp100mhz {
			fsl,pins = <
				MX6UL_PAD_SD1_CMD__USDHC1_CMD     0x170b9
				MX6UL_PAD_SD1_CLK__USDHC1_CLK     0x100b9
				MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170b9
				MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170b9
				MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170b9
				MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170b9
			>;
		};

		pinctrl_usdhc1_200mhz: usdhc1grp200mhz {
			fsl,pins = <
				MX6UL_PAD_SD1_CMD__USDHC1_CMD     0x170f9
				MX6UL_PAD_SD1_CLK__USDHC1_CLK     0x100f9
				MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170f9
				MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170f9
				MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170f9
				MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170f9
			>;
		};

		pinctrl_usdhc2: usdhc2grp {
			fsl,pins = <
				MX6UL_PAD_NAND_RE_B__USDHC2_CLK     0x10069
				MX6UL_PAD_NAND_WE_B__USDHC2_CMD     0x17059
				MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x17059
				MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x17059
				MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x17059
				MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x17059
			>;
		};

		pinctrl_usdhc2_8bit: usdhc2grp_8bit {
			fsl,pins = <
				MX6UL_PAD_NAND_RE_B__USDHC2_CLK     0x10069
				MX6UL_PAD_NAND_WE_B__USDHC2_CMD     0x17059
				MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x17059
				MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x17059
				MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x17059
				MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x17059
				MX6UL_PAD_NAND_DATA04__USDHC2_DATA4 0x17059
				MX6UL_PAD_NAND_DATA05__USDHC2_DATA5 0x17059
				MX6UL_PAD_NAND_DATA06__USDHC2_DATA6 0x17059
				MX6UL_PAD_NAND_DATA07__USDHC2_DATA7 0x17059
			>;
		};

		pinctrl_usdhc2_8bit_100mhz: usdhc2grp_8bit_100mhz {
			fsl,pins = <
				MX6UL_PAD_NAND_RE_B__USDHC2_CLK     0x100b9
				MX6UL_PAD_NAND_WE_B__USDHC2_CMD     0x170b9
				MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x170b9
				MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x170b9
				MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x170b9
				MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x170b9
				MX6UL_PAD_NAND_DATA04__USDHC2_DATA4 0x170b9
				MX6UL_PAD_NAND_DATA05__USDHC2_DATA5 0x170b9
				MX6UL_PAD_NAND_DATA06__USDHC2_DATA6 0x170b9
				MX6UL_PAD_NAND_DATA07__USDHC2_DATA7 0x170b9
			>;
		};

		pinctrl_usdhc2_8bit_200mhz: usdhc2grp_8bit_200mhz {
			fsl,pins = <
				MX6UL_PAD_NAND_RE_B__USDHC2_CLK     0x100f9
				MX6UL_PAD_NAND_WE_B__USDHC2_CMD     0x170f9
				MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x170f9
				MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x170f9
				MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x170f9
				MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x170f9
				MX6UL_PAD_NAND_DATA04__USDHC2_DATA4 0x170f9
				MX6UL_PAD_NAND_DATA05__USDHC2_DATA5 0x170f9
				MX6UL_PAD_NAND_DATA06__USDHC2_DATA6 0x170f9
				MX6UL_PAD_NAND_DATA07__USDHC2_DATA7 0x170f9
			>;
		};

		pinctrl_tsc_reset: tscresetgrp {
			fsl,pins = <
				/* used for tsc reset */
				MX6UL_PAD_LCD_RESET__GPIO3_IO04		0x05
			>;
		};

		pinctrl_onewire: onewire {
			fsl,pins = <
				//MX6UL_PAD_GPIO1_IO02__GPIO1_IO02          0x000090B1     DHT11
				MX6UL_PAD_GPIO1_IO02__GPIO1_IO02          0x400138B1
			>;
		};

		pinctrl_sii902x: hdmigrp {
			fsl,pins = <
				MX6UL_PAD_UART1_CTS_B__GPIO1_IO18 0x59
			>;
		};

		pinctrl_adc1: adc1grp{
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO03__GPIO1_IO03          0x000010B1
			>;
		};
/*
		pinctrl_beep: beepgrp{
			fsl,pins = <
				MX6UL_PAD_UART1_RTS_B__GPIO1_IO19    0x000050B1
			>;
		};
*/

	};
};

&iomuxc_snvs {
	pinctrl-names = "default_snvs";
        pinctrl-0 = <&pinctrl_tsc_irq>;
        imx6ul-evk {
		/* MPU6050   intterrupt*/
		pinctrl_hog_2: hoggrp-2 {
			fsl,pins = <
				MX6ULL_PAD_SNVS_TAMPER0__GPIO5_IO00      0x80000000
			>;
		};
		/*
		pinctrl_dvfs: dvfsgrp {
			fsl,pins = <
				MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03	0x79
			>;
		};
		*/
		pinctrl_gpio_keys: keysgrp{
			fsl,pins = <
				MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01      0x000110A1
			>;
		};
		pinctrl_spi4: spi4grp {
			fsl,pins = <
				MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10		0x70a1
				MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11		0x70a1
				MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07		0x70a1
				MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08		0x80000000
			>;
		};
		pinctrl_led: ledgrp {
			fsl,pins = <
				MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x1b0b0
			>;
		};
		pinctrl_sai2_hp_det_b: sai2_hp_det_grp {
			fsl,pins = <
				MX6ULL_PAD_SNVS_TAMPER4__GPIO5_IO04        0x000110A1
			>;
		};

		pinctrl_tsc_irq: tsc_irq {
			fsl,pins = <
				MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09        0x4001b8b0
			>;
		};
	};
};



&lcdif {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_lcdif_dat
		     &pinctrl_lcdif_ctrl>;
	display = <&display0>;
	status = "disable"; // 这里先禁能

	display0: display@0 {
		bits-per-pixel = <16>;
		bus-width = <24>;

		display-timings {
			native-mode = <&timing0>;

			timing0: timing0 {
			mode_name = "TFT50AB";
			clock-frequency = <27000000>;
			hactive = <800>;
			vactive = <480>;
			hfront-porch = <23>;
			hback-porch = <46>;
			hsync-len = <1>;
			vback-porch = <22>;
			vfront-porch = <22>;
			vsync-len = <1>;

			hsync-active = <0>;
			vsync-active = <0>;
			de-active = <1>;
			pixelclk-active = <0>;
			};
		};
	};
};

	
&pwm1 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_pwm1>;
	status = "disabled"; // fix okay->disabled
};

&pwm3 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_pwm3>;
	status = "okay";
};

&pwm7 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_pwm7>;
	status = "okay";
};

&pwm8 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_pwm8>;
	status = "okay";
};

&pxp {
	status = "okay";
};

/*
&qspi {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_qspi>;
	status = "okay";
	ddrsmp=<0>;

	flash0: n25q256a@0 {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "micron,n25q256a";
		spi-max-frequency = <29000000>;
		spi-nor,ddr-quad-read-dummy = <6>;
		reg = <0>;
	};
};
*/

&sai2 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_sai2>;
		     /*&pinctrl_sai2_hp_det_b>;*/

	assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>,
			  <&clks IMX6UL_CLK_SAI2>;
	assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
	assigned-clock-rates = <0>, <12288000>;

	status = "okay";
};

&uart1 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_uart1>;
	status = "okay";
};

/*
&uart2 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_uart2>;
	/*fsl,uart-has-rtscts;
	/* for DTE mode, add below change
	/* fsl,dte-mode;
	/*pinctrl-0 = <&pinctrl_uart2dte>;
	status = "disable";
};*/

&uart2{
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_485>;
	fsl,rs485-gpio-txen = <&gpio1 31 GPIO_ACTIVE_HIGH>;
	linux,rs485-enabled-at-boot-time;
	status = "disable";
};

&uart3{
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_uart3>;
	status = "okay";
};


&usbotg1 {
	dr_mode = "otg";
	srp-disable;
	hnp-disable;
	adp-disable;
	status = "okay";
};

&usbotg2 {
	dr_mode = "host";
	disable-over-current;
	status = "okay";
};

&usbphy1 {
	tx-d-cal = <0x5>;
};

&usbphy2 {
	tx-d-cal = <0x5>;
};

&usdhc1 {
	pinctrl-names = "default", "state_100mhz", "state_200mhz";
	pinctrl-0 = <&pinctrl_usdhc1>;
	pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
	pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
	/*cd-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;*/
	keep-power-in-suspend;
	enable-sdio-wakeup;
	vmmc-supply = <&reg_wifi_vmmc>;
	status = "okay";
};

&usdhc2 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_usdhc2>;
	non-removable;
	status = "disable";
};

&gpmi {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_gpmi_nand>;
	status = "okay";
	nand-on-flash-bbt;
};

/*
&wdog1 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_wdog>;
	fsl,wdog_b;
};
*/

&spdif {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_spdif>;
	assigned-clocks = <&clks IMX6UL_CLK_SPDIF_SEL>,
			  <&clks IMX6UL_CLK_SPDIF_PODF>;
	assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
	assigned-clock-rates = <0>, <49152000>;
	status = "okay";
};

&adc1 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_adc1>;
	vref-supply = <&reg_vref_3v3>;
	num-channels = <4>; 			//此参数为ADC的个�?	status = "okay";
};

&gpt2 {
    status = "okay";
};

新增驱动文件

  1. 屏蔽掉以前的设备驱动,新增自己的:修改文件drivers\video\fbdev\Makefile如下:

    ../../_images/image-20210913193636045.png

  2. 新增文件drivers\video\fbdev\imx6ull_fb.c:内如如下:

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/err.h>
    #include <linux/errno.h>
    #include <linux/string.h>
    #include <linux/mm.h>
    #include <linux/slab.h>
    #include <linux/delay.h>
    #include <linux/fb.h>
    #include <linux/init.h>
    #include <linux/dma-mapping.h>
    #include <linux/interrupt.h>
    #include <linux/platform_device.h>
    #include <linux/clk.h>
    #include <linux/cpufreq.h>
    #include <linux/io.h>
    #include <video/display_timing.h>
    #include <video/of_display_timing.h>
    #include <linux/gpio/consumer.h>
    
    #include <asm/div64.h>
    
    #include <asm/mach/map.h>
    
    #define _IMX6ULL_DEBUG_EN
    
    #ifdef _IMX6ULL_DEBUG_EN
    
    	#define _DEBUG_PRINT(...) \
    		do {\
    			printk("[%s:%s:%d]:", __FILE__, __FUNCTION__,__LINE__); \
    			printk(__VA_ARGS__);\
    		} while (0)
    
    #else
    	#define _DEBUG_PRINT(...)
    #endif
    
    
    
    struct imx6ull_lcdif {
    	volatile unsigned int CTRL;                              
    	volatile unsigned int CTRL_SET;                        
    	volatile unsigned int CTRL_CLR;                         
    	volatile unsigned int CTRL_TOG;                         
    	volatile unsigned int CTRL1;                             
    	volatile unsigned int CTRL1_SET;                         
    	volatile unsigned int CTRL1_CLR;                       
    	volatile unsigned int CTRL1_TOG;                       
    	volatile unsigned int CTRL2;                            
    	volatile unsigned int CTRL2_SET;                       
    	volatile unsigned int CTRL2_CLR;                        
    	volatile unsigned int CTRL2_TOG;                        
    	volatile unsigned int TRANSFER_COUNT;   
    		unsigned char RESERVED_0[12];
    	volatile unsigned int CUR_BUF;                          
    		unsigned char RESERVED_1[12];
    	volatile unsigned int NEXT_BUF;                        
    		unsigned char RESERVED_2[12];
    	volatile unsigned int TIMING;                          
    		unsigned char RESERVED_3[12];
    	volatile unsigned int VDCTRL0;                         
    	volatile unsigned int VDCTRL0_SET;                      
    	volatile unsigned int VDCTRL0_CLR;                     
    	volatile unsigned int VDCTRL0_TOG;                     
    	volatile unsigned int VDCTRL1;                          
    		unsigned char RESERVED_4[12];
    	volatile unsigned int VDCTRL2;                          
    		unsigned char RESERVED_5[12];
    	volatile unsigned int VDCTRL3;                          
    		unsigned char RESERVED_6[12];
    	volatile unsigned int VDCTRL4;                           
    		unsigned char RESERVED_7[12];
    	volatile unsigned int DVICTRL0;    
    		unsigned char RESERVED_8[12];
    	volatile unsigned int DVICTRL1;                         
    		unsigned char RESERVED_9[12];
    	volatile unsigned int DVICTRL2;                        
    		unsigned char RESERVED_10[12];
    	volatile unsigned int DVICTRL3;                        
    		unsigned char RESERVED_11[12];
    	volatile unsigned int DVICTRL4;                          
    		unsigned char RESERVED_12[12];
    	volatile unsigned int CSC_COEFF0;  
    		unsigned char RESERVED_13[12];
    	volatile unsigned int CSC_COEFF1;                        
    		unsigned char RESERVED_14[12];
    	volatile unsigned int CSC_COEFF2;                        
    		unsigned char RESERVED_15[12];
    	volatile unsigned int CSC_COEFF3;                        
    		unsigned char RESERVED_16[12];
    	volatile unsigned int CSC_COEFF4;   
    		unsigned char RESERVED_17[12];
    	volatile unsigned int CSC_OFFSET;  
    		unsigned char RESERVED_18[12];
    	volatile unsigned int CSC_LIMIT;  
    		unsigned char RESERVED_19[12];
    	volatile unsigned int DATA;                              
    		unsigned char RESERVED_20[12];
    	volatile unsigned int BM_ERROR_STAT;                     
    		unsigned char RESERVED_21[12];
    	volatile unsigned int CRC_STAT;                        
    		unsigned char RESERVED_22[12];
    	volatile  unsigned int STAT;                             
    		unsigned char RESERVED_23[76];
    	volatile unsigned int THRES;                             
    		unsigned char RESERVED_24[12];
    	volatile unsigned int AS_CTRL;                           
    		unsigned char RESERVED_25[12];
    	volatile unsigned int AS_BUF;                            
    		unsigned char RESERVED_26[12];
    	volatile unsigned int AS_NEXT_BUF;                     
    		unsigned char RESERVED_27[12];
    	volatile unsigned int AS_CLRKEYLOW;                    
    		unsigned char RESERVED_28[12];
    	volatile unsigned int AS_CLRKEYHIGH;                   
    		unsigned char RESERVED_29[12];
    	volatile unsigned int SYNC_DELAY;                      
    } ;
    
    /* LCD控制参数结构体 */
    struct lcd_dev{
    	unsigned int height;		/* LCD屏幕高度 */
    	unsigned int width;			/* LCD屏幕宽度 */
    	unsigned int pix_size;		/* 输入LCD每个像素所占bit位数:fb_bpp*/
    	unsigned int data_width; 	/* 输出数据线宽度:lcd_bpp */
    	unsigned int vspw;			/* 场脉宽 */
    	unsigned int vbpd;      	/* 场后肩 */
    	unsigned int vfpd;			/* 场前肩 */
    	unsigned int hspw;			/* 行脉宽 */
    	unsigned int hbpd;			/* 行后肩 */
    	unsigned int hfpd;			/* 行前肩 */
    	unsigned char hsync_pol;	/* 行信号极性 */
    	unsigned char vsync_pol;	/* 场信号极性 */
    	unsigned char de_pol;		/* de信号极性 */
    	unsigned char clk_pol;		/* 时钟信号极性 */
    	unsigned int  pixe_clk;		/* 数据信号极性 */
    
    	struct imx6ull_lcdif *LCDIF;
    	unsigned int phy_addr;
    
    	struct clk* clk_pix;
    	struct clk* clk_axi;
    	struct gpio_desc *bl_gpio;
    	struct fb_info *info;
    };
    
    
    /*
     * @description	: 复位ELCDIF接口
     * @param 		: 无
     * @return 		: 无
     */
    void lcd_reset(struct imx6ull_lcdif *LCDIF)
    {
    	LCDIF->CTRL  = 1<<31; /* 强制复位 */
    }
    
    /*
     * @description	: 结束复位ELCDIF接口
     * @param 		: 无
     * @return 		: 无
     */
    void lcd_noreset(struct imx6ull_lcdif *LCDIF)
    {
    	LCDIF->CTRL  = 0<<31; /* 取消强制复位 */
    }
    
    
    
    static unsigned int pseudo_palette[16];
    
    
    
    static void lcd_controller_enable(struct imx6ull_lcdif *lcdif)
    {
    	lcdif->CTRL |= (1<<0);
    }
    
    static int lcd_controller_init(struct lcd_dev *host)
    { 
    	unsigned int lcd_data_bus_width;
    	unsigned int fb_width;
    
    	struct imx6ull_lcdif *LCDIF =  host->LCDIF;
    
    	unsigned int height 		= host->height ;
    	unsigned int width			= host->width;
    	unsigned int fb_bpp   		= host->pix_size; 	// fb每个像素占bit数
    	unsigned int lcd_bpp		= host->data_width; // 输出数据总线宽度
    	unsigned int vspw			= host->vspw;		// 场脉宽
    	unsigned int vbpd			= host->vbpd;		// 下边界
    	unsigned int vfpd			= host->vfpd;		// 上边界
    
    	unsigned int hspw			= host->hspw;		// 行脉宽
    	unsigned int hbpd			= host->hbpd;		// 左边界
    	unsigned int hfpd			= host->hfpd;		// 右边界
    
    	unsigned int vsync_pol		= host->vsync_pol;
    	unsigned int hsync_pol		= host->hsync_pol;
    	unsigned int clk_pol  		= host->clk_pol;
    	unsigned int de_pol   		= host->de_pol;
    
    
    	if (lcd_bpp == 24)
    		lcd_data_bus_width = 0x3;
    	else if (lcd_bpp == 18)
    		lcd_data_bus_width = 0x2;
    	else if (lcd_bpp == 8)
    		lcd_data_bus_width = 0x1;
    	else if (lcd_bpp == 16)
    		lcd_data_bus_width = 0x0;
    	else
    		return -1;
    
    	if (fb_bpp == 24 || fb_bpp == 32)
    		fb_width = 0x3;
    	else if (fb_bpp == 18)
    		fb_width = 0x2;
    	else if (fb_bpp == 8)
    		fb_width = 0x1;
    	else if (fb_bpp == 16)
    		fb_width = 0x0;
    	else
    		return -1;
    
    	lcd_reset(LCDIF);		/* 复位LCD  			*/
    	mdelay(10);				/* 延时10ms 			*/
    	lcd_noreset(LCDIF);		/* 结束复位 			*/
    
    	/* 
         * 初始化LCD控制器的CTRL寄存器
         * [19]       :  1      : DOTCLK和DVI modes需要设置为1 
         * [17]       :  1      : 设置为1工作在DOTCLK模式
         * [15:14]    : 00      : 输入数据不交换(小端模式)默认就为0,不需设置
         * [13:12]    : 00      : CSC数据不交换(小端模式)默认就为0,不需设置
         * [11:10]    : 11		: 数据总线为24bit
         * [9:8]    根据显示屏资源文件bpp来设置:8位0x1 , 16位0x0 ,24位0x3
         * [5]        :  1      : 设置elcdif工作在主机模式
         * [1]        :  0      : 24位数据均是有效数据,默认就为0,不需设置
    	 */	
    	LCDIF->CTRL = 	(0<<30) | 
    					(0<<29) | 
    					(0<<28) | 
    					(1<<19) | 
    					(1<<17) | 
    					(lcd_data_bus_width<< 10) |
    					(fb_width << 8) | 
    					(1<<5);
    	/*
    	* 设置ELCDIF的寄存器CTRL1
    	* 根据bpp设置,bpp为24或32才设置
    	* [19:16]  : 111  :表示ARGB传输格式模式下,传输24位无压缩数据,A通道不用传输)
    	*/	  
    	if(fb_bpp == 24 || fb_bpp == 32)
    	{	  
    		  LCDIF->CTRL1 &= ~(0xf << 16); 
    		  LCDIF->CTRL1 |=  (0x7 << 16); 
    	}
    	else
    		LCDIF->CTRL1 |= (0xf << 16); 
    
    	 /*
          * 初始化ELCDIF的寄存器TRANSFER_COUNT寄存器
          * bit [31:16]  : 高度
          * bit [15:0]   : 宽度
    	  */
    	LCDIF->TRANSFER_COUNT  = (height << 16) | (width << 0);
    
    
    	/*
    	* 设置ELCDIF的VDCTRL0寄存器
    	* [29] 0 : VSYNC输出  ,默认为0,无需设置
    	* [28] 1 : 在DOTCLK模式下,设置1硬件会产生使能ENABLE输出
    	* [27] 0 : VSYNC低电平有效	,根据屏幕配置文件将其设置为0
    	* [26] 0 : HSYNC低电平有效 , 根据屏幕配置文件将其设置为0
    	* [25] 1 : DOTCLK下降沿有效 ,根据屏幕配置文件将其设置为1
    	* [24] 1 : ENABLE信号高电平有效,根据屏幕配置文件将其设置为1
    	* [21] 1 : 帧同步周期单位,DOTCLK mode设置为1
    	* [20] 1 : 帧同步脉冲宽度单位,DOTCLK mode设置为1
    	* [17:0] :  vysnc脉冲宽度 
    	*/
    	  LCDIF->VDCTRL0 = 	(1 << 28)|
    						( vsync_pol << 27)|
    						( hsync_pol << 26)|
    						( clk_pol   << 25)|
    						( de_pol    << 24)|
    						(1 << 21)|
    						(1 << 20)|
    						(vspw << 0);
    
    
    
    	/*
    	 * 初始化ELCDIF的VDCTRL1寄存器
    	 * 设置VSYNC总周期
    	 */  
    	LCDIF->VDCTRL1 = height + vspw + vfpd + vbpd;  //VSYNC周期
    
    	 /*
    	  * 初始化ELCDIF的VDCTRL2寄存器
    	  * 设置HSYNC周期
    	  * bit[31:18] :hsw
    	  * bit[17:0]  : HSYNC总周期
    	  */ 
    	LCDIF->VDCTRL2 = (hspw << 18) | (width + hspw + hfpd + hbpd);
    
    	/*
    	 * 初始化ELCDIF的VDCTRL3寄存器
    	 * 设置HSYNC周期
    	 * bit[27:16] :水平等待时钟数
    	 * bit[15:0]  : 垂直等待时钟数
    	 */ 
    	LCDIF->VDCTRL3 = ((hbpd + hspw) << 16) | (vbpd + vspw);
    
    	/*
    	 * 初始化ELCDIF的VDCTRL4寄存器
    	 * 设置HSYNC周期
    	 * bit[18] 1 : 当使用VSHYNC、HSYNC、DOTCLK的话此为置1
    	 * bit[17:0]  : 宽度
    	 */ 
    
    	LCDIF->VDCTRL4 = (1<<18) | (width);
    	/*
    	* 设置ELCDIF的CUR_BUF和NEXT_BUF寄存器
    	* CUR_BUF	 :	当前显存地址
    	* NEXT_BUF :	下一帧显存地址
    	* 方便运算,都设置为同一个显存地址
    	*/ 
    	LCDIF->CUR_BUF  =  host->phy_addr;
    	LCDIF->NEXT_BUF =  host->phy_addr;
    
    	return 0;
    }
    
    static void mylcd_prase_dt(struct platform_device *pdev,struct lcd_dev *host)
    {
    	struct device_node *display_np;
    	int ret;
    	struct display_timings *timings = NULL;
    	struct display_timing *dt = NULL;
    
    	struct gpio_desc *bl_gpio;
    	struct clk* clk_pix;
    	struct clk* clk_axi;
        unsigned int data_width;
    	unsigned int pix_size;
    
    	display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);
    
    	/* get common info */
    	ret = of_property_read_u32(display_np, "bus-width", 	&data_width); 	// 获取设备树数据宽度
    	ret = of_property_read_u32(display_np, "bits-per-pixel",&pix_size);	// 获取设备树一个像素bit数
    
    	/* get timming */
    	timings = of_get_display_timings(display_np);
    	dt = timings->timings[timings->native_mode];
    
    	/* get clk from device tree */
    	clk_pix = devm_clk_get(&pdev->dev, "pix");	
    	clk_axi = devm_clk_get(&pdev->dev, "axi");	 
    
    	/* get gpio from device tree */
    	bl_gpio = gpiod_get(&pdev->dev, "backlight", 0);	// 获取设备树 背光 gpio
    
    	host->pixe_clk = dt->pixelclock.typ;
    	host->height     = dt->vactive.typ;
    	host->width      = dt->hactive.typ;
    	host->pix_size   = pix_size;
    	host->data_width = data_width;
    	host->vspw 		= dt->vsync_len.typ;
    	host->vfpd 		= dt->vfront_porch.typ;
    	host->vbpd 		= dt->vback_porch.typ;
    
    	host->hspw 		= dt->hsync_len.typ;
    	host->hfpd 		= dt->hfront_porch.typ;
    	host->hbpd 		= dt->hback_porch.typ;
        host->clk_axi	= clk_axi;
     	host->clk_pix	= clk_pix;
    
    	host->bl_gpio    = bl_gpio;
    	if (dt->flags & DISPLAY_FLAGS_HSYNC_HIGH){
    		host->hsync_pol = 1;
    	}else{
    		host->hsync_pol = 0;
    	}
    
    	if (dt->flags & DISPLAY_FLAGS_VSYNC_HIGH){
    		host->vsync_pol = 1;
    	}else{
    		host->vsync_pol = 0;
    	}
    
    	if (dt->flags & DISPLAY_FLAGS_DE_HIGH){
    		host->de_pol = 1;
    	}else{
    		host->de_pol = 0;
    	}
    
    	if (dt->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE){
    		host->clk_pol = 1;
    	}else{
    		host->clk_pol = 0;
    	}
    
    
    	_DEBUG_PRINT("bits-per-pixel:%d bus-width:%d\n" ,host->pix_size,host->data_width);
    
    
    
    }
    /* from pxafb.c */
    static inline unsigned int chan_to_field(unsigned int chan,
    					 struct fb_bitfield *bf)
    {
    	chan &= 0xffff;
    	chan >>= 16 - bf->length;
    	return chan << bf->offset;
    }
    
    static int mylcd_setcolreg(unsigned regno,
    			       unsigned red, unsigned green, unsigned blue,
    			       unsigned transp, struct fb_info *info)
    {
    	unsigned int val;
    	unsigned int *pal;
    	/* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
    		   regno, red, green, blue); */
    
    	switch (info->fix.visual) {
    	case FB_VISUAL_TRUECOLOR:
    		/* true-colour, use pseudo-palette */
    
    		if (regno < 16) {
    			pal = info->pseudo_palette;
    
    			val  = chan_to_field(red,   &info->var.red);
    			val |= chan_to_field(green, &info->var.green);
    			val |= chan_to_field(blue,  &info->var.blue);
    
    			pal[regno] = val;
    		}
    		break;
    
    	default:
    		return 1;	/* unknown type */
    	}
    
    	return 0;
    }
    
    
    static struct fb_ops myfb_ops = {
    	.owner		= THIS_MODULE,
    	.fb_setcolreg	= mylcd_setcolreg,
    	.fb_fillrect	= cfb_fillrect,
    	.fb_copyarea	= cfb_copyarea,
    	.fb_imageblit	= cfb_imageblit,
    };
    
    
    
    struct lcd_dev host;
    static int mylcd_probe(struct platform_device *pdev)
    {
    	dma_addr_t phy_addr;
    	int ret;
    	struct imx6ull_lcdif *lcdif;
    	struct resource *res;
    
    	mylcd_prase_dt(pdev,&host);
    	/* config bl_gpio as output */
    	gpiod_direction_output(host.bl_gpio, 1);
    
    	/* set clk rate */
    	clk_set_rate(host.clk_pix,host.pixe_clk);	// 设置设备树 pix 时钟
    
    	/* enable clk */
    	clk_prepare_enable(host.clk_pix);	
    	clk_prepare_enable(host.clk_axi);			 	// 使能时钟
    
    	/* 1.1 分配fb_info */
    	host.info = framebuffer_alloc(0, NULL);
    
    	/* 1.2 设置fb_info */
    	/* a. var : LCD分辨率、颜色格式 */
    	host.info->var.xres_virtual = host.info->var.xres = host.width;
    	host.info->var.yres_virtual = host.info->var.yres = host.height;
    
    
    	host.info->var.bits_per_pixel = host.pix_size;  /* rgb888 */
    	host.info->var.red.offset = 16;
    	host.info->var.red.length = 8;
    
    	host.info->var.green.offset = 8;
    	host.info->var.green.length = 8;
    
    	host.info->var.blue.offset = 0;
    	host.info->var.blue.length = 8;
    
    
    
    	/* b. fix */
    	strcpy(host.info->fix.id, "flyrobot_lcd");
    	host.info->fix.smem_len = host.info->var.xres * host.info->var.yres * host.info->var.bits_per_pixel / 8; // 字节数
    	if (host.info->var.bits_per_pixel == 24)
    		host.info->fix.smem_len = host.info->var.xres * host.info->var.yres * 4;
    
    
    	/* fb的虚拟地址 */
    	host.info->screen_base = dma_alloc_writecombine(NULL, host.info->fix.smem_len, &phy_addr,GFP_KERNEL);
    	host.info->fix.smem_start = phy_addr;  /* fb的物理地址 */
    
    	host.phy_addr = phy_addr;
    
    	host.info->fix.type = FB_TYPE_PACKED_PIXELS;
    	host.info->fix.visual = FB_VISUAL_TRUECOLOR;
    
    	host.info->fix.line_length = host.info->var.xres * host.info->var.bits_per_pixel / 8;
    	if (host.info->var.bits_per_pixel == 24)
    		host.info->fix.line_length = host.info->var.xres * 4;
    
    
    	/* c. fbops */
    	host.info->fbops = &myfb_ops;
    	host.info->pseudo_palette = pseudo_palette;
    
    
    	/* 1.3 注册fb_info */
    	register_framebuffer(host.info);
    
    	/* 1.4 硬件操作 */
    	//lcdif = ioremap(0x021C8000, sizeof(*lcdif));
    
    	res 		= platform_get_resource(pdev, IORESOURCE_MEM, 0);
    	host.LCDIF 	= devm_ioremap_resource(&pdev->dev, res);
    
    
    	lcd_controller_init(&host);
    
    	lcd_controller_enable(host.LCDIF);
    	gpiod_set_value(host.bl_gpio, 1); 
    
    	return 0;
    }
    
    
    static int mylcd_remove(struct platform_device *pdev)
    {
    	/* 反过来操作 */
    	/* 2.1 反注册fb_info */
    	unregister_framebuffer(host.info);
    
    	/* 2.2 释放fb_info */
    	framebuffer_release(host.info);
    
    	//iounmap(mylcd_regs);
    
    	return 0;
    }
    
    
    static const struct of_device_id mylcd_of_match[] = {
    	{ .compatible = "flyrobot,lcd_drv", },
    	{ },
    };
    MODULE_DEVICE_TABLE(of, simplefb_of_match);
    
    static struct platform_driver mylcd_driver = {
    	.driver = {
    		.name = "mylcd",
    		.of_match_table = mylcd_of_match,
    	},
    	.probe = mylcd_probe,
    	.remove = mylcd_remove,
    };
    
    static int __init lcd_drv_init(void)
    {
    	int ret;
    
    	ret = platform_driver_register(&mylcd_driver);
    	if (ret)
    		return ret;
    
    	return 0;
    }
    
    /* 2. 出口 */
    static void __exit lcd_drv_exit(void)
    {
    	platform_driver_unregister(&mylcd_driver);
    }
    
    
    module_init(lcd_drv_init);
    module_exit(lcd_drv_exit);
    
    MODULE_AUTHOR("www.flyrobot.net");
    MODULE_DESCRIPTION("Framebuffer driver for the linux");
    MODULE_LICENSE("GPL");
    
    

驱动验证

  1. 编译完后烧录设备树和内核到板子,启动板后可以通过下面的命令查看设备节点:

    cd /sys/firmware/devicetree/base
    
    cd /sys/bus/platform/drivers/
    
  2. 应用程序如下:

    #include <sys/mman.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <linux/fb.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/ioctl.h>
    #include <time.h>
    
    static int fd_fb;
    static struct fb_fix_screeninfo fix;	/* Current fix */
    static struct fb_var_screeninfo var;	/* Current var */
    static int screen_size;
    static unsigned char *fb_base;
    static unsigned int line_width;
    static unsigned int pixel_width;
    
    /**********************************************************************
     * 函数名称: lcd_put_pixel
     * 功能描述: 在LCD指定位置上输出指定颜色(描点)
     * 输入参数: x坐标,y坐标,颜色
     * 输出参数: 无
     * 返 回 值: 会
     * 修改日期        版本号     修改人	      修改内容
     * -----------------------------------------------
     * 2020/05/12	     V1.0	  zh(angenao)	      创建
     ***********************************************************************/ 
    void lcd_put_pixel(void *fb_base, int x, int y, unsigned int color)
    {
    	unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;
    	unsigned short *pen_16;	
    	unsigned int *pen_32;	
    
    	unsigned int red, green, blue;	
    
    	pen_16 = (unsigned short *)pen_8;
    	pen_32 = (unsigned int *)pen_8;
    
    	switch (var.bits_per_pixel)
    	{
    		case 8:
    		{
    			*pen_8 = color;
    			break;
    		}
    		case 16:
    		{
    			/* 565 */
    			red   = (color >> 16) & 0xff;
    			green = (color >> 8) & 0xff;
    			blue  = (color >> 0) & 0xff;
    			color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
    			*pen_16 = color;
    			break;
    		}
    		case 32:
    		{
    			*pen_32 = color;
    			break;
    		}
    		default:
    		{
    			printf("can't surport %dbpp\n", var.bits_per_pixel);
    			break;
    		}
    	}
    }
    
    void lcd_draw_screen(void *fb_base, unsigned int color)
    {
    	int x, y;
    	for (x = 0; x < var.xres; x++)
    		for (y = 0; y < var.yres; y++)
    			lcd_put_pixel(fb_base, x, y, color);
    }
    
    
    /* ./multi_framebuffer_test single
     * ./multi_framebuffer_test double
     */
    int main(int argc, char **argv)
    {
    	int i;
    	int ret;
    	int nBuffers;
    	int nNextBuffer = 1;
    	char *pNextBuffer;
    	unsigned int colors[] = {0x00FF0000, 0x0000FF00, 0x000000FF, 0, 0x00FFFFFF};  /* 0x00RRGGBB */
    	struct timespec time;
    
    	time.tv_sec  = 0;
    	time.tv_nsec = 100000000;
    
    	if (argc != 2)
    	{
    		printf("Usage : %s <single|double>\n", argv[0]);
    		return -1;
    	}
    
    	fd_fb = open("/dev/fb0", O_RDWR);
    	if (fd_fb < 0)
    	{
    		printf("can't open /dev/fb0\n");
    		return -1;
    	}
    
    	if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
    	{
    		printf("can't get fix\n");
    		return -1;
    	}
    
    	if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
    	{
    		printf("can't get var\n");
    		return -1;
    	}
    
    	line_width  = var.xres * var.bits_per_pixel / 8;
    	pixel_width = var.bits_per_pixel / 8;
    	screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
    
    	nBuffers = fix.smem_len / screen_size;
    	printf("nBuffers = %d\n", nBuffers);
    
    	fb_base = (unsigned char *)mmap(NULL , fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
    	if (fb_base == (unsigned char *)-1)
    	{
    		printf("can't mmap\n");
    		return -1;
    	}
    
    	if ((argv[1][0] == 's') || (nBuffers == 1))
    	{
    		while (1)
    		{
    			/* use single buffer */
    			for (i = 0; i < sizeof(colors)/sizeof(colors[0]); i++)
    			{
    				lcd_draw_screen(fb_base, colors[i]);
    				nanosleep(&time, NULL);
    			}
    		}
    	}
    	else
    	{
    		/* use double buffer */
    		/* a. enable use multi buffers */
    		var.yres_virtual = nBuffers * var.yres;
    		ioctl(fd_fb, FBIOPUT_VSCREENINFO, &var);
    
    		while (1)
    		{
    			for (i = 0; i < sizeof(colors)/sizeof(colors[0]); i++)
    			{
    				/* get buffer */
    				pNextBuffer =  fb_base + nNextBuffer * screen_size;
    
    				/* set buffer */
    				lcd_draw_screen(pNextBuffer, colors[i]);
    
    				/* switch buffer */
    				var.yoffset = nNextBuffer * var.yres;
    				ioctl(fd_fb, FBIOPAN_DISPLAY, &var);
    
    				ret = 0;
    				ioctl(fd_fb, FBIO_WAITFORVSYNC, &ret);
    
    				nNextBuffer = !nNextBuffer;
    				nanosleep(&time, NULL);
    			}
    		}
    
    	}
    
    	munmap(fb_base , screen_size);
    	close(fd_fb);
    
    	return 0;	
    }