diff options
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/leds-mt6323.c | 153 |
1 files changed, 115 insertions, 38 deletions
diff --git a/drivers/leds/leds-mt6323.c b/drivers/leds/leds-mt6323.c index 17ee88043f52..65a99c067216 100644 --- a/drivers/leds/leds-mt6323.c +++ b/drivers/leds/leds-mt6323.c @@ -33,22 +33,18 @@ */ #define MT6323_RG_ISINK_CK_SEL_MASK(i) (BIT(10) << (i)) -/* - * Register for MT6323_ISINK_CON0 to setup the - * duty cycle of the blink. - */ -#define MT6323_ISINK_CON0(i) (MT6323_ISINK0_CON0 + 0x8 * (i)) +#define MT6323_ISINK_CON(r, i) (r + 0x8 * (i)) + +/* ISINK_CON0: Register to setup the duty cycle of the blink. */ #define MT6323_ISINK_DIM_DUTY_MASK (0x1f << 8) #define MT6323_ISINK_DIM_DUTY(i) (((i) << 8) & \ - MT6323_ISINK_DIM_DUTY_MASK) + MT6323_ISINK_DIM_DUTY_MASK) -/* Register to setup the period of the blink. */ -#define MT6323_ISINK_CON1(i) (MT6323_ISINK0_CON1 + 0x8 * (i)) +/* ISINK_CON1: Register to setup the period of the blink. */ #define MT6323_ISINK_DIM_FSEL_MASK (0xffff) #define MT6323_ISINK_DIM_FSEL(i) ((i) & MT6323_ISINK_DIM_FSEL_MASK) -/* Register to control the brightness. */ -#define MT6323_ISINK_CON2(i) (MT6323_ISINK0_CON2 + 0x8 * (i)) +/* ISINK_CON2: Register to control the brightness. */ #define MT6323_ISINK_CH_STEP_SHIFT 12 #define MT6323_ISINK_CH_STEP_MASK (0x7 << 12) #define MT6323_ISINK_CH_STEP(i) (((i) << 12) & \ @@ -63,12 +59,9 @@ #define MT6323_ISINK_CH_EN_MASK(i) BIT(i) #define MT6323_ISINK_CH_EN(i) BIT(i) -#define MT6323_MAX_PERIOD 10000 -#define MT6323_MAX_LEDS 4 -#define MT6323_MAX_BRIGHTNESS 6 -#define MT6323_UNIT_DUTY 3125 -#define MT6323_CAL_HW_DUTY(o, p) DIV_ROUND_CLOSEST((o) * 100000ul,\ - (p) * MT6323_UNIT_DUTY) +#define MAX_SUPPORTED_LEDS 8 +#define MT6323_CAL_HW_DUTY(o, p, u) DIV_ROUND_CLOSEST((o) * 100000ul,\ + (p) * (u)) struct mt6323_leds; @@ -87,11 +80,58 @@ struct mt6323_led { }; /** + * struct mt6323_regs - register spec for the LED device + * @top_ckpdn: Offset to ISINK_CKPDN[0..x] registers + * @num_top_ckpdn: Number of ISINK_CKPDN registers + * @top_ckcon: Offset to ISINK_CKCON[0..x] registers + * @num_top_ckcon: Number of ISINK_CKCON registers + * @isink_con: Offset to ISINKx_CON[0..x] registers + * @num_isink_con: Number of ISINKx_CON registers + * @isink_max_regs: Number of ISINK[0..x] registers + * @isink_en_ctrl: Offset to ISINK_EN_CTRL register + */ +struct mt6323_regs { + const u16 *top_ckpdn; + u8 num_top_ckpdn; + const u16 *top_ckcon; + u8 num_top_ckcon; + const u16 *isink_con; + u8 num_isink_con; + u8 isink_max_regs; + u16 isink_en_ctrl; +}; + +/** + * struct mt6323_hwspec - hardware specific parameters + * @max_period: Maximum period for all LEDs + * @max_leds: Maximum number of supported LEDs + * @max_brightness: Maximum brightness for all LEDs + * @unit_duty: Steps of duty per period + */ +struct mt6323_hwspec { + u16 max_period; + u8 max_leds; + u16 max_brightness; + u16 unit_duty; +}; + +/** + * struct mt6323_data - device specific data + * @regs: Register spec for this device + * @spec: Hardware specific parameters + */ +struct mt6323_data { + const struct mt6323_regs *regs; + const struct mt6323_hwspec *spec; +}; + +/** * struct mt6323_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 + * @pdata: device specific data * @lock: the lock among process context * @led: the array that contains the state of individual * LED device @@ -99,9 +139,10 @@ struct mt6323_led { struct mt6323_leds { struct device *dev; struct mt6397_chip *hw; + const struct mt6323_data *pdata; /* protect among process context */ struct mutex lock; - struct mt6323_led *led[MT6323_MAX_LEDS]; + struct mt6323_led *led[MAX_SUPPORTED_LEDS]; }; static int mt6323_led_hw_brightness(struct led_classdev *cdev, @@ -109,6 +150,7 @@ static int mt6323_led_hw_brightness(struct led_classdev *cdev, { struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); struct mt6323_leds *leds = led->parent; + const struct mt6323_regs *regs = leds->pdata->regs; struct regmap *regmap = leds->hw->regmap; u32 con2_mask = 0, con2_val = 0; int ret; @@ -124,7 +166,7 @@ static int mt6323_led_hw_brightness(struct led_classdev *cdev, MT6323_ISINK_SFSTR0_TC(2) | MT6323_ISINK_SFSTR0_EN; - ret = regmap_update_bits(regmap, MT6323_ISINK_CON2(led->id), + ret = regmap_update_bits(regmap, MT6323_ISINK_CON(regs->isink_con[2], led->id), con2_mask, con2_val); return ret; } @@ -133,18 +175,19 @@ static int mt6323_led_hw_off(struct led_classdev *cdev) { struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); struct mt6323_leds *leds = led->parent; + const struct mt6323_regs *regs = leds->pdata->regs; struct regmap *regmap = leds->hw->regmap; unsigned int status; int ret; status = MT6323_ISINK_CH_EN(led->id); - ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL, + ret = regmap_update_bits(regmap, regs->isink_en_ctrl, MT6323_ISINK_CH_EN_MASK(led->id), ~status); if (ret < 0) return ret; usleep_range(100, 300); - ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2, + ret = regmap_update_bits(regmap, regs->top_ckpdn[2], MT6323_RG_ISINK_CK_PDN_MASK(led->id), MT6323_RG_ISINK_CK_PDN(led->id)); if (ret < 0) @@ -158,25 +201,26 @@ mt6323_get_led_hw_brightness(struct led_classdev *cdev) { struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); struct mt6323_leds *leds = led->parent; + const struct mt6323_regs *regs = leds->pdata->regs; struct regmap *regmap = leds->hw->regmap; unsigned int status; int ret; - ret = regmap_read(regmap, MT6323_TOP_CKPDN2, &status); + ret = regmap_read(regmap, regs->top_ckpdn[2], &status); if (ret < 0) return ret; if (status & MT6323_RG_ISINK_CK_PDN_MASK(led->id)) return 0; - ret = regmap_read(regmap, MT6323_ISINK_EN_CTRL, &status); + ret = regmap_read(regmap, regs->isink_en_ctrl, &status); if (ret < 0) return ret; if (!(status & MT6323_ISINK_CH_EN(led->id))) return 0; - ret = regmap_read(regmap, MT6323_ISINK_CON2(led->id), &status); + ret = regmap_read(regmap, MT6323_ISINK_CON(regs->isink_con[2], led->id), &status); if (ret < 0) return ret; @@ -189,6 +233,7 @@ static int mt6323_led_hw_on(struct led_classdev *cdev, { struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); struct mt6323_leds *leds = led->parent; + const struct mt6323_regs *regs = leds->pdata->regs; struct regmap *regmap = leds->hw->regmap; unsigned int status; int ret; @@ -198,13 +243,13 @@ static int mt6323_led_hw_on(struct led_classdev *cdev, * clock and channel and let work with continuous blink as * the default. */ - ret = regmap_update_bits(regmap, MT6323_TOP_CKCON1, + ret = regmap_update_bits(regmap, regs->top_ckcon[1], MT6323_RG_ISINK_CK_SEL_MASK(led->id), 0); if (ret < 0) return ret; status = MT6323_RG_ISINK_CK_PDN(led->id); - ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2, + ret = regmap_update_bits(regmap, regs->top_ckpdn[2], MT6323_RG_ISINK_CK_PDN_MASK(led->id), ~status); if (ret < 0) @@ -212,7 +257,7 @@ static int mt6323_led_hw_on(struct led_classdev *cdev, usleep_range(100, 300); - ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL, + ret = regmap_update_bits(regmap, regs->isink_en_ctrl, MT6323_ISINK_CH_EN_MASK(led->id), MT6323_ISINK_CH_EN(led->id)); if (ret < 0) @@ -222,13 +267,13 @@ static int mt6323_led_hw_on(struct led_classdev *cdev, if (ret < 0) return ret; - ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id), + ret = regmap_update_bits(regmap, MT6323_ISINK_CON(regs->isink_con[0], led->id), MT6323_ISINK_DIM_DUTY_MASK, MT6323_ISINK_DIM_DUTY(31)); if (ret < 0) return ret; - ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id), + ret = regmap_update_bits(regmap, MT6323_ISINK_CON(regs->isink_con[1], led->id), MT6323_ISINK_DIM_FSEL_MASK, MT6323_ISINK_DIM_FSEL(1000)); if (ret < 0) @@ -243,6 +288,8 @@ static int mt6323_led_set_blink(struct led_classdev *cdev, { struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); struct mt6323_leds *leds = led->parent; + const struct mt6323_regs *regs = leds->pdata->regs; + const struct mt6323_hwspec *spec = leds->pdata->spec; struct regmap *regmap = leds->hw->regmap; unsigned long period; u8 duty_hw; @@ -265,14 +312,14 @@ static int mt6323_led_set_blink(struct led_classdev *cdev, */ period = *delay_on + *delay_off; - if (period > MT6323_MAX_PERIOD) + if (period > spec->max_period) return -EINVAL; /* * Calculate duty_hw based on the percentage of period during * which the led is ON. */ - duty_hw = MT6323_CAL_HW_DUTY(*delay_on, period); + duty_hw = MT6323_CAL_HW_DUTY(*delay_on, period, spec->unit_duty); /* hardware doesn't support zero duty cycle. */ if (!duty_hw) @@ -290,13 +337,13 @@ static int mt6323_led_set_blink(struct led_classdev *cdev, led->current_brightness = cdev->max_brightness; } - ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id), + ret = regmap_update_bits(regmap, MT6323_ISINK_CON(regs->isink_con[0], led->id), MT6323_ISINK_DIM_DUTY_MASK, MT6323_ISINK_DIM_DUTY(duty_hw - 1)); if (ret < 0) goto out; - ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id), + ret = regmap_update_bits(regmap, MT6323_ISINK_CON(regs->isink_con[1], led->id), MT6323_ISINK_DIM_FSEL_MASK, MT6323_ISINK_DIM_FSEL(period - 1)); out: @@ -369,6 +416,8 @@ static int mt6323_led_probe(struct platform_device *pdev) struct mt6397_chip *hw = dev_get_drvdata(dev->parent); struct mt6323_leds *leds; struct mt6323_led *led; + const struct mt6323_regs *regs; + const struct mt6323_hwspec *spec; int ret; unsigned int status; u32 reg; @@ -379,6 +428,9 @@ static int mt6323_led_probe(struct platform_device *pdev) platform_set_drvdata(pdev, leds); leds->dev = dev; + leds->pdata = device_get_match_data(dev); + regs = leds->pdata->regs; + spec = leds->pdata->spec; /* * leds->hw points to the underlying bus for the register @@ -388,11 +440,11 @@ static int mt6323_led_probe(struct platform_device *pdev) mutex_init(&leds->lock); status = MT6323_RG_DRV_32K_CK_PDN; - ret = regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0, + ret = regmap_update_bits(leds->hw->regmap, regs->top_ckpdn[0], MT6323_RG_DRV_32K_CK_PDN_MASK, ~status); if (ret < 0) { dev_err(leds->dev, - "Failed to update MT6323_TOP_CKPDN0 Register\n"); + "Failed to update TOP_CKPDN0 Register\n"); return ret; } @@ -405,7 +457,8 @@ static int mt6323_led_probe(struct platform_device *pdev) goto put_child_node; } - if (reg >= MT6323_MAX_LEDS || leds->led[reg]) { + if (reg >= spec->max_leds || reg >= MAX_SUPPORTED_LEDS || + leds->led[reg]) { dev_err(dev, "Invalid led reg %u\n", reg); ret = -EINVAL; goto put_child_node; @@ -419,7 +472,7 @@ static int mt6323_led_probe(struct platform_device *pdev) leds->led[reg] = led; leds->led[reg]->id = reg; - leds->led[reg]->cdev.max_brightness = MT6323_MAX_BRIGHTNESS; + leds->led[reg]->cdev.max_brightness = spec->max_brightness; leds->led[reg]->cdev.brightness_set_blocking = mt6323_led_set_brightness; leds->led[reg]->cdev.blink_set = mt6323_led_set_blink; @@ -454,13 +507,14 @@ put_child_node: static int mt6323_led_remove(struct platform_device *pdev) { struct mt6323_leds *leds = platform_get_drvdata(pdev); + const struct mt6323_regs *regs = leds->pdata->regs; int i; /* Turn the LEDs off on driver removal. */ for (i = 0 ; leds->led[i] ; i++) mt6323_led_hw_off(&leds->led[i]->cdev); - regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0, + regmap_update_bits(leds->hw->regmap, regs->top_ckpdn[0], MT6323_RG_DRV_32K_CK_PDN_MASK, MT6323_RG_DRV_32K_CK_PDN); @@ -469,8 +523,31 @@ static int mt6323_led_remove(struct platform_device *pdev) return 0; } +static const struct mt6323_regs mt6323_registers = { + .top_ckpdn = (const u16[]){ 0x102, 0x106, 0x10e }, + .num_top_ckpdn = 3, + .top_ckcon = (const u16[]){ 0x120, 0x126 }, + .num_top_ckcon = 2, + .isink_con = (const u16[]){ 0x330, 0x332, 0x334 }, + .num_isink_con = 3, + .isink_max_regs = 4, /* ISINK[0..3] */ + .isink_en_ctrl = 0x356, +}; + +static const struct mt6323_hwspec mt6323_spec = { + .max_period = 10000, + .max_leds = 4, + .max_brightness = 6, + .unit_duty = 3125, +}; + +static const struct mt6323_data mt6323_pdata = { + .regs = &mt6323_registers, + .spec = &mt6323_spec, +}; + static const struct of_device_id mt6323_led_dt_match[] = { - { .compatible = "mediatek,mt6323-led" }, + { .compatible = "mediatek,mt6323-led", .data = &mt6323_pdata}, {}, }; MODULE_DEVICE_TABLE(of, mt6323_led_dt_match); |