0
点赞
收藏
分享

微信扫一扫

Linux内核LCD驱动分析与换屏方法(Tiny4412)

Linux内核中换屏技术​

21.5.1 u-boot中的参数bootargs实现换屏

在uboot中有一个 bootargs环境变量,这个参数就是传递数据给内核的。

对tiny4412提供的内核,可以通过修改 bootargs 实现驱动不同的LCD屏。


21.5.2 分析bootargs中的lcd参数

再启动uboot的时候会有如下的环境参数:

bootargs=noinitrd root=/dev/nfs nfsroot=192.168.10.106:/home/xyd/rootfs/

ip=192.168.10.123:192.168.10.106:192.168.10.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0 coherent_pool=2M lcd=S702


每个参数之间都是空格分开。

可以看到上面传递了2,启动内核之后就可以驱动S702屏。

在内核启动阶段,内核会根据lcd=S720这个信息,在注册平台设备/驱动前LCD平台数据修改了。

21.5.3 分析在注册平台设备/驱动前修改lcd平台数据过程

首先从板级文件Mach-tiny4412.c中开始看。在这个文件中我们会看到一个机器初始化函数

(1) 机器初始化函数中多平台设备注册代码段

代码段如下,在02299行可以看到:

static void __init smdk4x12_machine_init(void)

{

……

//注册多平台设备

platform_add_devices(smdk4x12_devices, //包含LCD平台设备结构指针

ARRAY_SIZE(smdk4x12_devices));//数组元素个数,即平台设备个数

……

}


注册平台设备函数原型:

int platform_add_devices(struct platform_device **devs, int num)


LCD平台设备层结构指针就是存放在 smdk4x12_devices的,以上函数注册包含LCD平台设备。所以,我们可以推出修改平台数据的地方应该在 platform_add_devices 函数前


问题:在哪里修改lcd平台数据?


在platform_add_devices之前,有和lcd相关代码,下面的调用是在有触摸屏的时候才调用。我们只是把它作为例子来找到能识别s702屏的地方。

后边会讲到真正调用识别S702屏的地方。

代码如下:

static void __init smdk4x12_machine_init(void)

{

……

#ifdef CONFIG_TOUCHSCREEN_FT5X0X//配置FT5X0X触摸屏的宏

struct s3cfb_lcd *lcd = tiny4412_get_lcd();

ft5x0x_pdata.screen_max_x = lcd->width;

ft5x0x_pdata.screen_max_y = lcd->height;

#endif

……

platform_add_devices(smdk4x12_devices, ARRAY_SIZE(smdk4x12_devices));

……

}



其实真正的获得lcd参数结构是在tiny4412_fb_init_pdata函数里边调用,后面会有分析。


问题:现在我们分析如何通过bootargc传参获得lcd平台数据?


(2) 机器初始化函数中调用一个和lcd相关的tiny4412_get_lcd函数

SI里边调转,此函数在Tiny4412-lcds.c (linux-3.5\arch\arm\mach-exynos)文件下。

代码如下:

struct s3cfb_lcd *tiny4412_get_lcd(void)

{

return tiny4412_lcd_config[lcd_idx].lcd;

}


这个函数只返回一个数组元素,此数组定义和初始化如下:

在文件Tiny4412-lcds.c (linux-3.5\arch\arm\mach-exynos) 中。

代码如下:是一个结构体数组。

static struct {

char *name;

struct s3cfb_lcd *lcd;

int ctp;

} tiny4412_lcd_config[] = {

{ "HD700",&wxga_hd700, 1 },

{ "S70",&wvga_s70, 1 },

{ "S702",&wvga_s70, 3 },

{ "W50",&wvga_w50, 0 },

{ "W101",&wsvga_w101, 1 },

{ "A97",&xga_a97, 0 },

{ "LQ150",&xga_lq150, 1 },

{ "L80",&vga_l80, 1 },

{ "HD101",&wxga_hd101, 1 },

{ "BP101",&wxga_bp101, 1 },

{ "HDM",&hdmi_def, 0 },/* Pls keep it at last */

};



所以我们就要关注lcd_idx值,在哪里设置这个值。

SI跳转得到如下:

static int lcd_idx = 0; //在本文件中定义为静态变量


(3) 在哪里得到lcd_idx的值

我们采用搜索的方式查找lcd_idx,可以找到以下:

Tiny4412-lcds.c (arch\arm\mach-exynos):lcd_idx = i;


进入查看搜索语句所在代码:

static int __inittiny4412_setup_lcd(char *str)

{

int i;

……

/* 这里想通过名字查找对应的lcd配置参数,返回下标号lcd_idx */

for (i = 0; i < ARRAY_SIZE(tiny4412_lcd_config); i++) {

if (!strcascmp(tiny4412_lcd_config[i].name, str)) {

lcd_idx = i;

break;

}

}

__ret:

printk("TINY4412: %s selected\n", tiny4412_lcd_config[lcd_idx].name);

return 0;

}


这个是static静态函数,在本文件中有效。

这里我们可以知道tiny4412_lcd_config是一个数组,它就是存放所有支持的LCD屏信息。 通过比较数组成员nametiny4412_setup_lcd参数str决定lcd_idx的值。​

到这里就有疑问了:tiny4412_setup_lcd在哪里被调用?​

(4) 补充知识:字符串比较函数。

int strcasecmp(const char *s1, const char *s2)

{

int c1, c2;


do {

c1 = tolower(*s1++); //转换小写

c2 = tolower(*s2++); //转换小写

} while (c1 == c2 && c1 != 0);

return c1 - c2;

}

这个函数是不区分大小写比较


21.5.4 几个重要结构

(1) 结构定义即初始化

这个结构体是当前板子所支持的lcd屏列表。

/* 定义lcd屏支持列表 */

static struct {

char *name;

struct s3cfb_lcd *lcd;

int ctp;

} tiny4412_lcd_config[] = {

{ "HD700",&wxga_hd700, 1 },

{ "S70",&wvga_s70, 1 },//我们当前板子上所使用的屏

{ "W50",&wvga_w50, 0 },

{ "W101",&wsvga_w101, 1 },

{ "A97",&xga_a97, 0 },

{ "HDM",&hdmi_def, 0 },/* Pls keep it at last */

};


里边struct s3cfb_lcd *lcd;结构成员是存放lcd屏的工作时序参数。

(2)结构体定义

如下:这个结构已经包含LCD驱动所需要的平台数据信息。

/*

* @width:horizontal resolution---水平分辨率

* @height:vertical resolution---垂直分辨率

* @p_width:width of lcd in mm---屏物理尺寸(宽,mm单位)

* @p_height:height of lcd in mm---屏物理尺寸(高,mm单位)

* @bpp:bits per pixel

* @freq:vframe frequency---刷屏频率

* @timing:timing values--时序参数

* @polarity:polarity settings---极性参数

*/

struct s3cfb_lcd {

intwidth;

intheight;

intp_width;

intp_height;

intbpp;

intfreq;

structs3cfb_lcd_timing timing;

structs3cfb_lcd_polarity polarity;

};


(3) s3cfb_lcd_timing屏的时序参数结构

/*

* @h_fp:horizontal front porch

* @h_bp:horizontal back porch

* @h_sw:horizontal sync width

* @v_fp:vertical front porch

* @v_fpe:vertical front porch for even field

* @v_bp:vertical back porch

* @v_bpe:vertical back porch for even field

*/

struct s3cfb_lcd_timing {

inth_fp;

inth_bp;

inth_sw;

intv_fp;

intv_fpe;

intv_bp;

intv_bpe;

intv_sw;

};



(4) 屏的时序极性配置

/*

* @rise_vclk:if 1, video data is fetched at rising edge

* @inv_hsync:if HSYNC polarity is inversed

* @inv_vsync:if VSYNC polarity is inversed

* @inv_vden:if VDEN polarity is inversed

*/

struct s3cfb_lcd_polarity {

intrise_vclk;

intinv_hsync;

intinv_vsync;

intinv_vden;

};



21.5.5 以bootargc传入lcd=S70为例子填充s3cfb_lcd结构

定义struct s3cfb_lcd结构变量wvga_s70 ,并初始化屏参数;

在文件Tiny4412-lcds.c (arch\arm\Mach-exynos)下实现。

//以下信息是根据LCD屏的手册得到的

static struct s3cfb_lcdwvga_s70= {

.width = 800,

.height = 480,

.p_width = 155,

.p_height = 93,

.bpp = 24,

.freq = 63,

.timing = {//屏的时序参数结构

.h_fp = 80,

.h_bp = 36,

.h_sw = 10,

.v_fp = 22,

.v_fpe = 1,

.v_bp = 15,

.v_bpe = 1,

.v_sw = 8,

},

.polarity = {//屏的时序极性结构

.rise_vclk = 1,

.inv_hsync = 1,

.inv_vsync = 1,

.inv_vden = 0,

},

};


21.5.6 确定是谁使用 tiny4412_setup_lcd(char *str),str是否是"s70"

通过搜索tiny4412_setup_lcd的方式,在本文件中可以找到使用处:

Linux内核LCD驱动分析与换屏方法(Tiny4412)_初始化


我们可以知道是被early_param调用:

early_param("lcd", tiny4412_setup_lcd);


early_param这个宏是内核用来在系统启动初期需要解析的参数。

如:

上面的语句,系统启动初期会判断在bootargc环境变量中有lcd这个字符串;

如果有则把lcd =的值作为 tiny4412_setup_lcd函数的参数;

然后调用执行tiny4412_setup_lcd函数。


补充知识:early_param宏

/* early_param用于注册内核选项解析的处理函数 */

#define early_param(str, fn) \

__setup_param(str, fn, fn, 1)

str:参数名,就是bootargs 参数传递进来的参数对左边的字符串。

fn:处理函数,原型是 int XXX(char *s); 这个函数的参数是就 str=后面的字符串

示例

…… lcd=s70

就是作为传递给处理函数fn。


经过上面的分析已经知道 struct s3cfb_lcd *tiny4412_get_lcd(void) 返回屏的时序参数结构。

之前说真正调用tiny4412_get_lcd是:

smdk4x12_machine_init调用tiny4412_fb_init_pdata;

tiny4412_fb_init_pdata里边调用了tiny4412_get_lcd。


所以,就要知道哪里使用了tiny4412_get_lcd(void)返回的s3cfb_lcd *结构指针


21.5.7 真正调用了tiny4412_get_lcd(void),并使用返回Lcd结构指针修改屏的参数结构

在机器初始化函数中往下看,调用了一个tiny4412_fb_init_pdata函数,该函数参数就是LCD驱动数据结构指针。

真正的用意修改函数参数,就是修改LCD驱动数据结构指针(smdk4x12_lcd0_pdata)里边的数据,实现这个LCD驱动数据结构(smdk4x12_lcd0_pdata)填充的是s70的参数数据。

代码如下:

static void __init smdk4x12_machine_init(void)

{

……

samsung_bl_set(&smdk4x12_bl_gpio_info, &smdk4x12_bl_data);

//调用下面函数,其中有部分代码修改了smdk4x12_lcd0_pdata结构的数据。

tiny4412_fb_init_pdata(&smdk4x12_lcd0_pdata);

s5p_fimd0_set_platdata(&smdk4x12_lcd0_pdata);

……

platform_add_devices(smdk4x12_devices, ARRAY_SIZE(smdk4x12_devices));

……

}



先分析tiny4412_fb_init_pdata送进来的参数smdk4x12_lcd0_pdata是什么?

static struct s3c_fb_platdata smdk4x12_lcd0_pdata __initdata = {

.win[0]= &smdk4x12_fb_win0,

.win[1]= &smdk4x12_fb_win1,

.win[2]= &smdk4x12_fb_win2,

.win[3]= &smdk4x12_fb_win3,

.win[4]= &smdk4x12_fb_win4,

.vtiming= &smdk4x12_lcd_timing,

.vidcon0= VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,

.vidcon1= VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,

.setup_gpio= exynos4_fimd0_gpio_setup_24bpp,

};



struct s3c_fb_platdata结构原型:

struct s3c_fb_platdata {

void(*setup_gpio)(void);

struct s3c_fb_pd_win *win[S3C_FB_MAX_WIN];

struct fb_videomode *vtiming;

u32 vidcon0;

u32 vidcon1;

};



以上结构填充的一些默认数据在调用tiny4412_fb_init_pdata函数之后会被改掉,修改成s70屏的参数数据。


再分析tiny4412_fb_init_pdata函数的实现

这个函数实现在Mach-tiny4412.c (linux-3.5\arch\arm\mach-exynos)中。

该函数中把struct s3c_fb_platdata * smdk4x12_lcd0_pdata结构中的的数据设置到s3c_fb_pd_win窗口结构fb_videomode结构里边去

代码如下:

static void __init tiny4412_fb_init_pdata(struct s3c_fb_platdata *pd)

{

struct s3cfb_lcd *lcd;

struct s3c_fb_pd_win *win;

//把送进来的结构vtiming赋值

struct fb_videomode *mode = pd->vtiming;

unsigned long val = 0;

u64 pixclk = 1000000000000ULL;

u32 div;

int i;


/* 真的调用tiny4412_get_lcd获取s70 lcd屏参数

这个lcd屏参数是通过命令行传递lcd屏型号来找到具体是哪一个参数的 */

lcd = tiny4412_get_lcd();


/* 设置(修改)smdk4x12_lcd0_pdata结构中每个lcd窗口的参数 */

for (i = 0; i < S3C_FB_MAX_WIN; i++) {

/* 过滤空的lcd窗口结构 */

if (pd->win[i] == NULL)

continue;

/* 把lcd参数设置到s3c_fb_pd_win窗口结构中 */

//送进来的结构窗口赋值,修改其中的参数

win = pd->win[i];

win->xres = lcd->width;

win->yres = lcd->height;

win->default_bpp = lcd->bpp ? : 24;

win->virtual_x = win->xres;

win->virtual_y = win->yres * CONFIG_FB_S3C_NR_BUFFERS;

win->width = lcd->p_width;

win->height = lcd->p_height;

}


/* 把屏的时序设置到fb_videomode结构中,lcd注册需要使用到 */

mode->left_margin = lcd->timing.h_bp;

mode->right_margin = lcd->timing.h_fp;

mode->upper_margin = lcd->timing.v_bp;

mode->lower_margin = lcd->timing.v_fp;

mode->hsync_len = lcd->timing.h_sw;

mode->vsync_len = lcd->timing.v_sw;

mode->xres = lcd->width;

mode->yres = lcd->height;

/* calculates pixel clock */

div = mode->left_margin + mode->hsync_len + mode->right_margin +

mode->xres;

div *= mode->upper_margin + mode->vsync_len + mode->lower_margin +

mode->yres;

/*如果没有自定义频率,则设置为60Hz*/

div *= lcd->freq ? : 60;

do_div(pixclk, div);

mode->pixclock = pixclk + 386;//386

/* initialize signal polarity of RGB interface */

if (lcd->polarity.rise_vclk)

val |= VIDCON1_INV_VCLK;

if (lcd->polarity.inv_hsync)

val |= VIDCON1_INV_HSYNC;

if (lcd->polarity.inv_vsync)

val |= VIDCON1_INV_VSYNC;

if (lcd->polarity.inv_vden)

val |= VIDCON1_INV_VDEN;


//修改vidcon1的值

pd->vidcon1 = val;

}



到这里我们需要知道:在平台设备和驱动注册前修改lcd平台数据设置完成。


21.5.8 补充知识:tiny4412_fb_init_pdata涉及到几个结构。

1、屏窗口结构

struct s3c_fb_pd_win {

unsigned shortdefault_bpp;

unsigned shortmax_bpp;

unsigned shortxres;

unsigned shortyres;

unsigned shortvirtual_x;

unsigned shortvirtual_y;

unsigned shortwidth;

unsigned shortheight;

};



2、屏的工作时序模式结构

struct fb_videomode {

const char *name;/* optional */

u32 refresh;/* optional */

u32 xres;

u32 yres;

u32 pixclock;

u32 left_margin;

u32 right_margin;

u32 upper_margin;

u32 lower_margin;

u32 hsync_len;

u32 vsync_len;

u32 sync;

u32 vmode;

u32 flag;

};



到此,S70屏的数据应经存放在smdk4x12_lcd0_pdata结构中,但是谁来用这个已经修改好的smdk4x12_lcd0_pdata屏数据结构呢?

按正常来讲应该是平台设备结构的dev结构成员里边私有数据成员。


接下来我们来找这个私有数据成员。

21.5.9 内核带的平台结构变量s5p_device_fimd0没有初始化平台数据成员

到这里我们还有还有一个问题:内核带的平台数据结构变量没有初始化平台数据成员。

在devs.c中定义如下:

static 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),

// 此处应该初始化平台数据,但是没有。???

},

};



以上结构根据没有初始化已经修改好的平台数据。


平台设备结构原型:

struct platform_device {

const char *name;

int id;

//这个结构里边有一个void *platform_data成员

struct device dev;

u32 num_resources;

struct resource*resource;

const struct platform_device_id*id_entry;

struct mfd_cell *mfd_cell;

struct pdev_archdata archdata;

};



在平台设备结构中成员struct device dev结构原型:

struct device {

struct device *parent;

struct device_private*p;

struct kobject kobj;

const char *init_name; /* initial name of the device */

const struct device_type *type;

struct mutexmutex;/* mutex to synchronize calls toits driver*/

struct bus_type*bus; /* type of bus device is on */

struct device_driver *driver;/* which driver has allocated this device */

Void *platform_data;/* Platform specific data, device

core doesn't touch it */

struct dev_pm_infopower;

struct dev_pm_domain *pm_domain;


#ifdef CONFIG_NUMA

int numa_node;

#endif

u64 *dma_mask;

u64 coherent_dma_mask;

struct device_dma_parameters *dma_parms;

struct list_headdma_pools;

struct dma_coherent_mem*dma_mem;

#ifdef CONFIG_CMA

struct cma *cma_area;

#endif

struct dev_archdataarchdata;

struct device_node*of_node;

dev_tdevt;

u32 id;

spinlock_t devres_lock;

struct list_head devres_head;

struct klist_node knode_class;

struct class*class;

const struct attribute_group **groups;

void(*release)(struct device *dev);

};



没有看到有填充私有数据,那么一定在注册s5p_device_fimd0前使用其他方法把私有数据设置进来,比如调用函数设置了平台数据。

因除了这个结构初始化平台设备数据,没有其他的会做这个事情了。


21.5.10 在lcd数据初始化之后,又对lcd数据进行了设置

搜索 smdk4x12_lcd0_pdata 平台数据结构变量。

查看其他地方私有了这个数据结构。

static void __init smdk4x12_machine_init(void)

{

……

tiny4412_fb_init_pdata(&smdk4x12_lcd0_pdata);

//在lcd数据初始化之后,又对lcd数据进行了设置

s5p_fimd0_set_platdata(&smdk4x12_lcd0_pdata);

……

platform_add_devices(smdk4x12_devices, ARRAY_SIZE(smdk4x12_devices));

……

}


在lcd数据初始化之后,又调用函数s5p_fimd0_set_platdata对lcd数据进行了设置(修改)。


s5p_fimd0_set_platdata函数原型

void __init s5p_fimd0_set_platdata(struct s3c_fb_platdata *pd)

{

s3c_set_platdata(pd, sizeof(struct s3c_fb_platdata),&s5p_device_fimd0);

}


注意:

以上的参数s5p_device_fimd0 就是lcd平台设备结构变量。

pd :返推回去就是 smdk4x12_lcd0_pdata。


s3c_set_platdata函数原型

可以知道pdev 就是 &s5p_device_fimd0。

void __init *s3c_set_platdata(void *pd, //把平台数据结构传进来

size_t pdsize,//平台数据结构的长度

struct platform_device *pdev)//平台设备结构

{

void *npd;


if (!pd) {

printk(KERN_ERR "%s: no platform data supplied\n", pdev->name);

return NULL;

}

npd = kmemdup(pd, pdsize, GFP_KERNEL);

if (!npd) {

printk(KERN_ERR "%s: cannot clone platform data\n", pdev->name);

return NULL;

}

pdev->dev.platform_data = npd;

return npd;

}



该函数先开辟平台数据结构(pdsize)空间,然后把 pd 数据复制到新开辟的空间,返回新的空间地址。

kmemdup函数会开辟一个空间,然后把 pd 指向内存空间中的 pdsize 字节数量复制到新开辟的空间中,返回新开辟空间首地址。

所以,在我们知道该函数先给smdk4x12_lcd0_pdata结构开辟空间;

然后把smdk4x12_lcd0_pdata结构中的数据复制到这个新开辟的空间中;

得到这个存有smdk4x12_lcd0_pdata结构数据的新空间首地址赋值给:

s5p_device_fimd0->dev.platform_data

此处我们知道s5p_device_fimd0结构的dev结构私有数据就是smdk4x12_lcd0_pdata结构的首地址。

到此,平台设备的数据都已经准备好,只等拿去注册了。


  • 小知识:

那有时候会有同学问为什么要这样做?为什么不直接填充到平台结构的私有数据?

出于内存使用合理性考虑。

  1. 定义在静态存储区,则这个结构的生命周期和系统同在。

只要系统运行着始终会存在,就算驱动模块卸载了,还存在。

2)如果把定义堆空间中,在驱动卸载时候可以把它占用的空间释放

所以内核中就这样的调用kmemdup函数,在堆空间中开辟一块区域,便于释放。


注意:平台数结构变量加了 __initdata 修饰,这块内存在启动完成后,挂接文件系统后会释放。所以,所在传递给驱动的信息,如果定义时候添加 __initdata 修饰,则需要重新开辟一个空间,把它的值复制到里面去。


下面我通过注册时使用的smdk4x12_devices平台数据结构指针变量反推来查找注册关联。

21.5.11 smdk4x12_devices平台数据结构指针变量定义及填充

就在Mach-tiny4412.c (linux-3.5\arch\arm\mach-exynos)文件中;

定义了注册多平台设备用到的smdk4x12_devices平台数据结构指针变量

代码如下:

static struct platform_device *smdk4x12_devices[] __initdata = {

#ifdef CONFIG_EXYNOS4_DEV_DWMCI

&exynos_device_dwmci,

#endif

&s3c_device_hsmmc2,

&s3c_device_hsmmc3,

&wm8994_fixed_voltage0,

&wm8994_fixed_voltage1,

&wm8994_fixed_voltage2,

&s3c_device_i2c0,

&s3c_device_i2c1,

&s3c_device_i2c2,

&s3c_device_i2c3,

#ifdef CONFIG_VIDEO_M5MOLS

&s3c_device_i2c4,

#endif

&s3c_device_i2c7,

&s3c_device_adc,

&s3c_device_rtc,

&s3c_device_wdt,

#ifdef CONFIG_TINY4412_BUZZER

&s3c_device_timer[0],

#endif

#ifdef CONFIG_VIDEO_EXYNOS_FIMC_LITE

&exynos_device_flite0,

&exynos_device_flite1,

#endif

&s5p_device_mipi_csis0,

&s5p_device_mipi_csis1,

&s5p_device_fimc0,

&s5p_device_fimc1,

&s5p_device_fimc2,

&s5p_device_fimc3,

&s5p_device_fimc_md,

&s5p_device_fimd0,//在这里把屏的平台设备结构填充进来

&mali_gpu_device,

&s5p_device_mfc,

&s5p_device_mfc_l,

&s5p_device_mfc_r,

&s5p_device_jpeg,

#ifdef CONFIG_SAMSUNG_DEV_KEYPAD

&samsung_device_keypad,

#endif

&tiny4412_device_1wire,

&tiny4412_device_adc,

#ifdef CONFIG_INPUT_GPIO

&tiny4412_input_device,

#endif

#ifdef CONFIG_IR_GPIO_CIR

&tiny4412_device_gpiorc,

#endif

#ifdef CONFIG_VIDEO_EXYNOS_FIMC_IS

&exynos4_device_fimc_is,

#endif

#ifdef CONFIG_LCD_LMS501KF03

&s3c_device_spi_gpio,

#endif

#ifdef CONFIG_S3C64XX_DEV_SPI0

&s3c64xx_device_spi0,

#endif

#ifdef CONFIG_S3C64XX_DEV_SPI1

&s3c64xx_device_spi1,

#endif

#ifdef CONFIG_S3C64XX_DEV_SPI2

&s3c64xx_device_spi2,

#endif

#ifdef CONFIG_ION_EXYNOS

&exynos_device_ion,

#endif

&s5p_device_i2c_hdmiphy,

&s5p_device_hdmi,

&s5p_device_mixer,

&exynos4_bus_devfreq,

&samsung_asoc_dma,

&samsung_asoc_idma,

#ifdef CONFIG_SND_SAMSUNG_I2S

&exynos4_device_i2s0,

#endif

#ifdef CONFIG_SND_SAMSUNG_PCM

&exynos4_device_pcm0,

#endif

#ifdef CONFIG_SND_SAMSUNG_SPDIF

&exynos4_device_spdif,

#endif

&tiny4412_audio,

#ifdef CONFIG_VIDEO_EXYNOS_FIMG2D

&s5p_device_fimg2d,

#endif

#ifdef CONFIG_EXYNOS_THERMAL

&exynos_device_tmu,

#endif

&s5p_device_ehci,

&exynos4_device_ohci,

&s5p_device_usbswitch,

#if defined CONFIG_SND_SAMSUNG_ALP

&exynos_device_srp,

#endif

#ifdef CONFIG_BUSFREQ_OPP

&exynos4_busfreq,

#endif

#ifdef CONFIG_BATTERY_SAMSUNG

&samsung_device_battery,

#endif

};



到此,调用注册函数就可以把屏注册进内核了。


举报

相关推荐

0 条评论