开发板:tiny4412(1611)
内核:linux4.4
编译器: arm-none-linux-gnueabi-gcc (gcc version 4.8.3 20140320)
参考linux3.5版本的驱动,显示功能一般都是平台设备。
tiny4412-lcds.c 中配置了屏幕的一些参数。mach-tiny4412.c文件中的smdk4x12_machine_init 函数,通过里面的tiny4412_fb_init_pdata(&smdk4x12_lcd0_pdata); 和s5p_fimd0_set_platdata(&smdk4x12_lcd0_pdata); 填充到平台设备s5p_device_fimd0 ;再通过 platform_add_devices(smdk4x12_devices, ARRAY_SIZE(smdk4x12_devices)); 注册,在exynos4_map_io()函数中通过s5p_fb_setname(0, "exynos4-fb") 把s5p_device_fimd0 设备名称改为exynos4-fb 。
对应的驱动即是driver/video/s3c-fb.c 。
static struct resource s5p_fimd0_resource[] = {
[0] = DEFINE_RES_MEM(S5P_PA_FIMD0, SZ_32K),
[1] = DEFINE_RES_IRQ(IRQ_FIMD0_VSYNC),
[2] = DEFINE_RES_IRQ(IRQ_FIMD0_FIFO),
[3] = DEFINE_RES_IRQ(IRQ_FIMD0_SYSTEM),
};
struct platform_device s5p_device_fimd0 = {
.name = "s5p-fb",
.id = 0,
.num_resources = ARRAY_SIZE(s5p_fimd0_resource),
.resource = s5p_fimd0_resource,
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
};
驱动待调试。。。。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <asm/uaccess.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>
#include <video/samsung_fimd.h>
static struct fb_info *fbi=NULL;
//void __iomem *fb_base;
static volatile void __iomem *regs1=NULL;
static volatile void __iomem *regs2=NULL;
struct clk *sclk=NULL;
struct clk *clk_parent=NULL;
static int s3d_setcolreg(unsigned regno,
unsigned red, unsigned green, unsigned blue,
unsigned transp, struct fb_info *info)
{
return 0;
}
static struct fb_ops fb_ops = {
.owner = THIS_MODULE,
//.fb_setcolreg = s3d_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
static int fb_probe(struct platform_device *pdev) {
struct device *dev = &pdev->dev;
int ret;
u32 reg;
printk("%s enter.\n", __func__);
sclk = devm_clk_get(dev,"sclk_fimd");
if (IS_ERR(sclk)) {
pr_err("Unable to get clock\n");
return PTR_ERR(sclk);
}
clk_parent=devm_clk_get(dev,"mout_mpll_user");
if (IS_ERR(clk_parent)) {
pr_err("Unable to get parent clock\n");
return PTR_ERR(clk_parent);
}
/* error
ret=clk_set_parent(sclk, clk_parent);
if (ret) {
printk("%d",ret);
pr_err("Unable to set parent clock \n");
return PTR_ERR(sclk);
}
if (clk_set_rate(sclk, 800000000)) {
pr_err("rate change failed\n");
return PTR_ERR(sclk);
}
*/
clk_prepare(sclk);
ret=clk_enable(sclk);
clk_prepare(clk_parent);
ret=clk_enable(clk_parent);
printk("%ld\n",clk_get_rate(clk_parent));
/*
res=platform_get_resource(pdev,IORESOURCE_MEM,0);
if(!res){
dev_err(dev, "failed to get timer base mem resource\n");
return -ENOMEM;
}
fb_reg=devm_ioremap_resource(dev,res);
if(fb_reg<0){
dev_err(dev, "failed to get timer base mem\n");
return -ENOMEM;
}
*/
regs1=ioremap(0x11C00000,0x8000);
printk("map: %p\n",regs1);
regs2=ioremap(0x10030000,0xd000);
printk("map: %p\n",regs2);
/* output polarity controls */
printk("test\n");
reg = 0xe0|(1 << 9);
writel( reg, regs1 + VIDCON1);
printk("ok\n");
/* set video clock running at under-run */
reg = readl( regs1 + VIDCON1);
reg &= ~VIDCON1_VCLK_MASK;
reg |= VIDCON1_VCLK_RUN;
writel(reg, regs1 + VIDCON1);
/* zero all windows before we do anything */
/* initialise colour key controls */
/* fb set rgb timing */
/* 1. selcet SCLKMPLL_USER_T 0110 : 800M*/
#define CLK_SRC_LCD0 0xC234
reg = readl( regs2 + CLK_SRC_LCD0); //0x1003_0000
reg &= ~0x0f;
reg |= 0x06;
writel( reg, regs2 + CLK_SRC_LCD0);
/* 2. set SCLK_FIMD0 divider ratio 1:1 */
#define CLK_DIV_LCD 0xC534
reg = readl( regs2 + CLK_DIV_LCD); //0x1003_0000
reg &= ~0x0f;
writel( reg, regs2 + CLK_DIV_LCD);
/* 3. set VCLK divider ratio 24:1 =33.3M*/ //Is there others should to set?
reg = readl( regs1 + VIDCON0);
reg &= ~(0xf<<6);
reg |= 0x17<<6;
writel( reg, regs1 + VIDCON0);
/* 4. set horizontal and vertical lcd rgb interface timing */
//set VBPD VFPD VSFW - 12 21 9
reg = readl( regs1 + VIDTCON0);
reg &= ~(0xfff);
reg |= (12<<16) | (21<<8) | 9;
writel( reg, regs1 + VIDTCON0);
//set HBPD HFPD HSPW - 35 209 9
reg = readl( regs1 + VIDTCON1);
reg &= ~(0xfff);
reg |= (35<<16) | (209<<8) | 9;
writel( reg, regs1 + VIDTCON1);
//set LINEVAL HOZVAL - 479 799
writel( (479<<11) | 799, regs1 + VIDTCON2);
//VIDTCON3 config the FrameINT ,which is used for double buffer
/* set data format */
// 24bpp [5:2]:1011 enable signal [0]:1
reg = readl( regs1 + WINCON(0));
reg &= ~( ( 0b1111 <<2 ) | 1 );
reg |= ( 0b1011 <<2 ) |1 ;
writel( reg, regs1 + WINCON(0));
// enable chanel 0 [0]:1
reg = readl( regs1 + SHADOWCON);
reg &= ~ 1 ;
reg |= 1 ;
writel( reg, regs1 + SHADOWCON);
// select chanel [18:16]:1 [2:0]:1
#define WINCHMAP2 0x003C
reg = readl( regs1 + WINCHMAP2);
reg &= ~ ( ( 0b111<<16 ) | 0b111);
reg |= (1<<16) | 1;
writel( reg, regs1 + WINCHMAP2);
// set coordinate
//left top
#define VIDOSD0A 0x0040
writel( 0, regs1 + VIDOSD0A);
// right bottom [21:11]:800 [10:0]:480
#define VIDOSD0B 0x0044
writel( ( 799<<11 ) | 479, regs1 + VIDOSD0B);
// size
#define VIDOSD0C 0x0048
writel( 480*800, regs1 + VIDOSD0C);
/* we have the register setup, start allocating framebuffers */
fbi = framebuffer_alloc(0, NULL);
fbi->flags = FBINFO_DEFAULT;
fbi->fbops = &fb_ops;
//fbi->screen_base = fb_base; need to be set
fbi->screen_size = 800*480*4;
// fill fix common fields
strlcpy(fbi->fix.id, "win9_fb", sizeof(fbi->fix.id));
//fbi->fix.smem_start = sp->fb_base_phys; need to be set
fbi->fix.smem_len = 800*480*4;
fbi->fix.type = FB_TYPE_PLANES;
fbi->fix.visual = FB_VISUAL_TRUECOLOR;
fbi->var.xres = 800;
fbi->var.yres = 480;
fbi->var.xres_virtual = 800;
fbi->var.yres_virtual = 480;
fbi->var.bits_per_pixel = 32; //32 or 24 ??
fbi->var.red.offset = 16;
fbi->var.red.length = 8;
fbi->var.green.offset = 8;
fbi->var.green.length = 8;
fbi->var.blue.offset = 0;
fbi->var.blue.length = 8;
// allocate mem
fbi->screen_base = dma_alloc_writecombine(dev, fbi->fix.smem_len, (dma_addr_t *)&fbi->fix.smem_start, GFP_KERNEL);
// start adress of video buffer(win0-0)
#define VIDW00ADD0B0 0x00A0
writel( fbi->fix.smem_start, regs1 + VIDW00ADD0B0); //phys_address
// end address of video buffer(win0-0)
#define VIDW00ADD1B0 0x00D0
writel( fbi->fix.smem_start + fbi->fix.smem_len , regs1 + VIDW00ADD1B0);
// pagewidth [12:0] 800*4
#define VIDW00ADD2 0x0100
writel( 800*4 , regs1 + VIDW00ADD2);
// start output signal
reg = readl( regs1 + VIDCON0);
writel(reg | 0x01, regs1 + VIDCON0);
ret = register_framebuffer(fbi);
return ret;
}
static int fb_remove(struct platform_device *pdev) {
return 0;
}
static const struct of_device_id dt_ids[] = {
{ .compatible = "tiny4412,lcd_win9", },
{},
};
MODULE_DEVICE_TABLE(of, dt_ids);
static struct platform_driver fb_driver = {
.driver = {
.name = "fb",
.of_match_table = of_match_ptr(dt_ids),
},
.probe = fb_probe,
.remove = fb_remove,
};
static int fb_init(void){
return platform_driver_register(&fb_driver);
}
static void fb_exit(void){
platform_driver_unregister(&fb_driver);
}
module_init(fb_init);
module_exit(fb_exit);
MODULE_LICENSE("GPL");