From: ChiYuan Huang <[email protected]>
This patch series includes some fixes and add supproted ISNK hardware features.
From MT6360, ISNK can support three modes (CC, PWM, and Breath). The previous
one can only support CC mode.
ChiYuan Huang (4):
leds: flash: mt6360: Fix the wrong enable_reg in multicolor brightness
set
leds: flash: mt6360: Remove unused dependency in Kconfig
leds: flash: mt6360: Add mt6360 isnk channel hardware timer dimming
mode support
leds: flash: mt6360: Add mt6360 isnk channel hardwre breath mode
support
drivers/leds/flash/Kconfig | 4 +-
drivers/leds/flash/leds-mt6360.c | 413 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 410 insertions(+), 7 deletions(-)
--
2.7.4
From: ChiYuan Huang <[email protected]>
Add mt6360 isnk channel hardware timer dimming mode support.
Signed-off-by: ChiYuan Huang <[email protected]>
---
drivers/leds/flash/leds-mt6360.c | 209 +++++++++++++++++++++++++++++++++++++--
1 file changed, 203 insertions(+), 6 deletions(-)
diff --git a/drivers/leds/flash/leds-mt6360.c b/drivers/leds/flash/leds-mt6360.c
index 712cd46..8fe3dc4 100644
--- a/drivers/leds/flash/leds-mt6360.c
+++ b/drivers/leds/flash/leds-mt6360.c
@@ -24,9 +24,19 @@ enum {
MT6360_MAX_LEDS
};
+#define MT6360_ISNK_PWM_MODE 0
+#define MT6360_ISNK_CC_MODE 2
+
#define MT6360_REG_RGBEN 0x380
#define MT6360_REG_ISNK(_led_no) (0x381 + (_led_no))
+#define MT6360_REG_DIM(_led_no) (0x385 + (_led_no))
+#define MT6360_REG_FREQ1 0x389
+#define MT6360_REG_FREQ2 0x38A
#define MT6360_ISNK_ENMASK(_led_no) BIT(7 - (_led_no))
+#define MT6360_FREQ13_MASK GENMASK(7, 5)
+#define MT6360_FREQ2_MASK GENMASK(4, 2)
+#define MT6360_ISNK_MODE_MASK GENMASK(7, 6)
+#define MT6360_ISNK_MODE_SHFT 6
#define MT6360_ISNK_MASK GENMASK(4, 0)
#define MT6360_CHRINDSEL_MASK BIT(3)
@@ -98,13 +108,128 @@ struct mt6360_priv {
struct mt6360_led leds[];
};
+static int mt6360_get_selected_freq_duty(unsigned long on, unsigned long off,
+ unsigned int *fsel, unsigned int *dsel)
+{
+ static const unsigned int freq_ms[] = { 8000, 4000, 2000, 1000,
+ 500, 250, 8, 4 };
+ unsigned int sum = on + off;
+ int i;
+
+ /* Check if val is less than the smallest or larger then the largest */
+ if (sum > freq_ms[0] || sum < freq_ms[ARRAY_SIZE(freq_ms) - 1])
+ return -EINVAL;
+
+ /* Find the frequency for less or equal one */
+ for (i = 0; i < ARRAY_SIZE(freq_ms) - 1; i++) {
+ if (sum >= freq_ms[i])
+ break;
+ }
+
+ *fsel = i;
+ *dsel = on * 255 / sum;
+ return 0;
+}
+
+static int mt6360_set_pwm_dimming_param(struct mt6360_priv *priv,
+ unsigned int led_no, unsigned int fsel,
+ unsigned int dsel)
+{
+ unsigned int freq_reg, freq_mask, freq_shift;
+ int ret;
+
+ switch (led_no) {
+ case MT6360_LED_ISNK1:
+ freq_reg = MT6360_REG_FREQ1;
+ freq_mask = MT6360_FREQ13_MASK;
+ break;
+ case MT6360_LED_ISNK2:
+ freq_reg = MT6360_REG_FREQ1;
+ freq_mask = MT6360_FREQ2_MASK;
+ break;
+ case MT6360_LED_ISNK3:
+ freq_reg = MT6360_REG_FREQ2;
+ freq_mask = MT6360_FREQ13_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ freq_shift = ffs(freq_mask) - 1;
+
+ ret = regmap_update_bits(priv->regmap, freq_reg, freq_mask,
+ fsel << freq_shift);
+ if (ret)
+ return ret;
+
+ return regmap_write(priv->regmap, MT6360_REG_DIM(led_no), dsel);
+}
+
+static int mt6360_mc_blink_set(struct led_classdev *lcdev, unsigned long *don,
+ unsigned long *doff)
+{
+ struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev);
+ struct mt6360_led *led = container_of(mccdev, struct mt6360_led, mc);
+ struct mt6360_priv *priv = led->priv;
+ unsigned int freq_sel, duty_sel;
+ u8 mc_reg[3];
+ int i, ret;
+
+ if (!*don && !*doff)
+ *don = *doff = 500;
+
+ mutex_lock(&priv->lock);
+
+ ret = regmap_raw_read(priv->regmap, MT6360_REG_ISNK(0), mc_reg, 3);
+ if (ret)
+ goto out;
+
+ /* Configure all subleds to CC mode first */
+ for (i = 0; i < mccdev->num_colors; i++) {
+ struct mc_subled *subled = mccdev->subled_info + i;
+ int ch_idx = subled->channel;
+
+ mc_reg[ch_idx] &= ~MT6360_ISNK_MODE_MASK;
+ mc_reg[ch_idx] |= MT6360_ISNK_CC_MODE << MT6360_ISNK_MODE_SHFT;
+ }
+
+ ret = regmap_raw_write(priv->regmap, MT6360_REG_ISNK(0), mc_reg, 3);
+ if (ret)
+ goto out;
+
+ /* Get the desired selector for frequency and duty */
+ ret = mt6360_get_selected_freq_duty(*don, *doff, &freq_sel, &duty_sel);
+ if (ret)
+ goto out;
+
+ /* Configure all subleds dim freq/duty and change to PWM mode */
+ for (i = 0; i < mccdev->num_colors; i++) {
+ struct mc_subled *subled = mccdev->subled_info + i;
+ int ch_idx = subled->channel;
+
+ ret = mt6360_set_pwm_dimming_param(priv, ch_idx, freq_sel,
+ duty_sel);
+ if (ret)
+ goto out;
+
+ mc_reg[ch_idx] &= ~MT6360_ISNK_MODE_MASK;
+ mc_reg[ch_idx] |= MT6360_ISNK_PWM_MODE << MT6360_ISNK_MODE_SHFT;
+ }
+
+ ret = regmap_raw_write(priv->regmap, MT6360_REG_ISNK(0), mc_reg, 3);
+
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
static int mt6360_mc_brightness_set(struct led_classdev *lcdev,
enum led_brightness level)
{
struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev);
struct mt6360_led *led = container_of(mccdev, struct mt6360_led, mc);
struct mt6360_priv *priv = led->priv;
- u32 real_bright, enable_mask = 0, enable = 0;
+ u32 real_bright, enable_mask = 0, enable = 0, val;
int i, ret;
mutex_lock(&priv->lock);
@@ -113,11 +238,25 @@ static int mt6360_mc_brightness_set(struct led_classdev *lcdev,
for (i = 0; i < mccdev->num_colors; i++) {
struct mc_subled *subled = mccdev->subled_info + i;
+ unsigned int bright_reg = MT6360_REG_ISNK(subled->channel);
real_bright = min(lcdev->max_brightness, subled->brightness);
- ret = regmap_update_bits(priv->regmap,
- MT6360_REG_ISNK(subled->channel),
- MT6360_ISNK_MASK, real_bright);
+
+ ret = regmap_read(priv->regmap, bright_reg, &val);
+ if (ret)
+ goto out;
+
+ /* Assign the new brightness value */
+ val &= ~MT6360_ISNK_MASK;
+ val |= (real_bright & MT6360_ISNK_MASK);
+
+ /* If LED_OFF, always configure back to CC mode */
+ if (level == LED_OFF) {
+ val &= ~MT6360_ISNK_MODE_MASK;
+ val |= MT6360_ISNK_CC_MODE << MT6360_ISNK_MODE_SHFT;
+ }
+
+ ret = regmap_write(priv->regmap, bright_reg, val);
if (ret)
goto out;
@@ -134,6 +273,44 @@ static int mt6360_mc_brightness_set(struct led_classdev *lcdev,
return ret;
}
+static int mt6360_isnk_blink_set(struct led_classdev *lcdev, unsigned long *don,
+ unsigned long *doff)
+{
+ struct mt6360_led *led = container_of(lcdev, struct mt6360_led, isnk);
+ struct mt6360_priv *priv = led->priv;
+ unsigned int freq_sel, duty_sel;
+ int ret;
+
+ if (!*don && !*doff)
+ *don = *doff = 500;
+
+ mutex_lock(&priv->lock);
+
+ ret = regmap_update_bits(priv->regmap, MT6360_REG_ISNK(led->led_no),
+ MT6360_ISNK_MODE_MASK,
+ MT6360_ISNK_CC_MODE << MT6360_ISNK_MODE_SHFT);
+ if (ret)
+ goto out;
+
+ /* Get the desired selector for frequency and duty */
+ ret = mt6360_get_selected_freq_duty(*don, *doff, &freq_sel, &duty_sel);
+ if (ret)
+ goto out;
+
+ ret = mt6360_set_pwm_dimming_param(priv, led->led_no, freq_sel,
+ duty_sel);
+ if (ret)
+ goto out;
+
+ ret = regmap_update_bits(priv->regmap, MT6360_REG_ISNK(led->led_no),
+ MT6360_ISNK_MODE_MASK,
+ MT6360_ISNK_PWM_MODE << MT6360_ISNK_MODE_SHFT);
+
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
static int mt6360_isnk_brightness_set(struct led_classdev *lcdev,
enum led_brightness level)
{
@@ -141,12 +318,27 @@ static int mt6360_isnk_brightness_set(struct led_classdev *lcdev,
struct mt6360_priv *priv = led->priv;
u32 enable_mask = MT6360_ISNK_ENMASK(led->led_no);
u32 val = level ? MT6360_ISNK_ENMASK(led->led_no) : 0;
+ unsigned int bright_val;
int ret;
mutex_lock(&priv->lock);
- ret = regmap_update_bits(priv->regmap, MT6360_REG_ISNK(led->led_no),
- MT6360_ISNK_MASK, level);
+ ret = regmap_read(priv->regmap, MT6360_REG_ISNK(led->led_no),
+ &bright_val);
+ if (ret)
+ goto out;
+
+ bright_val &= ~MT6360_ISNK_MASK;
+ bright_val |= (level & MT6360_ISNK_MASK);
+
+ /* If LED_OFF, always configure back to CC mode */
+ if (led->led_no != MT6360_LED_ISNKML && level == LED_OFF) {
+ bright_val &= ~MT6360_ISNK_MODE_MASK;
+ bright_val |= MT6360_ISNK_CC_MODE << MT6360_ISNK_MODE_SHFT;
+ }
+
+ ret = regmap_write(priv->regmap, MT6360_REG_ISNK(led->led_no),
+ bright_val);
if (ret)
goto out;
@@ -666,6 +858,7 @@ static int mt6360_init_isnk_properties(struct mt6360_led *led,
lcdev = &led->mc.led_cdev;
lcdev->brightness_set_blocking = mt6360_mc_brightness_set;
+ lcdev->blink_set = mt6360_mc_blink_set;
} else {
if (led->led_no == MT6360_LED_ISNKML) {
step_uA = MT6360_ISNKML_STEPUA;
@@ -674,6 +867,10 @@ static int mt6360_init_isnk_properties(struct mt6360_led *led,
lcdev = &led->isnk;
lcdev->brightness_set_blocking = mt6360_isnk_brightness_set;
+
+ /* Suppose only ISNK1/2/3 support mode change */
+ if (led->led_no != MT6360_LED_ISNKML)
+ lcdev->blink_set = mt6360_isnk_blink_set;
}
ret = fwnode_property_read_u32(init_data->fwnode, "led-max-microamp",
--
2.7.4
Hi:
Is there any comment about this patch series?
One is for the fix, second is Kconfig refine, others are for the HW
PWM/Breath feature added.
cy_huang <[email protected]> 於 2022年4月28日 週四 下午5:33寫道:
>
> From: ChiYuan Huang <[email protected]>
>
> This patch series includes some fixes and add supproted ISNK hardware features.
> From MT6360, ISNK can support three modes (CC, PWM, and Breath). The previous
> one can only support CC mode.
>
> ChiYuan Huang (4):
> leds: flash: mt6360: Fix the wrong enable_reg in multicolor brightness
> set
> leds: flash: mt6360: Remove unused dependency in Kconfig
> leds: flash: mt6360: Add mt6360 isnk channel hardware timer dimming
> mode support
> leds: flash: mt6360: Add mt6360 isnk channel hardwre breath mode
> support
>
> drivers/leds/flash/Kconfig | 4 +-
> drivers/leds/flash/leds-mt6360.c | 413 ++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 410 insertions(+), 7 deletions(-)
>
> --
> 2.7.4
>