要做的事情
- 去除内核自带的驱动程序
- 加入我们编写的驱动程序、设备树文件
- 重新编译内核、设备树
- 上机测试: 使用编译出来的内核、设备树启动板子
1.去除内核自带的驱动程序
修改内核文件:drivers/video/fbdev/Makefile ,把内核自带驱动程序mxsfb.c对应的那行注释掉,如下:
2.加入新驱动程序、设备树
-
复制驱动程序:
- 把
11_lcd_drv_imx6ull_ok\lcd_drv.c 放到内核源码目录drivers/video/fbdev - 备份内核自带设备树文件:
arch/arm/boot/dts/100ask_imx6ull-14x14.dts - 把
11_lcd_drv_imx6ull_ok\100ask_imx6ull-14x14.dts 放到内核源码目录arch/arm/boot/dts/ -
修改内核文件:
- 修改:
drivers/video/fbdev/Makefile ,使用我们提供的lcd_drv.c,如下:
obj-$(CONFIG_FB_MXS) += lcd_drv.o
3. 重新编译内核、设备树
export PATH=$PATH:/home/book/imx6ull/ToolChain-6.2.1/bin
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
cp imx6ull-14x14-ebf.dts ~/imx6ull/linux-4.9.88/arch/arm/boot/dts/imx6ull-14x14-ebf.dts
cp lcd_drv.c ~/imx6ull/linux-4.9.88/drivers/video/fbdev/
make dtbs
make zImage -j4
cp arch/arm/boot/dts/imx6ull-14x14-ebf.dtb ~/tftpboot/
cp arch/arm/boot/zImage ~/tftpboot/zImage-imx6ull
4.上调机试
- 重启观察开发板现象
如果可以看到企鹅logo,就正常。 如果在/dev/fb0 有fb0节点也是正常的
5. 经验教训
pwm信号是直接gpio输出,所以要关闭pwm输出
调试后的代码
lcd_drv.h
#ifndef __LCD_DRV_H_
#define __LCD_DRV_H_
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;
};
#endif
lcd_drv.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fb.h>
#include <linux/types.h>
#include <linux/dma-mapping.h>
#include <video/display_timing.h>
#include <linux/platform_device.h>
#include <video/of_display_timing.h>
#include <linux/of_gpio.h>
#include <linux/clk.h>
#include "lcd_drv.h"
static unsigned int pseudo_palette[16];
struct lcd_regs {
volatile unsigned int fb_base_phys;
volatile unsigned int fb_xres;
volatile unsigned int fb_yres;
volatile unsigned int fb_bpp;
};
static struct lcd_regs *mylcd_regs;
static struct fb_info *myfb_info;
static struct gpio_desc *bl_gpio;
static struct clk* clk_pix;
static struct clk* clk_axi;
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 s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
unsigned int transp, struct fb_info *info)
{
unsigned int val;
switch (info->fix.visual) {
case FB_VISUAL_TRUECOLOR:
if (regno < 16) {
u32 *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;
}
return 0;
}
static struct fb_ops myfb_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = s3c_lcdfb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
static void lcd_controller_enable(struct imx6ull_lcdif *lcdif)
{
lcdif->CTRL |= (1<<0);
}
static int lcd_controller_init(struct imx6ull_lcdif *lcdif,
struct display_timing *dt, int lcd_bpp, int fb_bpp,
unsigned int fb_phy)
{
int lcd_data_bus_width;
int fb_width;
int vsync_pol = 0;
int hsync_pol = 0;
int dotclk_pol = 0;
int de_pol = 0;
switch(lcd_bpp) {
case 16:
lcd_data_bus_width = 0x0;
break;
case 8:
lcd_data_bus_width = 0x1;
break;
case 18:
lcd_data_bus_width = 0x2;
break;
case 24:
lcd_data_bus_width = 0x3;
break;
default:
lcd_data_bus_width = 0x0;
break;
}
switch(fb_bpp) {
case 16:
fb_width = 0x0;
break;
case 8:
fb_width = 0x1;
break;
case 18:
fb_width = 0x2;
break;
case 24:
case 32:
fb_width = 0x3;
break;
default:
fb_width = 0x0;
break;
}
lcdif->CTRL = (0<<30) | (0<<29) | (0<<28) | (1<<19) | (1<<17) | (lcd_data_bus_width << 10) |\
(fb_width << 8) | (1<<5);
if(fb_bpp == 24 || fb_bpp == 32) {
lcdif->CTRL1 &= ~(0xf << 16);
lcdif->CTRL1 |= (0x7 << 16);
} else {
lcdif->CTRL1 |= (0xf << 16);
}
lcdif->TRANSFER_COUNT = (dt->vactive.typ << 16 )| (dt->hactive.typ<<0);
if(dt->flags & DISPLAY_FLAGS_HSYNC_HIGH)
hsync_pol = 1;
if(dt->flags & DISPLAY_FLAGS_VSYNC_HIGH)
vsync_pol = 1;
if(dt->flags & DISPLAY_FLAGS_DE_HIGH)
de_pol = 1;
if(dt->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
dotclk_pol = 1;
lcdif->VDCTRL0 = (1 << 28) | (vsync_pol << 27) | (hsync_pol << 26) | (dotclk_pol << 25) | (de_pol << 24) | (1<<21) |
(1<<20) | (dt->vsync_len.typ << 0);
lcdif->VDCTRL1 = dt->vback_porch.typ + dt->vsync_len.typ + dt->vactive.typ + dt->vfront_porch.typ;
lcdif->VDCTRL2 = (dt->hsync_len.typ << 18)| (dt->hback_porch.typ + dt->hsync_len.typ + dt->hactive.typ + dt->hfront_porch.typ) << 0;
lcdif->VDCTRL3 = (dt->hback_porch.typ + dt->hsync_len.typ) << 16 |
((dt->vback_porch.typ + dt->vsync_len.typ) << 0);
lcdif->VDCTRL4 = (1 << 18) | (dt->hactive.typ);
lcdif->CUR_BUF = fb_phy;
lcdif->NEXT_BUF = fb_phy;
return 0;
}
static int mylcd_probe(struct platform_device *pdev)
{
struct device_node *display_np;
int ret;
dma_addr_t phy_addr;
int width, bits_per_pixel;
struct display_timings *timings = NULL;
struct display_timing *dt = NULL;
struct imx6ull_lcdif *lcdif;
struct resource *res;
display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);
if(!display_np) {
dev_err(&pdev->dev, "missing property display \n");
return -EINVAL;
}
ret = of_property_read_u32(display_np, "bus-width", &width);
if(ret) {
dev_err(&pdev->dev, "Not found bus-width\n");
return -EINVAL;
}
ret = of_property_read_u32(display_np, "bits-per-pixel", &bits_per_pixel);
if(ret) {
dev_err(&pdev->dev, "Not found bits-per-pixel\n");
return -EINVAL;
}
timings = of_get_display_timings(display_np);
if (!timings) {
dev_err(&pdev->dev, "failed to get display timings\n");
return -EINVAL;
}
dt = timings->timings[timings->native_mode];
bl_gpio = gpiod_get(&pdev->dev, "backlight", 0);
if(IS_ERR(bl_gpio)) {
dev_err(&pdev->dev, "failed to get gpio backlight \n");
return -1;
}
gpiod_direction_output(bl_gpio, 1);
clk_pix = devm_clk_get(&pdev->dev, "pix");
if(IS_ERR(clk_pix)) {
dev_err(&pdev->dev, "failed to get pix \n");
return -1;
}
clk_axi = devm_clk_get(&pdev->dev, "axi");
if(IS_ERR(clk_axi)) {
dev_err(&pdev->dev, "failed to get axi \n");
return -1;
}
clk_set_rate(clk_pix, dt->pixelclock.typ);
clk_prepare_enable(clk_pix);
clk_prepare_enable(clk_axi);
myfb_info = framebuffer_alloc(0, NULL);
if(!myfb_info)
return -ENOMEM;
myfb_info->var.xres = dt->hactive.typ;
myfb_info->var.yres = dt->vactive.typ;
myfb_info->var.bits_per_pixel = 16;
myfb_info->var.red.offset = 11;
myfb_info->var.red.length = 5;
myfb_info->var.green.offset = 5;
myfb_info->var.green.length = 6;
myfb_info->var.blue.offset = 0;
myfb_info->var.blue.length = 5;
myfb_info->var.xres_virtual = dt->hactive.typ;
myfb_info->var.yres_virtual = dt->vactive.typ;
strcpy(myfb_info->fix.id, "mylcd");
myfb_info->fix.smem_len = myfb_info->var.xres * myfb_info->var.yres * myfb_info->var.bits_per_pixel / 8;
if(myfb_info->var.bits_per_pixel == 24) {
myfb_info->fix.smem_len = myfb_info->var.xres * myfb_info->var.yres * 4;
}
myfb_info->screen_base = dma_alloc_wc(NULL, myfb_info->fix.smem_len, &phy_addr, GFP_KERNEL);
myfb_info->fix.smem_start = phy_addr;
myfb_info->fix.type = FB_TYPE_PACKED_PIXELS;
myfb_info->fix.visual = FB_VISUAL_TRUECOLOR;
myfb_info->fix.line_length = (myfb_info->var.xres_virtual * myfb_info->var.bits_per_pixel / 8);
if(myfb_info->var.bits_per_pixel == 24) {
myfb_info->fix.line_length = myfb_info->var.xres_virtual * 4;
}
myfb_info->fbops = &myfb_ops;
myfb_info->pseudo_palette = pseudo_palette;
ret = register_framebuffer(myfb_info);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
lcdif = devm_ioremap_resource(&pdev->dev, res);
lcd_controller_init(lcdif, dt, bits_per_pixel, 16, phy_addr);
lcd_controller_enable(lcdif);
gpiod_set_value(bl_gpio, 1);
return 0;
}
static int mylcd_remove(struct platform_device *pdev)
{
iounmap(mylcd_regs);
unregister_framebuffer(myfb_info);
framebuffer_release(myfb_info);
return 0;
}
static const struct of_device_id mylcd_of_match[] = {
{ .compatible = "chen,mylcd", },
{ },
};
static struct platform_driver mylcd_driver = {
.probe = mylcd_probe,
.remove = mylcd_remove,
.driver = {
.name = "drv,mylcd",
.of_match_table = mylcd_of_match,
},
};
static int lcd_drv_init(void)
{
int ret = platform_driver_register(&mylcd_driver);
if(ret)
return ret;
return ret;
}
static void lcd_drv_exit(void)
{
platform_driver_unregister(&mylcd_driver);
}
module_init(lcd_drv_init);
module_exit(lcd_drv_exit);
MODULE_AUTHOR("chen");
MODULE_DESCRIPTION("Framebuffer driver for the linux");
MODULE_LICENSE("GPL");
imx6ull-14x14-ebf.dts
/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";
backlight {
compatible = "pwm-backlight";
pwms = <&pwm1 0 5000000>;
brightness-levels = <0 4 8 16 32 64 128 255>;
default-brightness-level = <6>;
status = "disabled";
};
framebuffer-mylcd {
compatible = "chen,mylcd";
reg = <0x021c8000 0x4000>;
pinctrl-name = "default";
pinctrl-0 = <&mylcd_pinctrl>;
backlight-gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
clocks = <&clks IMX6UL_CLK_LCDIF_PIX>,
<&clks IMX6UL_CLK_LCDIF_APB>;
clock-names = "pix", "axi";
satus = "okay";
display = <&displayF>;
displayF: display {
bits-per-pixel = <16>;
bus-width = <24>;
display-timings {
native-mode = <&timingF>;
timingF: timing0_800x480 {
clock-frequency=<27000000>;
hactive = <800>;
vactive = <480>;
hfront-porch = <22>;
hback-porch = <46>;
hsync-len = <1>;
vback-porch = <23>;
vfront-porch = <22>;
vsync-len = <1>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <1>;
pixelclk-active = <0>;
};
};
};
};
&iomuxc {
pinctrl-names = "default";
imx6ul-evk {
pinctrl_remote_control: remote_control {
fsl,pins = <
MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX 0x000010B1
>;
};
mylcd_pinctrl: mylcd_pingrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO08__GPIO1_IO08 0x000010B0
MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x000010B0
MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x000010B0
MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x000010B0
MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x000010B0
MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x000010B0
MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x000010B0
MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x000010B0
MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x000010B0
MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x000010B0
MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x000010B0
MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x000010B0
MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x000010B0
MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x000010B0
MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x000010B0
MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x000010B0
MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x000010B0
MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x000010B0
MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x000010B0
MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x000010B0
MX6UL_PAD_LCD_DATA18__LCDIF_DATA18 0x000010B0
MX6UL_PAD_LCD_DATA19__LCDIF_DATA19 0x000010B0
MX6UL_PAD_LCD_DATA20__LCDIF_DATA20 0x000010B0
MX6UL_PAD_LCD_DATA21__LCDIF_DATA21 0x000010B0
MX6UL_PAD_LCD_DATA22__LCDIF_DATA22 0x000010B0
MX6UL_PAD_LCD_DATA23__LCDIF_DATA23 0x000010B0
MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x000010B0
MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x000010B0
MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x000010B0
>;
};
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_pwm1: pwm1grp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO08__PWM1_OUT 0x000010B1
>;
};
};
};
&iomuxc_snvs {
pinctrl-names = "default_snvs";
pinctrl-0 = <&pinctrl_tsc_irq>;
imx6ul-evk {
pinctrl_hog_2: hoggrp-2 {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x80000000
>;
};
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 0x000110A1
>;
};
};
};
&pwm1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm1>;
status = "disabled";
};
|