Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> MTK6735(Android6.0)-按鍵燈的實現

MTK6735(Android6.0)-按鍵燈的實現

編輯:關於Android編程

一、按鍵燈的簡介
最近調試一下按鍵燈,今天抽空順便把的流程分析了一下。按鍵燈也是一種led,它的使用規則如命名一樣,當按鍵按下亮燈,如果一定時間不操作的話,一會會滅燈。其實這裡的按鍵燈亮滅策略通常不是驅動來完成的,而是有用戶空間來控制。正如一句老話“驅動注重的不是策略,而是機制”;所以我們在編寫驅動只需要完成led的點亮和熄滅功能即可。當然在實際使用中我們會發現不是所有驅動都如此,有時候平台中也會添加一定的策略,這個本章不作分析。 調試按鍵燈,
當然首先從硬件開始。按鍵燈的原理圖如下:
\
\

從原理圖中我們發現,button-backlight是由兩路ISINK控制的,ISNIK是一種類似於PWM的控制器,它可以發出類似PWM的信號,可以通過寄存器的配置調整其占空比等參數,進入調節輸出電流,從而控制led的亮度。

二、按鍵燈的驅動實現
1. 設備和驅動的匹配
//驅動文件中定義platform_driver
file:kernel-3.18/drivers/misc/mediatek/leds/leds_drv.c
#define USE_PINCTRL
#ifdef USE_PINCTRL
static const struct of_device_id leds_of_ids[] = {
	{.compatible = "mediatek,leds-mt65xx",},
	{}
};
#endif
static struct platform_driver mt65xx_leds_driver = {
	.driver = {
		   .name = "leds-mt65xx",
		   .owner = THIS_MODULE,
#ifdef USE_PINCTRL
	.of_match_table = leds_of_ids,  //和dts中定義一致
#endif
		   },
	.probe = mt65xx_leds_probe,
	.remove = mt65xx_leds_remove,
	/* .suspend      = mt65xx_leds_suspend, */
	.shutdown = mt65xx_leds_shutdown,


};
//驅動模塊的加載
static int __init mt65xx_leds_init(void)
{
	ret = platform_driver_register(&mt65xx_leds_driver);


	....
	return ret;
}
//dts中定義leds 的相關節點如:red,green,blue,keyboard-backlight,button-backlight等(後面會用到)
file:kernel-3.18/arch/arm/boot/dts/rlk6737m_35g_c_m0.dts
led0:led@0 {
	compatible = "mediatek,red";
	led_mode = <0>;
	data = <1>;
	pwm_config = <0 0 0 0 0>;
};
led1:led@1 {
	compatible = "mediatek,green";
	led_mode = <0>;
	data = <1>;
	pwm_config = <0 0 0 0 0>;
};
led2:led@2 {
	compatible = "mediatek,blue";
	led_mode = <0>;
	data = <1>;
	pwm_config = <0 0 0 0 0>;
};
led4:led@4 {
	compatible = "mediatek,keyboard-backlight";
	led_mode = <0>;
	data = <1>;
	pwm_config = <0 0 0 0 0>;
};
led5:led@5 {
	compatible = "mediatek,button-backlight"; //這裡著重分析按鍵燈button-backlight
	led_mode = <3>;
	data = <1>;
	pwm_config = <0 0 0 0 0>;
};
led6:led@6 {
	compatible = "mediatek,lcd-backlight";
	led_mode = <5>;
	data = <1>;
	pwm_config = <0 0 0 0 0>;
};
//dts中定義和platform_device相關的節點信息
file:kernel-3.18/arch/arm/boot/dts/mt6735m.dtsi
lightsystem: leds {
	compatible = "mediatek,leds-mt65xx"; //這裡的定義和上面platform_driver中定義的一致
};
上述dts中定義按鍵燈leds節點配置,內核起來後會解析dts生成相關的設備,並與驅動中的driver匹配,如果匹配成功就執行下面的probe
2.leds probe 函數實現
file:kernel-3.18/drivers/misc/mediatek/leds/leds_drv.c
static int mt65xx_leds_probe(struct platform_device *pdev)
{
	int i;
	int ret;/* rc; */
	//進入probe後,會從dts中獲取led節點的mode和data
	struct cust_mt65xx_led *cust_led_list = mt_get_cust_led_list();


	LEDS_DRV_DEBUG("%s\n", __func__);
	get_div_array();
	//MT65XX_LED_TYPE_TOTAL為改通用(common)驅動所支持燈的個數
	for (i = 0; i < MT65XX_LED_TYPE_TOTAL; i++) { 
	//觀察上面dts中mode如果mode為0(MT65XX_LED_MODE_NONE),則遍歷下個元素
		if (cust_led_list[i].mode == MT65XX_LED_MODE_NONE) {  
			g_leds_data[i] = NULL;
			continue;
		}
		...
		//將dts中配置的mode,name ,data 保存起來後面會用到
		//通過觀察上面的button-backlight 的配置,得出其mode為3,data為1,name為button-backlight
		g_leds_data[i]->cust.mode = cust_led_list[i].mode; 
		g_leds_data[i]->cust.data = cust_led_list[i].data;
		g_leds_data[i]->cust.name = cust_led_list[i].name;


		g_leds_data[i]->cdev.name = cust_led_list[i].name;
		g_leds_data[i]->cust.config_data = cust_led_list[i].config_data;	/* bei add */


		g_leds_data[i]->cdev.brightness_set = mt65xx_led_set; //設置led亮度的函數
		
		//創建sys目錄下的brightness等屬性節點,提供給用戶空間調用
		ret = led_classdev_register(&pdev->dev, &g_leds_data[i]->cdev);
		...
	return 0;
		...
}
//後面會用到的一些結構的定義
file:kernel-3.18/drivers/misc/mediatek/leds/mt6735/leds_sw.h
enum mt65xx_led_type {
	MT65XX_LED_TYPE_RED = 0,
	MT65XX_LED_TYPE_GREEN,
	MT65XX_LED_TYPE_BLUE,
	MT65XX_LED_TYPE_JOGBALL,
	MT65XX_LED_TYPE_KEYBOARD,
	MT65XX_LED_TYPE_BUTTON,  
	MT65XX_LED_TYPE_LCD,
	MT65XX_LED_TYPE_TOTAL,
};
/**
 * led customization data structure
 * name : must the same as lights HAL
 * mode : control mode
 * data :
 *    PWM:  pwm number
 *    GPIO: gpio id
 *    PMIC: enum mt65xx_led_pmic
 *    CUST: custom set brightness function pointer
 * config_data: pwm config data
 */
struct cust_mt65xx_led {
	char *name;
	enum mt65xx_led_mode mode;
	long data;
	struct PWM_config config_data;
};
/**
 * led device node structure with mtk extentions
 * cdev: common led device structure
 * cust: customization data from device tree
 * work: workqueue for specialfied led device
 * level: brightness level
 * delay_on: on time if led is blinking
 * delay_off: off time if led is blinking
 */
struct mt65xx_led_data {
	struct led_classdev cdev;
	struct cust_mt65xx_led cust;
	struct work_struct work;
	int level;
	int delay_on;
	int delay_off;
};
file:kernel-3.18/include/linux/leds.h
enum led_brightness {
	LED_OFF		= 0,
	LED_HALF	= 127,
	LED_FULL	= 255,
};
struct led_classdev {
	const char		*name;
	enum led_brightness	 brightness;
	enum led_brightness	 max_brightness;
	int			 flags;
	...
	/* Set LED brightness level */
	/* Must not sleep, use a workqueue if needed */
	void		(*brightness_set)(struct led_classdev *led_cdev,
					  enum led_brightness brightness);
	/* Get LED brightness level */
	enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);


	.....


	struct device		*dev;
	const struct attribute_group	**groups;


	struct list_head	 node;			/* LED Device list */
	...
};
file:kernel-3.18/drivers/misc/mediatek/leds/mt6735/leds.c
char *leds_name[MT65XX_LED_TYPE_TOTAL] = {
	"red",
	"green",
	"blue",
	"jogball-backlight",
	"keyboard-backlight",
	"button-backlight",
	"lcd-backlight",
}
3.從dts中獲取各種led的配置信息
struct cust_mt65xx_led *mt_get_cust_led_list(void)
{
	struct cust_mt65xx_led *cust_led_list = get_cust_led_dtsi();
	return cust_led_list;
}
struct cust_mt65xx_led *get_cust_led_dtsi(void)
{
	struct device_node *led_node = NULL;
	...
		//MT65XX_LED_TYPE_TOTAL 為led數組長度,即可以支持led的個數
		for (i = 0; i < MT65XX_LED_TYPE_TOTAL; i++) {


			char node_name[32] = "mediatek,";


			pled_dtsi[i].name = leds_name[i];
			
			//使用"mediatek,button-backlight"尋找dtsi中定義的節點
			led_node =
			    of_find_compatible_node(NULL, NULL,
						    strcat(node_name,
							   leds_name[i])); 
			if (!led_node) {
				LEDS_DEBUG("Cannot find LED node from dts\n");
				pled_dtsi[i].mode = 0;
				pled_dtsi[i].data = -1;
			} else {
				isSupportDTS = true;
				//讀取led_mode值
				ret =
				    of_property_read_u32(led_node, "led_mode",  
							 &mode);
				if (!ret) {
					pled_dtsi[i].mode = mode;
					LEDS_DEBUG
					    ("The %s's led mode is : %d\n",
					     pled_dtsi[i].name,
					     pled_dtsi[i].mode);
				}
				//讀取led的data值
				ret =
				    of_property_read_u32(led_node, "data",
							 &data);
				if (!ret) {
					pled_dtsi[i].data = data;
					LEDS_DEBUG
					    ("The %s's led data is : %ld\n",
					     pled_dtsi[i].name,
					     pled_dtsi[i].data);
				} 
				...
	return pled_dtsi;
}
4. 創建相關的設備節點
**
 * led_classdev_register - register a new object of led_classdev class.
 * @parent: The device to register.
 * @led_cdev: the led_classdev structure for this device.
 */
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
	led_cdev->dev = device_create_with_groups(leds_class, parent, 0,
					led_cdev, led_cdev->groups,
					"%s", led_cdev->name);
	...


	return 0;
}
//device_create_with_groups的實現
file:kernel-3.18/drivers/base/core.c
/**
 * device_create_with_groups - creates a device and registers it with sysfs
 * @class: pointer to the struct class that this device should be registered to
 * @parent: pointer to the parent struct device of this new device, if any
 * @devt: the dev_t for the char device to be added
 * @drvdata: the data to be added to the device for callbacks
 * @groups: NULL-terminated list of attribute groups to be created
 * @fmt: string for the device's name
 *
 * This function can be used by char device classes.  A struct device
 * will be created in sysfs, registered to the specified class.
 * Additional attributes specified in the groups parameter will also
 * be created automatically.
 *
 * A "dev" file will be created, showing the dev_t for the device, if
 * the dev_t is not 0,0.
 * If a pointer to a parent struct device is passed in, the newly created
 * struct device will be a child of that device in sysfs.
 * The pointer to the struct device will be returned from the call.
 * Any further sysfs files that might be required can be created using this
 * pointer.
 *
 * Returns &struct device pointer on success, or ERR_PTR() on error.
 *
 * Note: the struct class passed to this function must have previously
 * been created with a call to class_create().
 */
struct device *device_create_with_groups(struct class *class,
					 struct device *parent, dev_t devt,
					 void *drvdata,
					 const struct attribute_group **groups,
					 const char *fmt, ...)
{
	va_list vargs;
	struct device *dev;


	va_start(vargs, fmt);
	dev = device_create_groups_vargs(class, parent, devt, drvdata, groups,
					 fmt, vargs);
	va_end(vargs);
	return dev;
}
//device_create_groups_vargs 的實現
static struct device *
device_create_groups_vargs(struct class *class, struct device *parent,
			   dev_t devt, void *drvdata,
			   const struct attribute_group **groups,
			   const char *fmt, va_list args)
{
	struct device *dev = NULL;
	int retval = -ENODEV;


	if (class == NULL || IS_ERR(class))
		goto error;


	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
		retval = -ENOMEM;
		goto error;
	}


	device_initialize(dev);
	dev->devt = devt;
	dev->class = class;
	dev->parent = parent;
	dev->groups = groups;
	dev->release = device_create_release;
	dev_set_drvdata(dev, drvdata);


	retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
	if (retval)
		goto error;


	retval = device_add(dev);
	if (retval)
		goto error;


	return dev;


error:
	put_device(dev);
	return ERR_PTR(retval);
}
//device_add的實現
int device_add(struct device *dev)
{
	...
	/* first, register with generic layer. */
	/* we require the name to be set before, and pass NULL */
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
	if (error)
		goto Error;


	...
	error = device_add_attrs(dev);
	...
}
device_add_attrs的實現這裡將會調用device_add_groups,class->dev_groups 作為參數呗傳入,此時節點/sys/class/leds/xxx/brightness已經被
創建 這裡的xxx 對應驅動中的red,green,button_backlight,
lcd-backlight ... 已經創建
static int device_add_attrs(struct device *dev)
{
	struct class *class = dev->class;
	const struct device_type *type = dev->type;
	int error;
	
	 //這裡class->dev_groups先前已經在led-class.c的leds_init中被賦值 leds_class->dev_groups = led_groups;
	if (class) {
		error = device_add_groups(dev, class->dev_groups);
		if (error)
			return error;
	}
	...
	return 0;
}
5、屬性節點的讀寫方法定義
file:kernel-3.18/drivers/leds/led-class.c
static int __init leds_init(void)
{
	leds_class = class_create(THIS_MODULE, "leds"); //創建class對象
	...
	leds_class->dev_groups = led_groups; //傳入brightness節點參數,led屬性節點組賦值給leds_class
	...
	return 0;
}
再看led_groups的定義如下:
static const struct attribute_group *led_groups[] = {
	&led_group,
#ifdef CONFIG_LEDS_TRIGGERS
	&led_trigger_group,
#endif
	NULL,
};
static const struct attribute_group led_group = {
	.attrs = led_class_attrs,
};
static struct attribute *led_class_attrs[] = {
	&dev_attr_brightness.attr,
	&dev_attr_max_brightness.attr,
	NULL,
};
當用戶空間讀取屬性節點時候,會直接輸入當前亮度值
static ssize_t brightness_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct led_classdev *led_cdev = dev_get_drvdata(dev);


	/* no lock needed for this */
	led_update_brightness(led_cdev);


	return sprintf(buf, "%u\n", led_cdev->brightness);
}
static ssize_t brightness_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
	unsigned long state;
	ssize_t ret = -EINVAL;


	ret = kstrtoul(buf, 10, &state); //將傳入的字符串轉換為十進制
	if (ret)
		return ret;


	if (state == LED_OFF)
		led_trigger_remove(led_cdev);
	__led_set_brightness(led_cdev, state); //設置燈的亮度(亮滅)


	return size;
}
定義brightness屬性的變量
static DEVICE_ATTR_RW(brightness);
6.button-backlight 亮燈的實現
通過上面節點的 /sys/class/leds/button-backlight/brightness 寫方法brightness_store的定義可知,當brightness節點被用戶空間寫入後,將觸發執行__led_set_brightness,我們可以通過用戶空間傳入的參數調節燈的亮度,這裡這裡的傳入參數范圍0~255這裡的__led_set_brightness如下定義:
file:kernel-3.18/drivers/leds/leds.h
static inline void __led_set_brightness(struct led_classdev *led_cdev,
					enum led_brightness value)
{
	if (value > led_cdev->max_brightness)
		value = led_cdev->max_brightness; //對傳入的值作越界處理
	led_cdev->brightness = value;
	if (!(led_cdev->flags & LED_SUSPENDED))
		led_cdev->brightness_set(led_cdev, value);//執行亮燈操作
}
這個函數最終會調用led_cdev->brightness_set,而 led_cdev->brightness_set在leds_drv.c 中已經被賦值過如下:
file:kernel-3.18/drivers/misc/mediatek/leds/leds_drv.c
g_leds_data[i]->cdev.brightness_set = mt65xx_led_set;
static void mt65xx_led_set(struct led_classdev *led_cdev,
			   enum led_brightness level)
{
	struct mt65xx_led_data *led_data =
	    container_of(led_cdev, struct mt65xx_led_data, cdev);
	...
	mt_mt65xx_led_set(led_cdev, level);
	...
}
file:kernel-3.18/drivers/misc/mediatek/leds/mt6735/leds.c
void mt_mt65xx_led_set(struct led_classdev *led_cdev, enum led_brightness level)
{
	struct mt65xx_led_data *led_data =
	    container_of(led_cdev, struct mt65xx_led_data, cdev);
	/* unsigned long flags; */
	/* spin_lock_irqsave(&leds_lock, flags); */
	...

	/* do something only when level is changed */
	if (led_data->level != level) {
		led_data->level = level;
		if (strcmp(led_data->cust.name, "lcd-backlight") != 0) {
			LEDS_DEBUG("Set NLED directly %d at time %lu\n",
				   led_data->level, jiffies);
			schedule_work(&led_data->work);
		} else {
			if (level != 0
			    && level * CONFIG_LIGHTNESS_MAPPING_VALUE < 255) {
				level = 1;
			} else {
				level =
				    (level * CONFIG_LIGHTNESS_MAPPING_VALUE) /
				    255;
			}
			LEDS_DEBUG
			    ("Set Backlight directly %d at time %lu, mapping level is %d\n",
			     led_data->level, jiffies, level);
			if (MT65XX_LED_MODE_CUST_BLS_PWM == led_data->cust.mode) {
				mt_mt65xx_led_set_cust(&led_data->cust,
						       ((((1 <<
							   MT_LED_INTERNAL_LEVEL_BIT_CNT)
							  - 1) * level +
							 127) / 255));
			} else {
			//最終調用mt_mt65xx_led_set_cust
				mt_mt65xx_led_set_cust(&led_data->cust, level); 
			}
		}
	}
}
mt_mt65xx_led_set_cust的實現
file:kernel-3.18/drivers/misc/mediatek/leds/mt6735/leds.c
int mt_mt65xx_led_set_cust(struct cust_mt65xx_led *cust, int level)
{
	struct nled_setting led_tmp_setting = { 0, 0, 0 };
	int tmp_level = level;
	static bool button_flag;
	unsigned int BacklightLevelSupport =
	    Cust_GetBacklightLevelSupport_byPWM();


	switch (cust->mode) {


	case MT65XX_LED_MODE_PWM:
		if (strcmp(cust->name, "lcd-backlight") == 0) {
			bl_brightness_hal = level;
			if (level == 0) {
				mt_pwm_disable(cust->data,
					       cust->config_data.pmic_pad);


			} else {


				if (BacklightLevelSupport ==
				    BACKLIGHT_LEVEL_PWM_256_SUPPORT)
					level = brightness_mapping(tmp_level);
				else
					level = brightness_mapto64(tmp_level);
				mt_backlight_set_pwm(cust->data, level,
						     bl_div_hal,
						     &cust->config_data);
			}
			bl_duty_hal = level;


		} else {
			if (level == 0) {
				led_tmp_setting.nled_mode = NLED_OFF;
				mt_led_set_pwm(cust->data, &led_tmp_setting);
				mt_pwm_disable(cust->data,
					       cust->config_data.pmic_pad);
			} else {
				led_tmp_setting.nled_mode = NLED_ON;
				mt_led_set_pwm(cust->data, &led_tmp_setting);
			}
		}
		return 1;


	case MT65XX_LED_MODE_GPIO:
		LEDS_DEBUG("brightness_set_cust:go GPIO mode!!!!!\n");
		return ((cust_set_brightness) (cust->data)) (level);
   //這裡的MT65XX_LED_MODE_PMIC對應button-backlight的配置的mode=3 
	case MT65XX_LED_MODE_PMIC:  
		/* for button baclight used SINK channel, when set button ISINK,
			don't do disable other ISINK channel */
		//使用button-backlight的調用如下:
		if ((strcmp(cust->name, "button-backlight") == 0)) { 
			if (button_flag == false) {
				switch (cust->data) {
				case MT65XX_LED_PMIC_NLED_ISINK0:
					button_flag_isink0 = 1;
					break;
				case MT65XX_LED_PMIC_NLED_ISINK1:
					button_flag_isink1 = 1;
					break;
				case MT65XX_LED_PMIC_NLED_ISINK2:
					button_flag_isink2 = 1;
					break;
				case MT65XX_LED_PMIC_NLED_ISINK3:
					button_flag_isink3 = 1;
					break;
				default:
					break;
				}
				button_flag = true;
			}
		}
		return mt_brightness_set_pmic(cust->data, level, bl_div_hal);


	case MT65XX_LED_MODE_CUST_LCM:
		if (strcmp(cust->name, "lcd-backlight") == 0)
			bl_brightness_hal = level;
		LEDS_DEBUG("brightness_set_cust:backlight control by LCM\n");
		/* warning for this API revork */
		return ((cust_brightness_set) (cust->data)) (level, bl_div_hal);


	case MT65XX_LED_MODE_CUST_BLS_PWM:
		if (strcmp(cust->name, "lcd-backlight") == 0)
			bl_brightness_hal = level;
		return ((cust_set_brightness) (cust->data)) (level);


	case MT65XX_LED_MODE_NONE:
	default:
		break;
	}
	return -1;
}
也就是說當用戶對屬性節點 /sys/class/leds/button-backlight/brightness寫入時最終調用mt_brightness_set_pmic函數,
mt_brightness_set_pmic的實現如下:
file:kernel-3.18/drivers/misc/mediatek/leds/mt6735/leds.c
int mt_brightness_set_pmic(enum mt65xx_led_pmic pmic_type, u32 level, u32 div)
{
	static bool first_time = true;


	LEDS_DEBUG("PMIC#%d:%d\n", pmic_type, level);
	mutex_lock(&leds_pmic_mutex);
	if (pmic_type == MT65XX_LED_PMIC_NLED_ISINK0) {
		if ((button_flag_isink0 == 0) && (first_time == true)) {	/* button
			flag ==0, means this ISINK is not for button backlight */
			if (button_flag_isink1 == 0)
				pmic_set_register_value(PMIC_ISINK_CH1_EN, NLED_OFF);	/* sw
			workround for sync leds status */
			if (button_flag_isink2 == 0)
				pmic_set_register_value(PMIC_ISINK_CH2_EN,
							NLED_OFF);
			if (button_flag_isink3 == 0)
				pmic_set_register_value(PMIC_ISINK_CH3_EN,
							NLED_OFF);
			first_time = false;
		}
		pmic_set_register_value(PMIC_RG_DRV_32K_CK_PDN, 0x0);	/* Disable power down */
		pmic_set_register_value(PMIC_RG_DRV_ISINK0_CK_PDN, 0);
		pmic_set_register_value(PMIC_RG_DRV_ISINK0_CK_CKSEL, 0);
		pmic_set_register_value(PMIC_ISINK_CH0_MODE, ISINK_PWM_MODE);
		pmic_set_register_value(PMIC_ISINK_CH0_STEP, ISINK_3);	/* 16mA */
		pmic_set_register_value(PMIC_ISINK_DIM0_DUTY, 15);
		pmic_set_register_value(PMIC_ISINK_DIM0_FSEL, ISINK_1KHZ);	/* 1KHz */
		if (level)
			pmic_set_register_value(PMIC_ISINK_CH0_EN, NLED_ON);
		else
			pmic_set_register_value(PMIC_ISINK_CH0_EN, NLED_OFF);
		mutex_unlock(&leds_pmic_mutex);
		return 0;
	} else if (pmic_type == MT65XX_LED_PMIC_NLED_ISINK1) {
		if ((button_flag_isink1 == 0) && (first_time == true)) {	/* button
			flag ==0, means this ISINK is not for button backlight */
			if (button_flag_isink0 == 0)
				pmic_set_register_value(PMIC_ISINK_CH0_EN, NLED_OFF);	/* sw
				workround for sync leds status */
			if (button_flag_isink2 == 0)
				pmic_set_register_value(PMIC_ISINK_CH2_EN,
							NLED_OFF);
			if (button_flag_isink3 == 0)
				pmic_set_register_value(PMIC_ISINK_CH3_EN,
							NLED_OFF);
			first_time = false;
		}
		pmic_set_register_value(PMIC_RG_DRV_32K_CK_PDN, 0x0);	/* Disable power down */
		pmic_set_register_value(PMIC_RG_DRV_ISINK1_CK_PDN, 0);
		pmic_set_register_value(PMIC_RG_DRV_ISINK1_CK_CKSEL, 0);
		pmic_set_register_value(PMIC_ISINK_CH1_MODE, ISINK_PWM_MODE);
		pmic_set_register_value(PMIC_ISINK_CH1_STEP, ISINK_3);	/* 16mA */
		pmic_set_register_value(PMIC_ISINK_DIM1_DUTY, 15);
		pmic_set_register_value(PMIC_ISINK_DIM1_FSEL, ISINK_1KHZ);	/* 1KHz */
		if (level)
			pmic_set_register_value(PMIC_ISINK_CH1_EN, NLED_ON);
		else
			pmic_set_register_value(PMIC_ISINK_CH1_EN, NLED_OFF);
		mutex_unlock(&leds_pmic_mutex);
		return 0;
	}
	mutex_unlock(&leds_pmic_mutex);
	return -1;
}
上述pmic_set_register_value的操作就是對ISINK具體寄存器的操作,本文不作深入研究
三、總結
通過上述的分析,我們大致可以看出在mtk平台上leds系列的驅動流程大致如下,先在dts中定義各個led節點的配置,配置如mode,name,data 預留給driver調用,然後創建common drver (通用驅動)對各個led統一管理,在通用驅動中各個不同類型led 通過數組區分,common driver對各個led進行統一的設備注冊,屬性節點創建等。當然led的種類繁多還有充電指示燈,呼吸燈等,在加上每種燈硬件配置不一樣驅動實現方式也不同,這個需要另行分析了。







  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved