Linux PWM framework(二)- 背光子系統【轉】


轉自:https://blog.csdn.net/weixin_41028621/article/details/103542751?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-3

了解backlight driver.
1.Backlight Framework


1.1.用戶空間

  背光設備文件對應於/sys/class/backlight/目錄下的文件。/sys/class/backlight是注冊的背光設備類型,而在/sys/class/backlight/目錄下的文件就是所注冊的背光設備。系統完成背光設備類型的注冊,代碼如下:

drivers/video/backlight/backlight.c:
662 static int __init backlight_class_init(void)
663 {
664 backlight_class = class_create(THIS_MODULE, "backlight"); //注冊背光設備類型;
670
671 backlight_class->dev_groups = bl_device_groups; //指定背光設備類型的屬性文件;
672 backlight_class->pm = &backlight_class_dev_pm_ops;
673 INIT_LIST_HEAD(&backlight_dev_list);
674 mutex_init(&backlight_dev_list_mutex);
675 BLOCKING_INIT_NOTIFIER_HEAD(&backlight_notifier);
676
677 return 0;
678 }

289 static struct attribute *bl_device_attrs[] = {
290 &dev_attr_bl_power.attr,
291 &dev_attr_brightness.attr,
292 &dev_attr_actual_brightness.attr,
293 &dev_attr_max_brightness.attr,
294 &dev_attr_type.attr,
295 NULL,
296 };
297 ATTRIBUTE_GROUPS(bl_device);


  backlight背光子系統的主要就是靠 bl_device_attrs這個類屬性,當設置背光值就是向類屬性中某個成員寫背光值,這個類屬性就是給用戶的一種接口。

  backlight創建bl_power,brightness,actural_brightness,max_brightness四個成員,其中brightness是當前亮度,max_brightness是最大亮度。當用戶層通過cat或者echo命令就會觸發這些成員。對於這些屬性的讀寫函數,以函數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); //輸出最大亮度
}

  這個函數很簡單,重點是引入了幾個backlight背光子系統的幾個重要的數據結構。

1.2.struct backlight_device

89 struct backlight_device {
90 /* Backlight properties */
91 struct backlight_properties props;
92
93 /* Serialise access to update_status method */
94 struct mutex update_lock;
95
96 /* This protects the 'ops' field. If 'ops' is NULL, the driver that
97 registered this device has been unloaded, and if class_get_devdata()
98 points to something in the body of that driver, it is also invalid. */
99 struct mutex ops_lock;
100 const struct backlight_ops *ops; //背光設備的相關操作函數
101
102 /* The framebuffer notifier block */
103 struct notifier_block fb_notif;
104
105 /* list entry of all registered backlight devices */
106 struct list_head entry;
107
108 struct device dev;
109
110 /* Multiple framebuffers may share one backlight device */
111 bool fb_bl_on[FB_MAX];
112
113 int use_count;
114 };

  其中backlight_properties和backlight_ops結構體定義如下:

67 /* This structure defines all the properties of a backlight */
68 struct backlight_properties {
70 int brightness;
72 int max_brightness;
75 int power;
78 int fb_blank;
80 enum backlight_type type;
82 unsigned int state;
87 };

52 struct backlight_ops {
53 unsigned int options;
58 int (*update_status)(struct backlight_device *); //更新背光設備亮度等屬性
61 int (*get_brightness)(struct backlight_device *); //獲取背光設備亮度
64 int (*check_fb)(struct backlight_device *, struct fb_info *);
65 };

  繼續看backlight類屬性中寫的函數,例如設置當前背光值函數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); //調用backlight_update_status設備背光值
rc = count;
}
}
mutex_unlock(&bd->ops_lock);
backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);
return rc;
}

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); //調用背光操作函數中改變背光狀態函數update_status
mutex_unlock(&bd->update_lock);
}

留個懸念: bd->ops->update_status(bd); 會調用哪個函數?

1.3.Backlight APIs:

Register/unregister backlight device
在/sys/class/backlight/目錄下注冊和移除具體的背光設備:
struct backlight_device *backlight_device_register(const char *name,struct device *dev, void *devdata, struct backlight_ops *ops);
void backlight_device_unregister(struct backlight_device *bd);
1
2
3
Get backlight device/Drop backlight reference
struct backlight_device *devm_of_find_backlight(struct device *dev);
static void devm_backlight_release(void *data);
1
2
Enable backlight/Disable backlight
static inline int backlight_enable(struct backlight_device *bd);
static inline int backlight_disable(struct backlight_device *bd)
1
2
2.PWM Backlight

2.1.結構體

struct platform_pwm_backlight_data:

10 struct platform_pwm_backlight_data {
11 int pwm_id;
12 unsigned int max_brightness;
13 unsigned int dft_brightness;
14 unsigned int lth_brightness;
15 unsigned int pwm_period_ns;
16 unsigned int *levels;
17 unsigned int post_pwm_on_delay;
18 unsigned int pwm_off_delay;
19 /* TODO remove once all users are switched to gpiod_* API */
20 int enable_gpio;
21 int (*init)(struct device *dev);
22 int (*notify)(struct device *dev, int brightness);
23 void (*notify_after)(struct device *dev, int brightness);
24 void (*exit)(struct device *dev);
25 int (*check_fb)(struct device *dev, struct fb_info *info);
26 };

struct pwm_bl_data:

28 struct pwm_bl_data {
29 struct pwm_device *pwm;
30 struct device *dev;
31 unsigned int period;
32 unsigned int lth_brightness;
33 unsigned int *levels;
34 bool enabled;
35 struct regulator *power_supply;
36 struct gpio_desc *enable_gpio;
37 unsigned int scale;
38 bool legacy;
39 unsigned int post_pwm_on_delay;
40 unsigned int pwm_off_delay;
41 int (*notify)(struct device *,
42 int brightness);
43 void (*notify_after)(struct device *,
44 int brightness);
45 int (*check_fb)(struct device *, struct fb_info *);
46 void (*exit)(struct device *);
47 };

2.2.sysfs

  在/sys/class/backlight/目錄下創建pwm backlight node,並注冊pwm_backlight_ops。

drivers/video/backlight/pwm_bl.c:
bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
&pwm_backlight_ops, &props);

static const struct backlight_ops pwm_backlight_ops = {
.update_status = pwm_backlight_update_status, //更新背光亮度
.get_brightness = pwm_backlight_get_brightness, //獲取背光亮度
};

  其中.update_status = pwm_backlight_update_status, 就是bd->ops->update_status(bd); 調用的函數。

2.3.驅動分析

drivers/video/backlight/pwm_bl.c
pwm_backlight_probe
pwm_backlight_parse_dt //解析 dts 中的 brightness-levels、default-brightness-level
//RK3288 還會在這里解析 enable-gpios ,但是 3399 沒有,3399 是在 probe 里面用 devm_gpiod_get_optional //獲取 enable-gpio 狀態的
devm_gpiod_get_optional //實際上就是封裝了 gpio_request_one
devm_gpio_request_one //申請背光使能 gpio
devm_pwm_get -> /drivers/pwm/core.c //獲得一個pwm
pwm_get ->
of_pwm_get ->
of_parse_phandle_with_args 解析上面dts中的pwms屬性.
of_node_to_pwmchip
pwm = pc->of_xlate //最終生成struct pwm_device類型.
pwm_request //申請pwm,防止其他驅動也會使用.
pwm_set_period //pb->pwm->period = data->pwm_period_ns
pwm_get_period //獲取period.
dev_set_name(&pdev->dev, "rk28_bl"); //name不能改,用戶空間會被用到:/sys/class/backlight/rk28_bl
backlight_device_register -> /drivers/video/baklight/backlight.c //注冊標准背光設備
device_register
backlight_register_fb ->
fb_register_client //callback 是 fb_notifier_callback
fb_register_client // 注冊內核通知鏈
backlight_update_status -> //用默認值更新.
bd->ops->update_status ->
pwm_backlight_update_status -> //更新背光亮度
compute_duty_cycle //計算占空比
pwm_config //配置pwm
pwm_backlight_power_on //enable背光
platform_set_drvdata //可以將 pdev 保存成平台總線設備的私有數據,以后再要使用它時只需調用 platform_get_drvdata

2.3.1.backlight_device_register

backlight_register_fb
指定背光通知回調函數fb_notifier_callback,並注冊到通知鏈里。
Linux 內核中每個模塊之間都是獨立的,如果模塊需要感知其他模塊的事件,就需要用到內核通知鏈。最典型的通知鏈應用就是 LCD 和 TP 之間,TP 需要根據 LCD 的亮滅來控制是否打開關閉觸摸功能。通俗的講,LCD 會創建一個函數鏈表,TP 會將 suspend 和 resume 函數添加到鏈表中,當 LCD 發生亮滅變化時,會根據情況執行鏈表上所有對應的函數,函數會根據不同的動作執行 TP 的 suspend 和 resume 函數。

1>.TP 驅動背光通知回調函數

  probe 函數里指定背光通知回調函數gtp_fb_notifier_callback,並注冊到通知鏈里。gtp_fb_notifier_callback函數根據收到的通知event信息調用TS resume 或suspend 函數。

ts->notifier.notifier_call = gtp_fb_notifier_callback;
fb_register_client(&ts->notifier);

/* frame buffer notifier block control the suspend/resume procedure */
static int gtp_fb_notifier_callback(struct notifier_block *noti, unsigned long event, void *data)
{
struct fb_event *ev_data = data;
struct goodix_ts_data *ts = container_of(noti, struct goodix_ts_data, notifier);
int *blank;

if (ev_data && ev_data->data && event == FB_EVENT_BLANK && ts) {
blank = ev_data->data;
if (*blank == FB_BLANK_UNBLANK) {
GTP_DEBUG("Resume by fb notifier.");
goodix_ts_resume(ts);

}
else if (*blank == FB_BLANK_POWERDOWN) {
GTP_DEBUG("Suspend by fb notifier.");
goodix_ts_suspend(ts);
}
}

return 0;
}

2.3.2.pwm_backlight_update_status

static int pwm_backlight_update_status(struct backlight_device *bl)
{
struct pwm_bl_data *pb = bl_get_data(bl);
int brightness = bl->props.brightness;
int duty_cycle;

if (bl->props.power != FB_BLANK_UNBLANK ||
bl->props.fb_blank != FB_BLANK_UNBLANK ||
bl->props.state & BL_CORE_FBBLANK)
brightness = 0;

if (pb->notify)
brightness = pb->notify(pb->dev, brightness);

if (brightness > 0) {
duty_cycle = compute_duty_cycle(pb, brightness);
pwm_config(pb->pwm, duty_cycle, pb->period);
pwm_backlight_power_on(pb, brightness);
} else
pwm_backlight_power_off(pb);

if (pb->notify_after)
pb->notify_after(pb->dev, brightness);

return 0;
}

/include/uapi/linux/fb.h:
enum {
/* screen: unblanked, hsync: on, vsync: on */
FB_BLANK_UNBLANK = VESA_NO_BLANKING,

/* screen: blanked, hsync: on, vsync: on */
FB_BLANK_NORMAL = VESA_NO_BLANKING + 1,

/* screen: blanked, hsync: on, vsync: off */
FB_BLANK_VSYNC_SUSPEND = VESA_VSYNC_SUSPEND + 1,

/* screen: blanked, hsync: off, vsync: on */
FB_BLANK_HSYNC_SUSPEND = VESA_HSYNC_SUSPEND + 1,

/* screen: blanked, hsync: off, vsync: off */
FB_BLANK_POWERDOWN = VESA_POWERDOWN + 1
};

2.3.3.計算占空比

static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)
{
/*一般情況下這個值都為0*/
unsigned int lth = pb->lth_brightness;
/*占空比*/
int duty_cycle;
/*pb->levels這個表格就是從dts節點brightness-levels中獲取的,
假設進來的參數brightness是254,那么得到的duty_cycle就是1,
如果沒有這個表格,那么就直接是進來的亮度值.*/
if (pb->levels)
duty_cycle = pb->levels[brightness];
else
duty_cycle = brightness;

/*假設這里lth是0,那么公式就是duty_cycle * pb->period / pb->scale
pb->period也就是dts節點 pwms 的第三個參數周期值為 25000
pb->scale為pb->levels數組中的最大值
所以這個公式就是按照將Android的純數值轉換成事件周期值對應的占空比.*/
return (duty_cycle * (pb->period - lth) / pb->scale) + lth;
}

2.3.4.更新背光

pwm_backlight_probe 函數,解析dts,調用backlight_update_status來改變背光;
sysfs - brightness_store(drivers/video/backlight/backlight.c),設置brightness,然后調用backlight_update_status來改變背光;
backlight_suspend/backlight_resume;
backlight_register_fb(new_bd); ->fb_notifier_callback 調用backlight_update_status來改變背光; (drivers/video/backlight/backlight.c)
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
...
/*只處理亮屏和滅屏事件.*/
/* If we aren't interested in this event, skip it immediately ... */
if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK)
return 0;
...
if (bd->ops)
if (!bd->ops->check_fb ||
bd->ops->check_fb(bd, evdata->info)) {
bd->props.fb_blank = *(int *)evdata->data;
//亮屏情況
if (bd->props.fb_blank == FB_BLANK_UNBLANK)
bd->props.state &= ~BL_CORE_FBBLANK;
//滅屏時
else
bd->props.state |= BL_CORE_FBBLANK;
backlight_update_status(bd);
}
...
}

2.4.Dtsi settings

backlight: backlight {
status = "disabled";
compatible = "pwm-backlight";
pwms = <&pwm0 0 25000 0>;
pwm-names = <backlight>;

/*開機初始化默認等級,Android起來之后會改變它.*/
default-brightness-level = <50>;
enable-gpios = <&gpio 71 GPIO_ACTIVE_HIGH>;

/*背光可調等級,比如這里是255級,實際反應到占空比就是當前值和數組中最大值的比值,
例如當前是200,那么最終duty cycle就是200/255.*/
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 101 102 103
104 105 106 107 108 109 110 111
112 113 114 115 116 117 118 119
120 121 122 123 124 125 126 127
128 129 130 131 132 133 134 135
136 137 138 139 140 141 142 143
144 145 146 147 148 149 150 151
152 153 154 155 156 157 158 159
160 161 162 163 164 165 166 167
168 169 170 171 172 173 174 175
176 177 178 179 180 181 182 183
184 185 186 187 188 189 190 191
192 193 194 195 196 197 198 199
200 201 202 203 204 205 206 207
208 209 210 211 212 213 214 215
216 217 218 219 220 221 222 223
224 225 226 227 228 229 230 231
232 233 234 235 236 237 238 239
240 241 242 243 244 245 246 247
248 249 250 251 252 253 254 255>;
};

說明:

pwms = <&pwm0 0 25000 0>;

第一個參數 表示此背光接在 pwm0 上;
第二個參數 表示 index 為 0,pwm0 下只有 1個 pwm,所以填 0
第三個參數 表示周期為 25000ns,即頻率 為 40k
第四個參數 表示極性,0 正極性,1 負極性
正極性 0 表示 背光為正極 0~255 ,占空比從 0~100% 變化
負極性 1 表示 背光為負極 255~0 ,占空比從 100~0% 變化
default-brightness-level = <50>;
表示默認的背光,它存在於開機時候背光初始化到安卓。設置下來新的背光這段時間,default-brightness-level = < 50 > 表示為第 50 個元素的背光亮度。

enable-gpios = <&gpio 71 GPIO_ACTIVE_HIGH>;
enable-gpios;表示背光使能腳,這個根據電路原理圖配置即可;有的硬件沒有這個背光使能腳,那么將這個配置刪除,背光驅動通過配置 brightness-levels 數組的第 0 個元素將顯示調黑。

2.4.1.enable-gpios 代碼解析

devm_gpiod_get_optional(&pdev->dev, "enable",GPIOD_ASIS);
-> of_find_gpio(dev, con_id, idx, &lookupflags);
->of_get_named_gpiod_flags
->of_find_gpiochip_by_xlate
->chip->of_xlate //gpio 驅動optional 實現,如果驅動沒有實現,則調用系統實現的of_gpio_simple_xlate
->of_xlate_and_get_gpiod_flags
->gpiochip_get_desc //獲取gpio desc
1
2
3
4
5
6
7
3.代碼

drivers/video/backlight/backlight.c
drivers/video/backlight/pwm_bl.c
drivers/pwm/sysfs.c
3.1.CONFIG

CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_BACKLIGH_PWM=y
3.2.Debug

/sys/class/backlight (drivers/pwm/sysfs.c)
/sys/device/platform/backlight (drivers/video/backlight/backlight.c)
/sys/devices/platform/backlight/backlight/backlight

actual_brightness:這個節點只讀,可以通過讀取這個節點,獲取LCD實際的亮度值。
brightness:這個節點可讀可寫,向這個節點寫入不同值,可調節LCD亮度。
max_brightness:這個節點只讀,通過讀取此節點,獲取可以設置的最大亮度級別。
Note:調節brightness 改變占空比。

3.3.shell腳本調節背光:

#!/system/bin/sh or #!/bin/sh
i=0
while [ $i -le 255 ]
do
echo $i
echo $i > /sys/class/backlight/backlight/brightness
i=$((i+1))
usleep 100000
done
1
2
3
4
5
6
7
8
9
refer to

https://www.dazhuanlan.com/2019/10/25/5db29684e6460/
https://blog.csdn.net/kris_fei/article/details/52485635
https://developer.ridgerun.com/wiki/index.php?title=Linux_PWM_Pulse_Width_Modulator
————————————————
版權聲明:本文為CSDN博主「Hacker_Albert」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_41028621/article/details/103542751


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM