日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux下的Backlight子系统

發(fā)布時(shí)間:2024/9/21 linux 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux下的Backlight子系统 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

版權(quán)所有,轉(zhuǎn)載必須說明轉(zhuǎn)自?http://my.csdn.net/weiqing1981127?

原創(chuàng)作者:南京郵電大學(xué)? 通信與信息系統(tǒng)專業(yè) 研二 魏清

一.Backlight背光子系統(tǒng)概述

我們的LCD屏常常需要一個(gè)背光,調(diào)節(jié)LCD屏背光的亮度,這里所說的背光不是僅僅亮和不亮兩種,而是根據(jù)用戶的需求,背光亮度是可以任意調(diào)節(jié)。Linux內(nèi)核中有一個(gè)backlight背光子系統(tǒng),該系統(tǒng)就是為滿足用戶這種需求設(shè)計(jì)的,用戶只要根據(jù)自己的LCD背光電路中PWM輸出引腳,對內(nèi)核backlight子系統(tǒng)代碼進(jìn)行相應(yīng)的配置,就可以實(shí)現(xiàn)LCD的背光。

LCD的背光原理主要是由核心板的一根引腳控制背光電源,一根PWM引腳控制背光亮度組成,應(yīng)用程序可以通過改變PWM的頻率達(dá)到改變背光亮度的目的。

?

我們這里主要講解基于backlight子系統(tǒng)的蜂鳴器驅(qū)動(dòng),其實(shí)簡單的使得蜂蜜器發(fā)聲的驅(qū)動(dòng)很簡單,這里只是把蜂鳴器作為一種設(shè)備,而且這種設(shè)備原理類似背光的原理,都是基于pwm的,而我們的終極目的是使用backlight背光子系統(tǒng)。綜上所述,backlight子系統(tǒng)是基于pwm核心的一種驅(qū)動(dòng)接口,如果你使用的一種設(shè)備也是基于pwm的,并且需要用戶可以調(diào)節(jié)pwm的頻率以達(dá)到諸如改變背光亮度,改變蜂鳴器頻率的效果,那么你可以使用這個(gè)backlight背光子系統(tǒng)。

?

二.PWM核心驅(qū)動(dòng)

我們先講解下PWM核心

先熟悉下pwm核心代碼在/arch/arm/plat-s3c/pwm.c

查看/arch/arm/plat-s3c/Makefile

obj-$(CONFIG_HAVE_PWM)???????????? += pwm.o

查看/arch/arm/plat-s3c/Konfig,發(fā)現(xiàn)同目錄的Konfig中無對應(yīng)HAVE_PWM選項(xiàng)

查看/arch/arm/plat-s3c24xx/Konfig

config S3C24XX_PWM

?????? bool "PWM device support"

?????? select HAVE_PWM

?????? help

?????? ? Support for exporting the PWM timer blocks via the pwm device

?????? ? system.

所以配置內(nèi)核make menuconfig?時(shí),需要選中這一項(xiàng)。

?

好了,我們看看pwm.c,它是pwm核心驅(qū)動(dòng),該驅(qū)動(dòng)把設(shè)備和驅(qū)動(dòng)沒有分離開來,都寫在了這個(gè)pwm.c中,我們先看看pwm.c中的驅(qū)動(dòng)部分

static int __init pwm_init(void)

{

?????? int ret;

?????? clk_scaler[0] = clk_get(NULL, "pwm-scaler0");? //獲取0號(hào)時(shí)鐘

?????? clk_scaler[1] = clk_get(NULL, "pwm-scaler1");? //獲取1號(hào)時(shí)鐘

?????? if (IS_ERR(clk_scaler[0]) || IS_ERR(clk_scaler[1])) {

????????????? printk(KERN_ERR "%s: failed to get scaler clocks\n", __func__);

????????????? return -EINVAL;

?????? }

?????? ret = platform_driver_register(&s3c_pwm_driver);? //注冊pwm驅(qū)動(dòng)

?????? if (ret)

????????????? printk(KERN_ERR "%s: failed to add pwm driver\n", __func__);

?????? return ret;

}

跟蹤下s3c_pwm_driver的定義

static struct platform_driver s3c_pwm_driver = {

?????? .driver??????????? = {

????????????? .name????? = "s3c24xx-pwm",? //驅(qū)動(dòng)名

????????????? .owner??? = THIS_MODULE,

?????? },

?????? .probe??????????? = s3c_pwm_probe,? //探測函數(shù)

?????? .remove????????? = __devexit_p(s3c_pwm_remove),

};

我們看看探測函數(shù)s3c_pwm_probe

static int s3c_pwm_probe(struct platform_device *pdev)

{

?????? struct device *dev = &pdev->dev;

?????? struct pwm_device *pwm;

?????? unsigned long flags;

?????? unsigned long tcon;

?????? unsigned int id = pdev->id;

?????? int ret;

?????? if (id == 4) {

????????????? dev_err(dev, "TIMER4 is currently not supported\n");

????????????? return -ENXIO;

?????? }

?????? pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); //分配pwm設(shè)備空間

?????? if (pwm == NULL) {

????????????? dev_err(dev, "failed to allocate pwm_device\n");

????????????? return -ENOMEM;

?????? }

?????? pwm->pdev = pdev;

?????? pwm->pwm_id = id;

?????? pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4;? //計(jì)算TCON中控制哪個(gè)定時(shí)器

?????? pwm->clk = clk_get(dev, "pwm-tin");? //獲取預(yù)分頻后的時(shí)鐘

?????? if (IS_ERR(pwm->clk)) {

????????????? dev_err(dev, "failed to get pwm tin clk\n");

????????????? ret = PTR_ERR(pwm->clk);

????????????? goto err_alloc;

?????? }

?????? pwm->clk_div = clk_get(dev, "pwm-tdiv");

?????? if (IS_ERR(pwm->clk_div)) {???? //獲取二次分頻后的時(shí)鐘

????????????? dev_err(dev, "failed to get pwm tdiv clk\n");

????????????? ret = PTR_ERR(pwm->clk_div);

????????????? goto err_clk_tin;

?????? }

?????? local_irq_save(flags);

?????? tcon = __raw_readl(S3C2410_TCON);

?????? tcon |= pwm_tcon_invert(pwm);?? //信號(hào)反轉(zhuǎn)輸出

?????? __raw_writel(tcon, S3C2410_TCON);

?????? local_irq_restore(flags);

?????? ret = pwm_register(pwm);??????? //注冊pwm設(shè)備

?????? if (ret) {

????????????? dev_err(dev, "failed to register pwm\n");

????????????? goto err_clk_tdiv;

?????? }

?????? pwm_dbg(pwm, "config bits %02x\n",

????????????? (__raw_readl(S3C2410_TCON) >> pwm->tcon_base) & 0x0f);

?????? dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n",

????????????? ?clk_get_rate(pwm->clk),

????????????? ?clk_get_rate(pwm->clk_div),

????????????? ?pwm_is_tdiv(pwm) ? "div" : "ext", pwm->tcon_base);

?????? platform_set_drvdata(pdev, pwm);

?????? return 0;

?err_clk_tdiv:

?????? clk_put(pwm->clk_div);

?err_clk_tin:

?????? clk_put(pwm->clk);

?err_alloc:

?????? kfree(pwm);

?????? return ret;

}

下面看看注冊pwm設(shè)備的函數(shù)pwm_register

static LIST_HEAD(pwm_list);

static int pwm_register(struct pwm_device *pwm)

{

?????? pwm->duty_ns = -1;

?????? pwm->period_ns = -1;

?????? mutex_lock(&pwm_lock);

?????? list_add_tail(&pwm->list, &pwm_list); //pwm設(shè)備掛到pwm_list鏈表上

?????? mutex_unlock(&pwm_lock);

?????? return 0;

}

剩下來,我們看看這個(gè)pwm.c給我們提供了哪些接口函數(shù)

struct pwm_device *pwm_request(int pwm_id, const char *label)

int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)

int pwm_enable(struct pwm_device *pwm)

void pwm_free(struct pwm_device *pwm)

EXPORT_SYMBOL(pwm_request);? //申請PWM設(shè)備

EXPORT_SYMBOL(pwm_config);?? //配置PWM設(shè)備,duty_ns為空占比,period_ns為周期

EXPORT_SYMBOL(pwm_enable);?? //啟動(dòng)Timer定時(shí)器

EXPORT_SYMBOL(pwm_disable);?? //關(guān)閉Timer定時(shí)器

上面這個(gè)函數(shù),只要知道API,會(huì)調(diào)用就行了,在此,我分析下最難的一個(gè)配置PWM函數(shù),這個(gè)函數(shù)主要是根據(jù)周期period_ns,計(jì)算TCNT,根據(jù)空占比duty_ns,計(jì)算TCMP,然后寫入相應(yīng)寄存器。

int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)

{

?????? unsigned long tin_rate;

?????? unsigned long tin_ns;

?????? unsigned long period;

?????? unsigned long flags;

?????? unsigned long tcon;

?????? unsigned long tcnt;

?????? long tcmp;

?????? if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ)

????????????? return -ERANGE;

?????? if (duty_ns > period_ns)

????????????? return -EINVAL;

?????? if (period_ns == pwm->period_ns &&

?????? ??? duty_ns == pwm->duty_ns)

????????????? return 0;

?????? tcmp = __raw_readl(S3C2410_TCMPB(pwm->pwm_id));

?????? tcnt = __raw_readl(S3C2410_TCNTB(pwm->pwm_id));

?????? period = NS_IN_HZ / period_ns; //計(jì)算周期

?????? pwm_dbg(pwm, "duty_ns=%d, period_ns=%d (%lu)\n",

????????????? duty_ns, period_ns, period);

?????? if (pwm->period_ns != period_ns) {

????????????? if (pwm_is_tdiv(pwm)) {

???????????????????? tin_rate = pwm_calc_tin(pwm, period);

???????????????????? clk_set_rate(pwm->clk_div, tin_rate);

????????????? } else

???????????????????? tin_rate = clk_get_rate(pwm->clk);

????????????? pwm->period_ns = period_ns;

????????????? pwm_dbg(pwm, "tin_rate=%lu\n", tin_rate);

????????????? tin_ns = NS_IN_HZ / tin_rate;

????????????? tcnt = period_ns / tin_ns;? //根據(jù)周期求TCNTn=To/Ti

?????? } else

????????????? tin_ns = NS_IN_HZ / clk_get_rate(pwm->clk);

?????? tcmp = duty_ns / tin_ns;?? //根據(jù)空占比求TCMP

?????? tcmp = tcnt - tcmp;? //根據(jù)占空比求TCMP

?????? if (tcmp == tcnt)

????????????? tcmp--;

?????? pwm_dbg(pwm, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt);

?????? if (tcmp < 0)

????????????? tcmp = 0;

?????? local_irq_save(flags);

?????? __raw_writel(tcmp, S3C2410_TCMPB(pwm->pwm_id)); //寫入TCMP

?????? __raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id)); //寫入TCNT

?????? tcon = __raw_readl(S3C2410_TCON);

?????? tcon |= pwm_tcon_manulupdate(pwm);

?????? tcon |= pwm_tcon_autoreload(pwm); //自動(dòng)加載

?????? __raw_writel(tcon, S3C2410_TCON);

?????? tcon &= ~pwm_tcon_manulupdate(pwm); //更新TCNTTCMP

?????? __raw_writel(tcon, S3C2410_TCON);

?????? local_irq_restore(flags);

?????? return 0;

}

?

下面說說這個(gè)周期是怎么設(shè)計(jì)的

我們定時(shí)器的輸出頻率fi=PCLK/(prescaler value+1)/(divider value),這個(gè)可以獲得確定值

我們需要寫入一個(gè)初值nTCNT,這樣就可以獲得一個(gè)頻率,為什么呢?

根據(jù)初值n=fi/fo,那么n=To/Ti

所以當(dāng)用戶給pwm_config函數(shù)傳遞一個(gè)周期period_ns,其實(shí)就是To=period_ns

這樣根據(jù)前面公式n=To/Ti= period_ns/fi,然后將這個(gè)初值n寫入TCNT就可以改變周期了

?

接著我再補(bǔ)充說明下pwm_config函數(shù)里代碼注釋關(guān)于自動(dòng)加載怎么回事?

定時(shí)器工作原理其實(shí)是TCNT的值在時(shí)鐘到來時(shí),減一計(jì)數(shù),每次減一完后,拿當(dāng)前TCNTTCMP比較,如果TCNT=TCMP,那么信號(hào)電平反向輸出,然后TCNT繼續(xù)減一計(jì)數(shù),知道TCNT減到零后,如果有自動(dòng)加載功能那么此時(shí)將由TCNTB把計(jì)數(shù)初值再次寫給TCNTP,同時(shí)TCMPB把比較值給TCMP,這樣就完成一次初值重裝,然后繼續(xù)進(jìn)行計(jì)數(shù)。我們給這種加載模式起了個(gè)名字叫雙緩沖機(jī)制,其中TCMPBTCNTB就是Buffer緩存。

?

前面說pwm.c集驅(qū)動(dòng)和設(shè)備于一體,那么下面我們看看設(shè)備相關(guān)的代碼

#define TIMER_RESOURCE_SIZE (1)

#define TIMER_RESOURCE(_tmr, _irq)?????????????????? \

?????? (struct resource [TIMER_RESOURCE_SIZE]) { \

????????????? [0] = {??????????????????????????????? \

???????????????????? .start?????? = _irq,?????????????????? \

???????????????????? .end = _irq,?????????????????? \

???????????????????? .flags????? = IORESOURCE_IRQ?? \

????????????? }???????????????????????????????? \

?????? }

#define DEFINE_S3C_TIMER(_tmr_no, _irq)????????????????? \

?????? .name???????????? = "s3c24xx-pwm",??????? \

?????? .id?????????? = _tmr_no,?????????????????? \

?????? .num_resources???? = TIMER_RESOURCE_SIZE,???????????? \

?????? .resource = TIMER_RESOURCE(_tmr_no, _irq),?????? \

struct platform_device s3c_device_timer[] = {

?????? [0] = { DEFINE_S3C_TIMER(0, IRQ_TIMER0) },

?????? [1] = { DEFINE_S3C_TIMER(1, IRQ_TIMER1) },

?????? [2] = { DEFINE_S3C_TIMER(2, IRQ_TIMER2) },

?????? [3] = { DEFINE_S3C_TIMER(3, IRQ_TIMER3) },

?????? [4] = { DEFINE_S3C_TIMER(4, IRQ_TIMER4) },

};

上面的代碼就是設(shè)備部分代碼,其實(shí)就是五個(gè)定時(shí)器的資源,我們把目光放在DEFINE_S3C_TIMER宏上,你會(huì)發(fā)現(xiàn)其設(shè)備名是"s3c24xx-pwm",而我們在pwm.c中定義的驅(qū)動(dòng)名也是"s3c24xx-pwm",這樣如果我們把設(shè)備注冊到內(nèi)核,那么設(shè)備"s3c24xx-pwm"和驅(qū)動(dòng)"s3c24xx-pwm"就會(huì)匹配成功。所以如果你用到定時(shí)器0,那么你只要在BSP中添加s3c_device_timer[0]就可以了。我們現(xiàn)在做的是蜂鳴器驅(qū)動(dòng),使用的是Timer0定時(shí)器,我們就在mini2440BSP文件mach-mini2440.c中添加如下代碼

static struct platform_device *mini2440_devices[] __initdata = {

?????? ……

?????? &s3c_device_timer[0],??? //添加

};

這樣我們就分析完pwm核心層的代碼了。

三.Backlight核心驅(qū)動(dòng)

下面我們講講backlight子系統(tǒng)。背光子系統(tǒng)目錄在/driver/video/backlight下,其中背光子系統(tǒng)核心代碼是backlight.c

先查看/driver/video/backlight/Makefile

obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o

繼續(xù)查看/driver/video/backlight/Kconfig

config BACKLIGHT_CLASS_DEVICE

??????? tristate "Lowlevel Backlight controls"

?????? depends on BACKLIGHT_LCD_SUPPORT

?????? default m

所以配置內(nèi)核make menuconfig時(shí),需要選中這一項(xiàng)。

?

下面看backlight背光的核心代碼backlight.c

static int __init backlight_class_init(void)

{

?????? backlight_class = class_create(THIS_MODULE, "backlight"); //注冊backlight

?????? if (IS_ERR(backlight_class)) {

????????????? printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n",

??????????????????????????? PTR_ERR(backlight_class));

????????????? return PTR_ERR(backlight_class);

?????? }

?????? backlight_class->dev_attrs = bl_device_attributes;? //添加類屬性

?????? backlight_class->suspend = backlight_suspend;

?????? backlight_class->resume = backlight_resume;

?????? return 0;

}

我們知道backlight背光子系統(tǒng)的主要就是靠這個(gè)類屬性,當(dāng)我們設(shè)置背光值就是向類屬性中某個(gè)成員寫背光值,這個(gè)類屬性就是給用戶的一種接口,我們重點(diǎn)看看

#define __ATTR(_name,_mode,_show,_store) { \

?????? .attr = {.name = __stringify(_name), .mode = _mode },???? \

?????? .show???? = _show,???????????????????????????? \

?????? .store????? = _store,???????????????????????????? \

}

static struct device_attribute bl_device_attributes[] = {

?????? __ATTR(bl_power, 0644, backlight_show_power, backlight_store_power),

?????? __ATTR(brightness, 0644, backlight_show_brightness,

????????????? ???? backlight_store_brightness),

?????? __ATTR(actual_brightness, 0444, backlight_show_actual_brightness,

????????????? ???? NULL),

?????? __ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),

?????? __ATTR_NULL,

};

很明顯,在backlight類中我們創(chuàng)建了bl_powerbrightnessactural_brightnessmax_brightness四個(gè)成員,其中brightness是當(dāng)前亮度,max_brightness是最大亮度。當(dāng)用戶層通過cat或者echo命令就會(huì)觸發(fā)這些成員。對于這些屬性的讀寫函數(shù),我們先看看讀的函數(shù)backlight_show_max_brightness

static ssize_t backlight_show_max_brightness(struct device *dev,

????????????? struct device_attribute *attr, char *buf)

{

?????? struct backlight_device *bd = to_backlight_device(dev);

?????? return sprintf(buf, "%d\n", bd->props.max_brightness);? //輸出最大亮度

}

這個(gè)函數(shù)很簡單,但是重點(diǎn)是引入了幾個(gè)backlight背光子系統(tǒng)的幾個(gè)重要的數(shù)據(jù)結(jié)構(gòu),我們好好學(xué)習(xí)下。

首先是backlight背光子系統(tǒng)的設(shè)備結(jié)構(gòu)體backlight_device

struct backlight_device {

?????? struct backlight_properties props;? //背光屬性

?????? struct mutex update_lock;

?????? struct mutex ops_lock;

?????? struct backlight_ops *ops;??? //背光操作函數(shù),類似于file_operations

?????? struct notifier_block fb_notif;

?????? struct device dev;? //內(nèi)嵌設(shè)備

};

下面先看看背光屬性結(jié)構(gòu)體backlight_properties

struct backlight_properties {

?????? int brightness;? //當(dāng)前背光值

?????? int max_brightness;? //最大背光值

?????? int power;

?????? int fb_blank;

?????? unsigned int state;

};

再看看背光操作函數(shù)結(jié)構(gòu)體

struct backlight_ops {

?????? unsigned int options;

#define BL_CORE_SUSPENDRESUME?????? (1 << 0)

?????? int (*update_status)(struct backlight_device *);?? //改變背光狀態(tài)

?????? int (*get_brightness)(struct backlight_device *);? //獲取背光值

?????? int (*check_fb)(struct fb_info *);

};

好了,我們繼續(xù)看backlight類屬性中寫的函數(shù),例如設(shè)置當(dāng)前背光值函數(shù)backlight_store_brightness

static ssize_t backlight_store_brightness(struct device *dev,

????????????? struct device_attribute *attr, const char *buf, size_t count)

{

?????? int rc;

?????? struct backlight_device *bd = to_backlight_device(dev);

?????? unsigned long brightness;

?????? rc = strict_strtoul(buf, 0, &brightness);

?????? if (rc)

?????? ?????? return rc;

?????? rc = -ENXIO;

?????? mutex_lock(&bd->ops_lock);

?????? if (bd->ops) {

????????????? if (brightness > bd->props.max_brightness)

???????????????????? rc = -EINVAL;

????????????? else {

???????????????????? pr_debug("backlight: set brightness to %lu\n",

??????????????????????????? ?brightness);

???????????????????? bd->props.brightness =brightness;? //傳入背光值

???????????????????? backlight_update_status(bd);? //調(diào)用backlight_update_status設(shè)備背光值

???????????????????? rc = count;

????????????? }

?????? }

?????? mutex_unlock(&bd->ops_lock);

?????? backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);

?????? return rc;

}

跟蹤backlight_update_status

static inline void backlight_update_status(struct backlight_device *bd)

{

?????? mutex_lock(&bd->update_lock);

?????? if (bd->ops && bd->ops->update_status)

????????????? bd->ops->update_status(bd); //調(diào)用背光操作函數(shù)中改變背光狀態(tài)函數(shù)update_status

?????? mutex_unlock(&bd->update_lock);

}

對于這個(gè)backlight背光核心層驅(qū)動(dòng)backlight.c,剩下的就是這個(gè)pwm.c給我們提供了哪些接口函數(shù)了。

struct backlight_device *backlight_device_register(const char *name,

????????????? struct device *parent, void *devdata, struct backlight_ops *ops)

void backlight_device_unregister(struct backlight_device *bd)

EXPORT_SYMBOL(backlight_device_register);? //注冊背光設(shè)備

EXPORT_SYMBOL(backlight_device_unregister); //注銷背光設(shè)備

這些接口很簡單,就不細(xì)說了,這樣我們的backlight子系統(tǒng)的核心層就介紹完了。

?

四.基于PWM&Backlight的蜂鳴器驅(qū)動(dòng)

下面我們結(jié)合上面的PWM核心層和Backlight背光子系統(tǒng)核心層,根據(jù)基于pwm的背光驅(qū)動(dòng)/driver/video/backlight/pwm_bl.c來修改成基于Mini2440的蜂鳴器驅(qū)動(dòng)。

先查看/driver/video/backlight/Makefile

obj-$(CONFIG_BACKLIGHT_PWM)?? += pwm_bl.o

繼續(xù)查看/driver/video/backlight/Kconfig

config BACKLIGHT_PWM

?????? tristate "Generic PWM based Backlight Driver"

?????? depends on BACKLIGHT_CLASS_DEVICE && HAVE_PWM

?????? help

?????? ? If you have a LCD backlight adjustable by PWM, say Y to enable

?????? ? this driver.

我們的HAVE_PWMBACKLIGHT_CLASS_DEVICE分別是在前面講pwm核心和backlight核心時(shí)已經(jīng)編譯了,所以配置內(nèi)核make menuconfig時(shí),需要再選中"Generic PWM based Backlight Driver"這項(xiàng)。

?

好了,我們先把我們的蜂鳴器移植進(jìn)去吧,首先我們知道蜂鳴器使用的是GPB0端口,該端口如果工作在TOU0模式,就可以通過設(shè)備定時(shí)器的TCNTTCMP來控制定時(shí)器的波形而來。先打開mini2440BSP文件mach-mini2440.c,如下添加

static struct platform_device s3c_backlight_device = {

?????? .name???????????? = "pwm-backlight",?????????? //設(shè)備名

?????? .dev??????? = {

????????????? .parent??? = &s3c_device_timer[0].dev,? //該設(shè)備基于pwm中的0號(hào)定時(shí)器

????????????? .platform_data = &s3c_backlight_data,

?????? },

?????? .id=0,???? //對應(yīng)的就是pwm0

};

添加平臺(tái)數(shù)據(jù)

static struct platform_pwm_backlight_data s3c_backlight_data = {

?????? .pwm_id???????? = 0,?? //對應(yīng)的就是Timer0

?????? .max_brightness???? = 1000, ?//最大亮度

?????? .dft_brightness?????? = 10 ,????? //當(dāng)前亮度

?????? .pwm_period_ns??? = 800000,? //這就是前面說的T0,即輸出時(shí)鐘周期

?????? .init???????? = s3c_bl_init,? //端口初始化

};

注意到平臺(tái)數(shù)據(jù)中定義了init函數(shù),由于在蜂鳴器的初始化時(shí),需要對GPB0設(shè)置為TOUT0模式,所以代碼如下編寫

static int s3c_bl_init(struct device *dev)

{

?????? s3c2410_gpio_pullup(S3C2410_GPB(0),0);?? // GPB0不上拉

?????? s3c2410_gpio_cfgpin(S3C2410_GPB(0),S3C2410_GPB0_TOUT0); // GPB0設(shè)置為TOUT0

?????? return 0;

}

然后把這個(gè)s3c_backlight_device加入到mini2440_devices數(shù)組

static struct platform_device *mini2440_devices[] __initdata = {

?????? ……

?????? &s3c_device_timer[0],

?????? &s3c_backlight_device, //添加

};

最后添加頭文件

#include <linux/pwm_backlight.h>

這樣配置完后,進(jìn)行make zImage生成zImage內(nèi)核鏡像。

?

好了,下面我們分析下基于pwm的背光驅(qū)動(dòng)/driver/video/backlight/pwm_bl.c

static struct platform_driver pwm_backlight_driver = {

?????? .driver??????????? = {

????????????? .name????? = "pwm-backlight", //驅(qū)動(dòng)名

????????????? .owner??? = THIS_MODULE,

?????? },

?????? .probe??????????? = pwm_backlight_probe, //探測函數(shù)

?????? .remove????????? = pwm_backlight_remove,

?????? .suspend? = pwm_backlight_suspend,

?????? .resume????????? = pwm_backlight_resume,

};

static int __init pwm_backlight_init(void)

{

?????? return platform_driver_register(&pwm_backlight_driver);

}

注意上面的pwm_backlight_driver中的驅(qū)動(dòng)名"pwm-backlight"和我們剛才移植時(shí)添加的設(shè)備名"pwm-backlight"是一致的,這樣設(shè)備和驅(qū)動(dòng)就能匹配成功。下面看探測函數(shù)

static int pwm_backlight_probe(struct platform_device *pdev)

{

?????? struct platform_pwm_backlight_data *data = pdev->dev.platform_data;

?????? struct backlight_device *bl;

?????? struct pwm_bl_data *pb;? //本驅(qū)動(dòng)的私有結(jié)構(gòu)體

?????? int ret;

?????? if (!data) {

????????????? dev_err(&pdev->dev, "failed to find platform data\n");

????????????? return -EINVAL;

?????? }

?????? if (data->init) {? //初始化端口,這個(gè)端口函數(shù)在BSP中定義

????????????? ret = data->init(&pdev->dev);

????????????? if (ret < 0)

???????????????????? return ret;

?????? }

?????? pb = kzalloc(sizeof(*pb), GFP_KERNEL); //分配pwm_bl_data空間

?????? if (!pb) {

????????????? dev_err(&pdev->dev, "no memory for state\n");

????????????? ret = -ENOMEM;

????????????? goto err_alloc;

?????? }

?????? pb->period = data->pwm_period_ns;?? //獲取周期

?????? pb->notify = data->notify;

?????? pb->pwm = pwm_request(data->pwm_id, "backlight"); //注冊pwm設(shè)備

?????? if (IS_ERR(pb->pwm)) {

????????????? dev_err(&pdev->dev, "unable to request PWM for backlight\n");

????????????? ret = PTR_ERR(pb->pwm);

????????????? goto err_pwm;

?????? } else

????????????? dev_dbg(&pdev->dev, "got pwm for backlight\n");

?????? bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev,

???????????????????? pb, &pwm_backlight_ops); //注冊backlight設(shè)備

?????? if (IS_ERR(bl)) {

????????????? dev_err(&pdev->dev, "failed to register backlight\n");

????????????? ret = PTR_ERR(bl);

????????????? goto err_bl;

?????? }

?????? bl->props.max_brightness = data->max_brightness;

?????? bl->props.brightness = data->dft_brightness;

?????? backlight_update_status(bl);? //先點(diǎn)亮背光

?????? platform_set_drvdata(pdev, bl); //設(shè)置bl為私有數(shù)據(jù)

?????? return 0;

err_bl:

?????? pwm_free(pb->pwm);

err_pwm:

?????? kfree(pb);

err_alloc:

?????? if (data->exit)

????????????? data->exit(&pdev->dev);

?????? return ret;

}

對于這個(gè)驅(qū)動(dòng),我們重點(diǎn)關(guān)注的是注冊backlight設(shè)備時(shí)傳入的參數(shù)pwm_backlight_ops,因?yàn)槲覀冎胺治?span style="font-family:'Times New Roman'">backlight背光子系統(tǒng)時(shí)說過,背光設(shè)備結(jié)構(gòu)體中有個(gè)操作背光的函數(shù)集合,在我們的pwm_bl.c中,就需要定義這個(gè)操作背光的函數(shù)集合,也就是pwm_backlight_ops

static struct backlight_ops pwm_backlight_ops = {

?????? .update_status = pwm_backlight_update_status, //更新背光亮度

?????? .get_brightness?????? = pwm_backlight_get_brightness, //獲取背光亮度

};

獲取背光亮度函數(shù)pwm_backlight_get_brightness很簡單,跟蹤得到

static int pwm_backlight_get_brightness(struct backlight_device *bl)

{

?????? return bl->props.brightness;

}

我們重點(diǎn)看更新背光亮度函數(shù)pwm_backlight_update_status

static int pwm_backlight_update_status(struct backlight_device *bl)

{

?????? struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);

?????? int brightness = bl->props.brightness;

?????? int max = bl->props.max_brightness;

?????? if (bl->props.power != FB_BLANK_UNBLANK)

????????????? brightness = 0;

?????? if (bl->props.fb_blank != FB_BLANK_UNBLANK)

????????????? brightness = 0;

?????? if (pb->notify)

????????????? brightness = pb->notify(brightness);

?????? if (brightness == 0) {? //背光值為0,關(guān)閉背光

????????????? pwm_config(pb->pwm, 0, pb->period);

????????????? pwm_disable(pb->pwm);

?????? } else {?? //調(diào)用pwm中的API設(shè)置背光

????????????? pwm_config(pb->pwm, brightness * pb->period / max, pb->period);

????????????? pwm_enable(pb->pwm);

?????? }

?????? return 0;

}

好了,這樣我們的pwm_bl.c也分析完了。在使用backlight子系統(tǒng)的時(shí)候,我們只需要在probe函數(shù)中注冊pwmbacklight設(shè)備,然后定義背光操作函數(shù)集合即可。

?

五.驅(qū)動(dòng)測試

實(shí)驗(yàn)環(huán)境:內(nèi)核linux2.6.32.2arm-linux-gcc交叉編譯器,mini2440開發(fā)板

下面我們進(jìn)行對上面的驅(qū)動(dòng)進(jìn)行測試,按照上面的步驟操作,將上文已經(jīng)編譯好的zImage燒入開發(fā)板,通過超級終端控制,能控制蜂鳴器的發(fā)出的聲音頻率。

?

?


與50位技術(shù)專家面對面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的Linux下的Backlight子系统的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。