kernel_samsung_a34x-permissive/drivers/misc/mediatek/leds/leds-mt6357.c

1433 lines
39 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 MediaTek Inc.
*
*/
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/mfd/mt6397/registers.h>
#include <linux/mfd/mt6397/core.h>
#include <linux/mfd/mt6357/registers.h>
#include <linux/mfd/mt6357/core.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include "mt_led_trigger.h"
#ifndef UNUSED
#define UNUSED(x) { (void)(x); }
#endif
/*
* Register field for mt6357_TOP_CKPDN0 to enable
* 128K clock common for LED device.
*/
#define RG_DRV_ISINK_CK_PDN MT6357_XPP_TOP_CKPDN_CON0
#define RG_DRV_128K_CK_PDN_SHIFT 4
#define RG_DRV_128K_CK_PDN_MASK 0x1
#define RG_DRV_ISINK1_CK_PDN_SHIFT 1
#define RG_DRV_ISINK1_CK_PDN_MASK 0x1
#define RG_DRV_CHRIND_CK_PDN_SHIFT 5
#define RG_DRV_CHRIND_CK_PDN_MASK 0x1
//ISINK channel enable
#define ISINK_EN_CTRL MT6357_ISINK_EN_CTRL
#define ISINK_CH1_BIAS_EN_SHIFT 11
#define ISINK_CH1_BIAS_EN_MASK 0x1
#define ISINK_CH1_EN_SHIFT 1
#define ISINK_CH1_EN_MASK 0x1
#define ISINK_CHOP1_EN_SHIFT 5
#define ISINK_CHOP1_EN_MASK 0x1
//ISINK Step
#define ISINK_CH1_STEP MT6357_ISINK1_CON1
#define ISINK_CH1_STEP_MASK 13
#define ISINK_CH1_STEP_SHIFT 0x7
#define ISINK_CH1_STEP_MAX 0x7
//ISINK mode
#define ISINK_CH1_MODE MT6357_ISINK_MODE_CTRL
#define ISINK_CH1_MODE_SHIFT 12
#define ISINK_CH1_MODE_MASK 0x3
#define ISINK_CH1_PWM_MODE_SHIFT 5
#define ISINK_CH1_PWM_MODE_MASK 0x1
#define ISINK_DIM1_FSEL MT6357_ISINK1_CON0
#define ISINK_DIM1_FSEL_SHIFT 0
#define ISINK_DIM1_FSEL_MASK 0xFFFF
#define ISINK_DIM1_DUTY MT6357_ISINK1_CON1
#define ISINK_DIM1_DUTY_SHIFT 5
#define ISINK_DIM1_DUTY_MASK 0xFF
//Breath mode :
#define ISINK_BREATH1_TR_SEL MT6357_ISINK1_CON2
#define ISINK_BREATH1_TR1_SEL_SHIFT 12
#define ISINK_BREATH1_TR1_SEL_MASK 0xF
#define ISINK_BREATH1_TR2_SEL_SHIFT 8
#define ISINK_BREATH1_TR2_SEL_MASK 0xF
#define ISINK_BREATH1_TF1_SEL_SHIFT 4
#define ISINK_BREATH1_TF1_SEL_MASK 0xF
#define ISINK_BREATH1_TF2_SEL_SHIFT 0
#define ISINK_BREATH1_TF2_SEL_MASK 0xF
#define ISINK_BREATH1_TON_SEL MT6357_ISINK1_CON3
#define ISINK_BREATH1_TON_SEL_SHIFT 8
#define ISINK_BREATH1_TON_SEL_MASK 0xF
#define ISINK_BREATH1_TOFF_SEL_SHIFT 0
#define ISINK_BREATH1_TOFF_SEL_MASK 0xF
#define ISINK_SFSTR1 MT6357_ISINK_SFSTR
#define ISINK_SFSTR1_TC_SHIFT 9
#define ISINK_SFSTR1_TC_MASK 0x3
#define ISINK_SFSTR1_EN_SHIFT 8
#define ISINK_SFSTR1_EN_MASK 0x1
#define ISINK_MODE_PWM (0x00)
#define ISINK_MODE_BREATH (0x01)
#define ISINK_MODE_REGISTER (0x11)
#define mt6357_MAX_PERIOD 10000
#define mt6357_MAX_BRIGHTNESS 255
//#define LED_TEST
#undef pr_fmt
#define pr_fmt(fmt) KBUILD_MODNAME " %s(%d) :" fmt, __func__, __LINE__
enum {
MT6357_ISINK0 = 0,
MT6357_ISINK1,
MT6357_ISINK2,
MT6357_ISINK3,
MT6357_ISINK_MAX,
};
enum {
MT_LEDMODE_DEFAULT,
MT_LEDMODE_REGISTER,
MT_LEDMODE_PWM,
MT_LEDMODE_BREATH,
MT_LEDMODE_MAX,
};
struct mt6357_leds;
/**
* struct mt6357_led - state container for the LED device
* @id: the identifier in mt6357 LED device
* @parent: the pointer to mt6357 LED controller
* @cdev: LED class device for this LED device
* @current_brightness: current state of the LED device
*/
struct mt6357_led {
struct mt_led_info l_info; /* most be the first member */
int id;
struct mt6357_leds *parent;
enum led_brightness current_brightness;
int step;
};
/**
* struct mt6357_leds - state container for holding LED controller
* of the driver
* @dev: the device pointer
* @hw: the underlying hardware providing shared
* bus for the register operations
* @lock: the lock among process context
* @led: the array that contains the state of individual
* LED device
*/
struct mt6357_leds {
struct device *dev;
struct regmap *regmap;
/* protect among process context */
struct mutex lock;
struct mt6357_led *led[MT6357_ISINK_MAX];
};
static int mt6357_led_get_clock(struct led_classdev *cdev, int *en)
{
struct mt_led_info *l_info = (struct mt_led_info *)cdev;
struct mt6357_led *led = container_of(l_info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret = 0;
ret = regmap_read(regmap, RG_DRV_ISINK_CK_PDN, en);
if (ret < 0) {
dev_notice(led->parent->dev,
"%s: RG_DRV_ISINK_CK_PDN Reg(0x%x) Read ERROR.\n",
__func__, RG_DRV_ISINK_CK_PDN);
return ret;
}
pr_info("get RG_DRV_ISINK_CK_PDN[0x%0x]: %d", RG_DRV_ISINK_CK_PDN, *en);
switch (led->id) {
case MT6357_ISINK1:
*en = (~*en >> RG_DRV_ISINK1_CK_PDN_SHIFT) & RG_DRV_ISINK1_CK_PDN_MASK;
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
pr_info("%s: PMIC LED(%d) Get clock %s.\n",
__func__, led->id, en ? "True" : "False");
return ret;
}
static int mt6357_led_set_clock(struct led_classdev *cdev, int en)
{
struct mt_led_info *l_info = (struct mt_led_info *)cdev;
struct mt6357_led *led = container_of(l_info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
unsigned int value;
int ret = 0;
dev_info(led->parent->dev, "%s: PMIC LED(%d) Set clock %s.\n",
__func__, led->id, en ? "True" : "False");
//XPP clock control
switch (led->id) {
case MT6357_ISINK1:
value = RG_DRV_ISINK1_CK_PDN_MASK;
value = en ? (~value & RG_DRV_ISINK1_CK_PDN_MASK) : value;
ret = regmap_update_bits(regmap,
RG_DRV_ISINK_CK_PDN,
RG_DRV_ISINK1_CK_PDN_MASK << RG_DRV_ISINK1_CK_PDN_SHIFT,
value << RG_DRV_ISINK1_CK_PDN_SHIFT);
if (ret < 0) {
dev_notice(led->parent->dev,
"%s: RG_DRV_ISINK_CK_PDN Reg(0x%x) Write ERROR.\n",
__func__, RG_DRV_ISINK_CK_PDN);
return ret;
}
pr_info("set ISINK_EN_CTRL[0x%0x]: 0x%0x",
RG_DRV_ISINK_CK_PDN,
value << RG_DRV_ISINK1_CK_PDN_SHIFT);
mt6357_led_get_clock(cdev, &en);
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_set_ISINK(struct led_classdev *cdev, int en)
{
struct mt_led_info *l_info = (struct mt_led_info *)cdev;
struct mt6357_led *led = container_of(l_info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
unsigned int en_value;
unsigned int chop_value;
int ret = 0;
dev_dbg(led->parent->dev, "%s: PMIC LED(%d) Set ISINK %s.\n",
__func__, led->id, en ? "True" : "False");
//PMIC ISINK enable
switch (led->id) {
case MT6357_ISINK1:
en_value = ISINK_CH1_EN_MASK;
chop_value = ISINK_CHOP1_EN_MASK;
if (!en) {
en_value = ~en_value;
chop_value = ~chop_value;
}
en_value = (en_value & ISINK_CH1_EN_MASK) << ISINK_CH1_EN_SHIFT;
chop_value = (chop_value & ISINK_CHOP1_EN_MASK) << ISINK_CHOP1_EN_SHIFT;
ret = regmap_update_bits(regmap,
ISINK_EN_CTRL,
(ISINK_CHOP1_EN_MASK << ISINK_CHOP1_EN_SHIFT) |
(ISINK_CH1_EN_MASK << ISINK_CH1_EN_SHIFT),
en_value | chop_value);
if (ret < 0) {
dev_notice(led->parent->dev,
"%s: ISINK_EN_CTRL Reg(0x%x) Write ERROR.\n",
__func__, ISINK_EN_CTRL);
return ret;
}
pr_info("set ISINK_EN_CTRL[0x%0x]: 0x%0x", ISINK_EN_CTRL, en_value | chop_value);
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_get_ISINK(struct led_classdev *cdev, int *CTL0, int *CTL1)
{
struct mt_led_info *l_info = (struct mt_led_info *)cdev;
struct mt6357_led *led = container_of(l_info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret = 0;
unsigned int value;
switch (led->id) {
case MT6357_ISINK1:
ret = regmap_read(regmap, ISINK_EN_CTRL, &value);
if (ret < 0) {
dev_notice(led->parent->dev,
"%s: ISINK_EN_CTRL Reg(0x%x) Read ERROR.\n",
__func__, ISINK_EN_CTRL);
return ret;
}
pr_info("get ISINK_EN_CTRL[0x%0x]: 0x%0x", ISINK_EN_CTRL, value);
*CTL0 = (value >> ISINK_CHOP1_EN_SHIFT) & ISINK_CHOP1_EN_MASK;
*CTL1 = (value >> ISINK_CH1_EN_SHIFT) & ISINK_CH1_EN_MASK;
break;
case MT6357_ISINK2:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
/*
* Trigger : register mode / breath mode / PWM mode
*/
//Trigger mode change
static int mt6357_led_set_current_step(struct mt_led_info *info, int step);
static int mt6357_led_set_pwm_dim_freq(struct mt_led_info *info, int freq);
static int mt6357_led_hw_brightness(struct led_classdev *cdev, enum led_brightness brightness);
static int mt6357_led_change_mode(struct led_classdev *cdev, int mode)
{
struct mt_led_info *l_info = (struct mt_led_info *)cdev;
struct mt6357_led *led = container_of(l_info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret = 0;
int mode_val = 0;
if (mode >= MT_LEDMODE_MAX)
return -EINVAL;
mutex_lock(&leds->lock);
dev_info(led->parent->dev, "%s mode = %s\n",
__func__, mt_led_trigger_mode_name[mode]);
switch (mode) {
case MT_LEDMODE_REGISTER:
mode_val = 2;
break;
case MT_LEDMODE_DEFAULT:
case MT_LEDMODE_PWM:
mode_val = 0;
break;
case MT_LEDMODE_BREATH:
mode_val = 1;
break;
}
//PMIC ISINK disable
mt6357_led_set_ISINK(cdev, false);
//PMIC mode: PWM mode(0x00) / Breath mode(0x01) / CC mode(0x10)
switch (led->id) {
case MT6357_ISINK1:
mode_val = mode_val << ISINK_CH1_MODE_SHIFT;
ret = regmap_update_bits(regmap, ISINK_CH1_MODE,
ISINK_CH1_MODE_MASK << ISINK_CH1_MODE_SHIFT, mode_val);
pr_info("set ISINK_CH1_MODE[0x%0x]: 0x%0x", ISINK_CH1_MODE, mode_val);
if (ret < 0) {
dev_notice(led->parent->dev,
"%s: ISINK_CH1_MODE Reg(0x%x) Write ERROR.\n",
__func__, ISINK_CH1_MODE);
return ret;
}
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
//PMIC ISINK enable
mt6357_led_set_ISINK(cdev, true);
//default mode
if (MT_LEDMODE_PWM == mode || MT_LEDMODE_DEFAULT == mode) {
mt6357_led_set_current_step(l_info, led->step);
mt6357_led_set_pwm_dim_freq(l_info, 0x0);
mt6357_led_hw_brightness(cdev, mt6357_MAX_BRIGHTNESS);
}
mutex_unlock(&leds->lock);
return ret;
}
static int mt6357_led_get_current_step(struct mt_led_info *info, int *value)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
switch (led->id) {
case MT6357_ISINK1:
//PMIC step
ret = regmap_read(regmap, ISINK_CH1_STEP, value);
if (ret < 0) {
dev_notice(led->parent->dev,
"%s: ISINK1_CH1_STEP Reg(0x%x) Write ERROR.\n",
__func__, ISINK_CH1_STEP);
return ret;
}
pr_info("get ISINK_CH1_STEP[0x%0x]: 0x%0x", ISINK_CH1_STEP, *value);
*value = (*value >> ISINK_CH1_STEP_MASK) & ISINK_CH1_STEP_SHIFT;
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_set_current_step(struct mt_led_info *info, int step)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
unsigned int value;
int ret;
if (step > 8 || step < 0) {
dev_notice(led->parent->dev, "%s: Input %d is out of range.\n", __func__, step);
return -EINVAL;
}
switch (led->id) {
case MT6357_ISINK1:
//PMIC step
value = step << ISINK_CH1_STEP_SHIFT;
ret = regmap_update_bits(regmap, ISINK_CH1_STEP,
ISINK_CH1_STEP_MASK << ISINK_CH1_STEP_SHIFT, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK1_CH1_STEP Reg(0x%x) Write ERROR.\n",
__func__, ISINK_CH1_STEP);
return -1;
}
pr_info("set ISINK_CH1_STEP[0x%0x]: 0x%0x", ISINK_CH1_STEP, value);
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return value;
}
/*
* Trigger : PWM mode
*/
static int mt6357_led_list_pwm_duty(struct mt_led_info *info, char *buf)
{
snprintf(buf, PAGE_SIZE, "%s\n", "0~255");
return 0;
}
static int mt6357_led_get_pwm_dim_duty(struct mt_led_info *info, int *value)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
switch (led->id) {
case MT6357_ISINK1:
//PMIC step
ret = regmap_read(regmap, ISINK_DIM1_DUTY, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK1_DIM1_DUTY Reg(0x%x) Read ERROR.\n",
__func__, ISINK_DIM1_DUTY);
return -1;
}
pr_info("get ISINK_DIM1_DUTY[0x%0x]: 0x%0x", ISINK_DIM1_DUTY, *value);
*value = (*value >> ISINK_DIM1_DUTY_SHIFT) & ISINK_DIM1_DUTY_MASK;
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_set_pwm_dim_duty(struct mt_led_info *info, int duty)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
unsigned int value;
if (duty > 255 || duty < 0) {
dev_notice(led->parent->dev, "%s: Input %d is out of range.\n", __func__, duty);
return -EINVAL;
}
//PWM Duty
switch (led->id) {
case MT6357_ISINK1:
//PMIC step
value = duty << ISINK_DIM1_DUTY_SHIFT;
ret = regmap_update_bits(regmap, ISINK_DIM1_DUTY,
ISINK_DIM1_DUTY_MASK << ISINK_DIM1_DUTY_SHIFT, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK_DIM1_DUTY Reg(0x%x) Write ERROR.\n",
__func__, ISINK_DIM1_DUTY);
return -1;
}
pr_info("set ISINK_DIM1_DUTY[0x%0x]: 0x%0x", ISINK_DIM1_DUTY, value);
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_list_pwm_freq(struct mt_led_info *info, char *buf)
{
snprintf(buf, PAGE_SIZE, "%s\n", "0~65535 (500 HZ ~ 0.076HZ)");
return 0;
}
static int mt6357_led_get_pwm_dim_freq(struct mt_led_info *info, int *value)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
switch (led->id) {
case MT6357_ISINK1:
ret = regmap_read(regmap, ISINK_DIM1_FSEL, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK_DIM1_FSEL Reg(0x%x) Write ERROR.\n",
__func__, ISINK_DIM1_FSEL);
return -1;
}
pr_info("get ISINK_DIM1_FSEL [0x%0x]: 0x%0x", ISINK_DIM1_FSEL, *value);
*value = (*value >> ISINK_DIM1_FSEL_SHIFT) & ISINK_DIM1_FSEL_MASK;
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_set_pwm_dim_freq(struct mt_led_info *info, int freq)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
unsigned int value;
int ret;
if (freq > 0xFFFF || freq < 0) {
dev_notice(led->parent->dev, "%s: Input %d is out of range.\n", __func__, freq);
return -EINVAL;
}
//PWM Frequency
switch (led->id) {
case MT6357_ISINK1:
value = value << ISINK_DIM1_FSEL_SHIFT;
ret = regmap_update_bits(regmap, ISINK_DIM1_FSEL,
ISINK_DIM1_FSEL_MASK << ISINK_DIM1_FSEL_SHIFT, value);
pr_info("set ISINK_DIM1_FSEL[0x%0x]: 0x%0x", ISINK_DIM1_FSEL, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK_DIM1_FSEL Reg(0x%x) Write ERROR.\n",
__func__, ISINK_DIM1_FSEL);
return -1;
}
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
/*
* Trigger : breath mode
*/
static int mt6357_led_get_breath_tr1(struct mt_led_info *info, int *value)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
switch (led->id) {
case MT6357_ISINK1:
ret = regmap_read(regmap, ISINK_BREATH1_TR_SEL, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK_BREATH1_TR_SEL Reg(0x%x) Read ERROR.\n",
__func__, ISINK_BREATH1_TR_SEL);
return -1;
}
pr_info("get ISINK_BREATH1_TR_SEL[0x%0x]1: 0x%0x", ISINK_BREATH1_TR_SEL, *value);
*value = (*value >> ISINK_BREATH1_TR1_SEL_SHIFT) & ISINK_BREATH1_TR1_SEL_MASK;
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_get_breath_tr2(struct mt_led_info *info, int *value)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
switch (led->id) {
case MT6357_ISINK1:
ret = regmap_read(regmap, ISINK_BREATH1_TR_SEL, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK_BREATH1_TR_SEL Reg(0x%x) Read ERROR.\n",
__func__, ISINK_BREATH1_TR_SEL);
return -1;
}
pr_info("get ISINK_BREATH1_TR_SEL[0x%0x]1: 0x%0x", ISINK_BREATH1_TR_SEL, *value);
*value = (*value >> ISINK_BREATH1_TR2_SEL_SHIFT) & ISINK_BREATH1_TR2_SEL_MASK;
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_get_breath_tf1(struct mt_led_info *info, int *value)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
switch (led->id) {
case MT6357_ISINK1:
ret = regmap_read(regmap, ISINK_BREATH1_TR_SEL, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK_BREATH1_TR_SEL Reg(0x%x) Read ERROR.\n",
__func__, ISINK_BREATH1_TR_SEL);
return -1;
}
pr_info("get ISINK_BREATH1_TR_SEL[0x%0x]: 0x%0x", ISINK_BREATH1_TR_SEL, *value);
*value = (*value >> ISINK_BREATH1_TF1_SEL_SHIFT) & ISINK_BREATH1_TF1_SEL_MASK;
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_get_breath_tf2(struct mt_led_info *info, int *value)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
switch (led->id) {
case MT6357_ISINK1:
ret = regmap_read(regmap, ISINK_BREATH1_TR_SEL, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK_BREATH1_TR_SEL Reg(0x%x) Read ERROR.\n",
__func__, ISINK_BREATH1_TR_SEL);
return -1;
}
pr_info("get ISINK_BREATH1_TR_SEL[0x%0x]: 0x%0x", ISINK_BREATH1_TR_SEL, *value);
*value = (*value >> ISINK_BREATH1_TF2_SEL_SHIFT) & ISINK_BREATH1_TF2_SEL_MASK;
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_get_breath_ton(struct mt_led_info *info, int *value)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
switch (led->id) {
case MT6357_ISINK1:
ret = regmap_read(regmap, ISINK_BREATH1_TON_SEL, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK_BREATH1_TON_SEL Reg(0x%x) Read ERROR.\n",
__func__, ISINK_BREATH1_TON_SEL);
return -1;
}
pr_info("get ISINK_BREATH1_TON_SEL[0x%0x]: 0x%0x", ISINK_BREATH1_TON_SEL, *value);
*value = (*value >> ISINK_BREATH1_TON_SEL_SHIFT) & ISINK_BREATH1_TON_SEL_MASK;
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_get_breath_toff(struct mt_led_info *info, int *value)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
switch (led->id) {
case MT6357_ISINK1:
ret = regmap_read(regmap, ISINK_BREATH1_TON_SEL, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK_BREATH1_TON_SEL Reg(0x%x) Read ERROR.\n",
__func__, ISINK_BREATH1_TON_SEL);
return -1;
}
pr_info("get ISINK_BREATH1_TON_SEL[0x%0x]: 0x%0x", ISINK_BREATH1_TON_SEL, *value);
*value = (*value >> ISINK_BREATH1_TOFF_SEL_SHIFT) & ISINK_BREATH1_TOFF_SEL_MASK;
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_set_breath_tr1(struct mt_led_info *info, int time)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
unsigned int value;
if (time > 15 || time < 0) {
dev_notice(led->parent->dev, "%s: Input %d is out of range.\n", __func__, time);
return -EINVAL;
}
//Breath Time
switch (led->id) {
case MT6357_ISINK1:
value = time << ISINK_BREATH1_TR1_SEL_SHIFT;
ret = regmap_update_bits(regmap, ISINK_BREATH1_TR_SEL,
ISINK_BREATH1_TR1_SEL_MASK << ISINK_BREATH1_TR1_SEL_SHIFT, value);
pr_info("set ISINK_BREATH1_TR_SEL[0x%0x]: 0x%0x", ISINK_BREATH1_TR_SEL, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK_BREATH1_TR_SEL Reg(0x%x) Write ERROR.\n",
__func__, ISINK_BREATH1_TR_SEL);
return -1;
}
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_set_breath_tr2(struct mt_led_info *info, int time)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
unsigned int value;
if (time > 15 || time < 0) {
dev_notice(led->parent->dev, "%s: Input %d is out of range.\n", __func__, time);
return -EINVAL;
}
//Breath Time
switch (led->id) {
case MT6357_ISINK1:
value = time << ISINK_BREATH1_TR2_SEL_SHIFT;
ret = regmap_update_bits(regmap, ISINK_BREATH1_TR_SEL,
ISINK_BREATH1_TR2_SEL_MASK << ISINK_BREATH1_TR2_SEL_SHIFT, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK1_BREATH1_TR_SEL Reg(0x%x) Write ERROR.\n",
__func__, ISINK_BREATH1_TR_SEL);
return -1;
}
pr_info("set ISINK_BREATH1_TR_SEL[0x%0x]: 0x%0x", ISINK_BREATH1_TR_SEL, value);
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_set_breath_tf1(struct mt_led_info *info, int time)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
unsigned int value;
if (time > 15 || time < 0) {
dev_notice(led->parent->dev, "%s: Input %d is out of range.\n", __func__, time);
return -EINVAL;
}
//Breath Time
value = time << ISINK_BREATH1_TF1_SEL_SHIFT;
switch (led->id) {
case MT6357_ISINK1:
ret = regmap_update_bits(regmap, ISINK_BREATH1_TR_SEL,
ISINK_BREATH1_TF1_SEL_MASK << ISINK_BREATH1_TF1_SEL_SHIFT, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK1_BREATH1_TR_SEL Reg(0x%x) Write ERROR.\n",
__func__, ISINK_BREATH1_TR_SEL);
return -1;
}
pr_info("set ISINK_BREATH1_TR_SEL[0x%0x]: 0x%0x", ISINK_BREATH1_TR_SEL, value);
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_set_breath_tf2(struct mt_led_info *info, int time)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
unsigned int value;
if (time > 15 || time < 0) {
dev_notice(led->parent->dev, "%s: Input %d is out of range.\n", __func__, time);
return -EINVAL;
}
//Breath Time
value = time << ISINK_BREATH1_TF2_SEL_SHIFT;
switch (led->id) {
case MT6357_ISINK1:
ret = regmap_update_bits(regmap, ISINK_BREATH1_TR_SEL,
ISINK_BREATH1_TF2_SEL_MASK << ISINK_BREATH1_TF2_SEL_SHIFT, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK1_BREATH1_TR_SEL Reg(0x%x) Write ERROR.\n",
__func__, ISINK_BREATH1_TR_SEL);
return -1;
}
pr_info("set ISINK_BREATH1_TR_SEL[0x%0x]: 0x%0x", ISINK_BREATH1_TR_SEL, value);
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_set_breath_ton(struct mt_led_info *info, int time)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
unsigned int value;
if (time > ISINK_BREATH1_TON_SEL_MASK || time < 0) {
dev_notice(led->parent->dev, "%s: Input %d is out of range.\n", __func__, time);
return -EINVAL;
}
//Breath Time
value = time << ISINK_BREATH1_TON_SEL_SHIFT;
switch (led->id) {
case MT6357_ISINK1:
ret = regmap_update_bits(regmap, ISINK_BREATH1_TON_SEL,
ISINK_BREATH1_TON_SEL_MASK << ISINK_BREATH1_TON_SEL_SHIFT, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK_BREATH1_TON_SEL Reg(0x%x) Write ERROR.\n",
__func__, ISINK_BREATH1_TON_SEL);
return -1;
}
pr_info("set ISINK_BREATH1_TON_SEL[0x%0x]: 0x%0x", ISINK_BREATH1_TON_SEL, value);
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
static int mt6357_led_set_breath_toff(struct mt_led_info *info, int time)
{
struct mt6357_led *led = container_of(info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
unsigned int value;
if (time > ISINK_BREATH1_TOFF_SEL_MASK || time < 0) {
dev_notice(led->parent->dev, "%s: Input %d is out of range.\n", __func__, time);
return -EINVAL;
}
//Breath Time
value = time << ISINK_BREATH1_TOFF_SEL_SHIFT;
switch (led->id) {
case MT6357_ISINK1:
ret = regmap_update_bits(regmap, ISINK_BREATH1_TON_SEL,
ISINK_BREATH1_TOFF_SEL_MASK << ISINK_BREATH1_TOFF_SEL_SHIFT, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK_BREATH1_TON_SEL Reg(0x%x) Write ERROR.\n",
__func__, ISINK_BREATH1_TON_SEL);
return -1;
}
pr_info("set ISINK_BREATH1_TON_SEL[0x%0x]: 0x%0x", ISINK_BREATH1_TON_SEL, value);
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return ret;
}
struct mt_led_ops mt6357_led_ops = {
.change_mode = &mt6357_led_change_mode,
.get_current_step = &mt6357_led_get_current_step,
.set_current_step = &mt6357_led_set_current_step,
.get_pwm_dim_duty = &mt6357_led_get_pwm_dim_duty,
.set_pwm_dim_duty = &mt6357_led_set_pwm_dim_duty,
.get_pwm_dim_freq = &mt6357_led_get_pwm_dim_freq,
.set_pwm_dim_freq = &mt6357_led_set_pwm_dim_freq,
.get_breath_tr1 = &mt6357_led_get_breath_tr1,
.get_breath_tr2 = &mt6357_led_get_breath_tr2,
.get_breath_tf1 = &mt6357_led_get_breath_tf1,
.get_breath_tf2 = &mt6357_led_get_breath_tf2,
.get_breath_ton = &mt6357_led_get_breath_ton,
.get_breath_toff = &mt6357_led_get_breath_toff,
.set_breath_tr1 = &mt6357_led_set_breath_tr1,
.set_breath_tr2 = &mt6357_led_set_breath_tr2,
.set_breath_tf1 = &mt6357_led_set_breath_tf1,
.set_breath_tf2 = &mt6357_led_set_breath_tf2,
.set_breath_ton = &mt6357_led_set_breath_ton,
.set_breath_toff = &mt6357_led_set_breath_toff,
.list_pwm_duty = &mt6357_led_list_pwm_duty,
.list_pwm_freq = &mt6357_led_list_pwm_freq,
};
/*
* Setup current output for the corresponding
* brightness level.
*/
static int mt6357_led_hw_brightness(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct mt_led_info *l_info = (struct mt_led_info *)cdev;
struct mt6357_led *led = container_of(l_info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
struct regmap *regmap = leds->regmap;
int ret;
unsigned int value;
//PMIC Duty
switch (led->id) {
case MT6357_ISINK1:
value = brightness << ISINK_DIM1_DUTY_SHIFT;
ret = regmap_update_bits(regmap, ISINK_DIM1_DUTY,
ISINK_DIM1_DUTY_MASK << ISINK_DIM1_DUTY_SHIFT, value);
if (ret < 0) {
dev_notice(led->parent->dev, "%s: ISINK_DIM1_DUTY Reg(0x%x) Write ERROR.\n",
__func__, ISINK_DIM1_DUTY);
return -1;
}
pr_info("set ISINK_DIM1_DUTY[0x%0x]: 0x%0x", ISINK_DIM1_DUTY, value);
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT.\n", __func__, led->id);
break;
}
return 0;
}
static int mt6357_led_hw_off(struct led_classdev *cdev)
{
struct mt_led_info *l_info = (struct mt_led_info *)cdev;
struct mt6357_led *led = container_of(l_info, struct mt6357_led, l_info);
dev_dbg(led->parent->dev, "%s: PMIC LED(%d) disable.\n",
__func__, led->id);
//PMIC ISINK disable
return mt6357_led_set_ISINK(cdev, false);
}
static enum led_brightness
mt6357_get_led_hw_brightness(struct led_classdev *cdev)
{
struct mt_led_info *l_info = (struct mt_led_info *)cdev;
struct mt6357_led *led = container_of(l_info, struct mt6357_led, l_info);
unsigned int status_CTL0, status_CTL1;
int value;
int ret;
mt6357_led_get_ISINK(cdev, &status_CTL0, &status_CTL1);
switch (led->id) {
case MT6357_ISINK1:
if (!(status_CTL0 & ISINK_CHOP1_EN_MASK))
return 0;
if (!(status_CTL1 & ISINK_CH1_EN_MASK))
return 0;
break;
case MT6357_ISINK2:
case MT6357_ISINK3:
default:
dev_notice(led->parent->dev, "%s: ISINK%d NOT SUPPORT SET MODE.\n",
__func__, led->id);
break;
}
ret = mt6357_led_get_pwm_dim_duty(l_info, &value);
if (ret < 0)
return ret;
return value;
}
static int mt6357_led_hw_on(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct mt_led_info *l_info = (struct mt_led_info *)cdev;
struct mt6357_led *led = container_of(l_info, struct mt6357_led, l_info);
int ret = 0;
dev_dbg(led->parent->dev, "%s: PMIC LED(%d) enable.\n",
__func__, led->id);
//PMIC ISINK enable
mt6357_led_set_ISINK(cdev, true);
ret = mt6357_led_hw_brightness(cdev, brightness);
if (ret < 0)
return ret;
return 0;
}
static void mt6357_led_set_brightness(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct mt_led_info *l_info = (struct mt_led_info *)cdev;
struct mt6357_led *led = container_of(l_info, struct mt6357_led, l_info);
struct mt6357_leds *leds = led->parent;
int ret;
mutex_lock(&leds->lock);
if (!led->current_brightness && brightness) {
ret = mt6357_led_hw_on(cdev, brightness);
if (ret < 0)
goto out;
} else if (brightness) {
ret = mt6357_led_hw_brightness(cdev, brightness);
if (ret < 0)
goto out;
} else {
ret = mt6357_led_hw_off(cdev);
if (ret < 0)
goto out;
}
led->current_brightness = brightness;
out:
mutex_unlock(&leds->lock);
}
static int mt6357_led_set_blink(struct led_classdev *cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
struct mt_led_info *l_info = (struct mt_led_info *)cdev;
struct mt6357_led *led = container_of(l_info, struct mt6357_led, l_info);
unsigned long period;
int duty;
int freq;
int precision = 1000;
int ret = 0;
//We do not care about delay on
dev_info(led->parent->dev, "%s: delay_on = %lu, delay_off=%lu\n",
__func__, *delay_on, *delay_off);
/*
* Units are in ms, if over the hardware able
* to support, fallback into software blink
*/
if ((*delay_on) < 0 || (*delay_off) < 0) {
dev_notice(led->parent->dev, "%s: delay_on (%lu) or delay_off (%lu) is invalid value.\n",
__func__, *delay_on, *delay_off);
return -EINVAL;
}
/*
* LED subsystem requires a default user
* friendly blink pattern for the LED so using
* 1Hz duty cycle 50% here if without specific
* value delay_on and delay off being assigned.
*/
if (!(*delay_on) && !(*delay_off)) {
*delay_on = 500;
*delay_off = 500;
}
period = (*delay_on) + (*delay_off);
if (period > mt6357_MAX_PERIOD) {
dev_notice(led->parent->dev,
"%s: delay_on + delay_off = %lu is invalid value.\n",
__func__, period);
return -EINVAL;
}
dev_info(led->parent->dev, "%s: period = %lu\n", __func__, period);
//change mode to PWM
ret = mt6357_led_change_mode(cdev, MT_LED_PWM_MODE);
if (ret < 0) {
dev_notice(led->parent->dev,
"%s: mt6357_led_change_mode (%d) ERROR.\n",
__func__, MT_LED_PWM_MODE);
goto error;
}
//duty is the ratio between 1~256
duty = precision*256*(*delay_on) / period;
duty /= precision;
duty = duty - 1; //0~255
dev_info(led->parent->dev, "%s: Duty = 0x%0x\n", __func__, duty);
ret = mt6357_led_hw_brightness(cdev, duty);
if (ret < 0) {
dev_notice(led->parent->dev,
"%s: mt6357_led_hw_brightness (%d) ERROR.\n",
__func__, duty);
goto error;
}
//freq=(period/2) -1 , unit of period is ms
freq = ((period/2)-1 > 0) ? ((period/2)-1) : 0;
dev_info(led->parent->dev, "%s: Frequency = 0x%0x\n", __func__, freq);
ret = mt6357_led_set_pwm_dim_freq(l_info, freq);
if (ret < 0) {
dev_notice(led->parent->dev,
"%s: mt6357_led_set_pwm_dim_freq (%d) ERROR.\n",
__func__, freq);
goto error;
}
return 0;
error:
//disable LED
mt6357_led_set_ISINK(cdev, false);
return -EIO;
}
static int mt6357_led_set_dt_default(struct led_classdev *cdev,
struct device_node *np)
{
struct mt_led_info *l_info = (struct mt_led_info *)cdev;
struct mt6357_led *led = container_of(l_info, struct mt6357_led, l_info);
const char *state;
int ret = 0;
dev_info(led->parent->dev, "mt6357 parse led start\n");
led->l_info.cdev.name = of_get_property(np, "label", NULL) ? : np->name;
led->l_info.cdev.default_trigger = of_get_property(np,
"linux,default-trigger",
NULL);
state = of_get_property(np, "default-state", NULL);
if (state) {
if (!strcmp(state, "keep")) {
ret = mt6357_get_led_hw_brightness(cdev);
if (ret < 0)
return ret;
led->current_brightness = ret;
ret = 0;
} else if (!strcmp(state, "on")) {
mt6357_led_set_brightness(cdev, cdev->max_brightness);
} else {
mt6357_led_set_brightness(cdev, LED_OFF);
}
}
pr_info("mt6357 parse led[%d]: %s, %s, %s\n",
led->id, led->l_info.cdev.name, state, led->l_info.cdev.default_trigger);
return ret;
}
static int mt6357_led_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct device_node *child;
struct mt6357_leds *leds;
struct mt6357_led *led;
struct mt6397_chip *pmic_chip = dev_get_drvdata(pdev->dev.parent);
int ret;
u32 reg;
u32 step;
dev_info(&pdev->dev, "mt6357 led probe\n");
leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL);
if (!leds)
return -ENOMEM;
platform_set_drvdata(pdev, leds);
leds->dev = dev;
leds->regmap = pmic_chip->regmap;
/*
* leds->hw points to the underlying bus for the register
* controlled.
*/
if (!leds->regmap) {
dev_notice(&pdev->dev, "failed to allocate regmap\n");
return -ENODEV;
}
mutex_init(&leds->lock);
ret = regmap_write(leds->regmap, RG_DRV_ISINK_CK_PDN,
~(RG_DRV_128K_CK_PDN_MASK << RG_DRV_128K_CK_PDN_SHIFT));
if (ret < 0)
return ret;
for_each_available_child_of_node(np, child) {
ret = of_property_read_u32(child, "reg", &reg);
if (ret) {
dev_notice(dev, "Failed to read led 'reg' property\n");
goto put_child_node;
}
ret = of_property_read_u32(child, "step", &step);
if (!ret)
dev_info(dev, "read led 'step' property: %d\n", step);
else
step = ISINK_CH1_STEP_MAX;
if (reg >= MT6357_ISINK_MAX || leds->led[reg]) {
dev_notice(dev, "Invalid led reg %u\n", reg);
ret = -EINVAL;
goto put_child_node;
}
led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
if (!led) {
ret = -ENOMEM;
goto put_child_node;
}
leds->led[reg] = led;
leds->led[reg]->id = reg;
leds->led[reg]->l_info.cdev.max_brightness = mt6357_MAX_BRIGHTNESS;
leds->led[reg]->l_info.cdev.brightness_set =
mt6357_led_set_brightness;
leds->led[reg]->l_info.cdev.blink_set = mt6357_led_set_blink;
leds->led[reg]->l_info.cdev.brightness_get =
mt6357_get_led_hw_brightness;
leds->led[reg]->l_info.magic_code = MT_LED_ALL_MAGIC_CODE;
leds->led[reg]->l_info.ops = &mt6357_led_ops;
leds->led[reg]->parent = leds;
ret = mt6357_led_set_dt_default(&leds->led[reg]->l_info.cdev, child);
if (ret < 0) {
dev_notice(leds->dev,
"Failed to parse LED[%d] node from devicetree\n", reg);
goto put_child_node;
}
ret = devm_led_classdev_register(dev, &leds->led[reg]->l_info.cdev);
if (ret) {
dev_notice(&pdev->dev, "Failed to register LED: %d\n",
ret);
goto put_child_node;
}
leds->led[reg]->l_info.cdev.dev->of_node = child;
//check operations and register trigger
mt_led_trigger_register(&mt6357_led_ops);
//clock ON
mt6357_led_set_clock(&leds->led[reg]->l_info.cdev, true);
//default PWM mode
mt6357_led_change_mode(&leds->led[reg]->l_info.cdev, MT_LED_PWM_MODE);
//default PWM step
mt6357_led_set_current_step(&leds->led[reg]->l_info, step);
leds->led[reg]->step = step;
//ISINK OFF
mt6357_led_hw_off(&leds->led[reg]->l_info.cdev);
}
pr_info("mt6357 led end!");
return 0;
put_child_node:
of_node_put(child);
return ret;
}
static int mt6357_led_remove(struct platform_device *pdev)
{
struct mt6357_leds *leds = platform_get_drvdata(pdev);
int i;
/* Turn the LEDs off on driver removal. */
for (i = 0 ; leds->led[i] ; i++) {
//ISINK disable
mt6357_led_hw_off(&leds->led[i]->l_info.cdev);
//clock OFF
mt6357_led_set_clock(&leds->led[i]->l_info.cdev, false);
}
mutex_destroy(&leds->lock);
return 0;
}
static const struct of_device_id mt6357_led_dt_match[] = {
{ .compatible = "mediatek,mt6357_leds" },
{},
};
MODULE_DEVICE_TABLE(of, mt6357_led_dt_match);
static struct platform_driver mt6357_led_driver = {
.probe = mt6357_led_probe,
.remove = mt6357_led_remove,
.driver = {
.name = "leds-mt6357",
.of_match_table = mt6357_led_dt_match,
},
};
static int __init mt6357_leds_init(void)
{
int ret;
pr_info("Leds init");
ret = platform_driver_register(&mt6357_led_driver);
if (ret) {
pr_info("driver register error: %d", ret);
return ret;
}
return ret;
}
static void __exit mt6357_leds_exit(void)
{
platform_driver_unregister(&mt6357_led_driver);
}
module_init(mt6357_leds_init);
module_exit(mt6357_leds_exit);
//module_platform_driver(mt6357_led_driver);
MODULE_DESCRIPTION("LED driver for Mediatek mt6357 PMIC");
MODULE_AUTHOR("Mediatek Corporation");
MODULE_LICENSE("GPL");