// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. */ #include #include #include #include #include #include #include #include #if IS_ENABLED(CONFIG_MTK_CHARGER) #include #endif #if IS_ENABLED(CONFIG_MTK_FLASHLIGHT) #include "flashlight-core.h" #include #endif enum { MT6362_INDICATOR_LED1 = 0, MT6362_INDICATOR_LED4, MT6362_INDICATOR_LEDMAX, }; enum { MT6362_ISINK_FLASHMODE = 0, MT6362_ISINK_BREATHMODE, MT6362_ISINK_REGMODE, }; enum { MT6362_FLASH_LED1 = 0, MT6362_FLASH_LED2, MT6362_FLASH_LEDMAX, }; /* Real register mappings */ #define MT6362_REG_FLEDSTRBTO (0x73) #define MT6362_REG_FLED1ISTRB (0x74) #define MT6362_REG_FLED1ITORCH (0x75) #define MT6362_REG_FLED2ISTRB (0x78) #define MT6362_REG_FLED2ITORCH (0x79) #define MT6362_REG_FLEDEN (0x7E) #define MT6362_REG_RGBEN (0x80) #define MT6362_REG_RGB1ISNK (0x81) #define MT6362_REG_RGBMLISNK (0x84) #define MT6362_REG_RGB1DIM (0x85) #define MT6362_REG_RGB12FREQ (0x89) /* macro replacement mappings */ #define MT6362_ILED1_BRIGHTMAX (16) #define MT6362_ILED4_BRIGHTMAX (32) #define MT6362_ILED1EN_MASK BIT(7) #define MT6362_ILED4EN_MASK BIT(4) #define MT6362_CHGINDEN_MASK BIT(3) #define MT6362_REG_ILED1CURR MT6362_REG_RGB1ISNK #define MT6362_ILED1CURR_MASK (0x0f) #define MT6362_REG_ILED4CURR MT6362_REG_RGBMLISNK #define MT6362_ILED4CURR_MASK (0x1f) #define MT6362_REG_ILED1MODE MT6362_REG_RGB1ISNK #define MT6362_ILED1MODE_MASK (0xc0) #define MT6362_REG_ILED4MODE (0) #define MT6362_ILED4MODE_MASK (0) #define MT6362_PFREQ_MASK (0xe0) #define MT6362_PFREQ_SHIFT (5) #define MT6362_PDUTY_MASK (0xff) #define MT6362_PDUTY_SHIFT (0) #define MT6362_ILED1_MAXUA (24000) #define MT6362_ILED1_STEPUA (2000) #define MT6362_ILED4_MAXUA (150000) #define MT6362_ILED4_STEPUA (5000) #define MT6362_FLED1CSEN_MASK BIT(1) #define MT6362_FLED2CSEN_MASK BIT(0) #define MT6362_FLEDITORCH_MASK (0x1f) #define MT6362_FLEDISTRB_MASK (0x7f) #define MT6362_FLEDUISTRB_MASK BIT(7) #define MT6362_FLEDSTRBTO_MASK (0x7f) #define MT6362_FLEDSTRBEN_MASK BIT(2) #define MT6362_FLEDTORCHEN_MASK BIT(3) #define MT6362_TORCHCURR_MIN (25000) #define MT6362_TORCHCURR_STEP (12500) #define MT6362_TORCHCURR_MAX (400000) #define MT6362_STRBCURR_MIN (25000) #define MT6362_STRBCURR_STEP (6250) #define MT6362_STRBCURR_MAX (1500000) #define MT6362_STRBUISTRB_BOUND (50000) /* (STRBUISTRB_BOUND - STRBCURR_MIN) / STRBCURR_STEP */ #define MT6362_STRBUSTRB_BDSTEP (4) #define MT6362_STRBTIMEOUT_MIN (64000) #define MT6362_STRBTIMEOUT_STEP (32000) #define MT6362_STRBTIMEOUT_MAX (2432000) struct mt6362_indicator_cdev { struct led_classdev cdev; struct device_node *np; struct fwnode_handle *fwnode; int idx; u32 enable_reg; u32 enable_mask; u32 currsel_reg; u32 currsel_mask; u32 mode_reg; u32 mode_mask; }; struct mt6362_flash_cdev { struct led_classdev_flash flash; struct v4l2_flash *v4l2_flash; struct device_node *np; struct fwnode_handle *fwnode; int idx; u32 source_enable_reg; u32 source_enable_mask; u32 torch_bright_reg; u32 torch_bright_mask; u32 strobe_bright_reg; u32 strobe_bright_mask; u32 faults; #if IS_ENABLED(CONFIG_MTK_FLASHLIGHT) struct flashlight_device_id dev_id; #endif }; #if IS_ENABLED(CONFIG_MTK_FLASHLIGHT) static struct led_classdev_flash *mt6362_flash_class[MT6362_FLASH_LEDMAX]; #define CHARGER_SUPPLY_NAME "charger_port1" /* is decrease voltage */ static int is_decrease_voltage; static DEFINE_MUTEX(mt6362_mutex); /* define usage count */ static int fd_use_count; #endif struct mt6362_leds_data { struct device *dev; struct regmap *regmap; struct mt6362_indicator_cdev indicators[MT6362_INDICATOR_LEDMAX]; struct mt6362_flash_cdev flashleds[MT6362_FLASH_LEDMAX]; unsigned long fl_torch_flags; unsigned long fl_strb_flags; struct charger_device *chg_dev; }; struct mt6362_led_irqt { const char *name; irq_handler_t irqh; }; static int mt6362_iled_brightness_set(struct led_classdev *cdev, enum led_brightness brightness) { struct mt6362_leds_data *data = dev_get_drvdata(cdev->dev->parent); struct mt6362_indicator_cdev *mtcdev = (void *)cdev; s32 val, shift = ffs(mtcdev->currsel_mask) - 1; int rv; if (mtcdev->idx == MT6362_INDICATOR_LED1) { /* Enable CHGIND software mode */ rv = regmap_update_bits(data->regmap, MT6362_REG_RGBEN, MT6362_CHGINDEN_MASK, MT6362_CHGINDEN_MASK); if (rv) return rv; } if (brightness == LED_OFF) { return regmap_update_bits(data->regmap, mtcdev->enable_reg, mtcdev->enable_mask, 0); } val = brightness - 1; rv = regmap_update_bits(data->regmap, mtcdev->currsel_reg, mtcdev->currsel_mask, val << shift); if (rv) return rv; return regmap_update_bits(data->regmap, mtcdev->enable_reg, mtcdev->enable_mask, mtcdev->enable_mask); } static enum led_brightness mt6362_iled_brightness_get(struct led_classdev *cdev) { struct mt6362_leds_data *data = dev_get_drvdata(cdev->dev->parent); struct mt6362_indicator_cdev *mtcdev = (void *)cdev; unsigned int val = 0, shift = ffs(mtcdev->currsel_mask) - 1; int rv; rv = regmap_read(data->regmap, mtcdev->enable_reg, &val); if (rv) return rv; if (!(val & mtcdev->enable_mask)) return LED_OFF; rv = regmap_read(data->regmap, mtcdev->currsel_reg, &val); if (rv) return rv; val &= mtcdev->currsel_mask; val >>= shift; /* 0 is off, plus 1 to return back */ return (val + 1); } static int mt6362_iled_blink_set(struct led_classdev *cdev, unsigned long *don, unsigned long *doff) { struct mt6362_leds_data *data = dev_get_drvdata(cdev->dev->parent); struct mt6362_indicator_cdev *mtcdev = (void *)cdev; const unsigned int dim_freqs[] = { 4, 8, 250, 500, 1000, 2000, 4000, 8000 }; unsigned long sum = *don + *doff; int freq, duty, shift, rv; if (!mtcdev->mode_reg) return -EOPNOTSUPP; if (!*don && !*doff) *don = *doff = 500; for (freq = 0; freq < ARRAY_SIZE(dim_freqs); freq++) { if (sum <= dim_freqs[freq]) break; } if (freq == ARRAY_SIZE(dim_freqs)) { dev_warn(cdev->dev, "no suited pwm freq, config to 0.125Hz\n"); freq = ARRAY_SIZE(dim_freqs) - 1; } freq = ARRAY_SIZE(dim_freqs) - freq - 1; rv = regmap_update_bits(data->regmap, MT6362_REG_RGB12FREQ, MT6362_PFREQ_MASK, freq << MT6362_PFREQ_SHIFT); if (rv) return rv; duty = 255 * (*don) * 1000 / sum; duty /= 1000; rv = regmap_update_bits(data->regmap, MT6362_REG_RGB1DIM, MT6362_PDUTY_MASK, duty << MT6362_PDUTY_SHIFT); if (rv) return rv; shift = ffs(mtcdev->mode_mask) - 1; return regmap_update_bits(data->regmap, mtcdev->mode_reg, mtcdev->mode_mask, MT6362_ISINK_FLASHMODE << shift); } static int mt6362_fled_brightness_set(struct led_classdev *cdev, enum led_brightness brightness) { struct mt6362_leds_data *data = dev_get_drvdata(cdev->dev->parent); struct mt6362_flash_cdev *mtcdev = (void *)lcdev_to_flcdev(cdev); int shift = ffs(mtcdev->torch_bright_mask) - 1, rv; dev_info(cdev->dev, "%s brightness:%d\n", __func__, brightness); if (data->fl_strb_flags) { dev_err(cdev->dev, "Disable all leds strobe [%lu]\n", data->fl_strb_flags); return -EINVAL; } if (brightness == LED_OFF) { rv = regmap_update_bits(data->regmap, mtcdev->source_enable_reg, mtcdev->source_enable_mask, 0); if (rv) return rv; clear_bit(mtcdev->idx, &data->fl_torch_flags); if (!data->fl_torch_flags) { /* if no user, turn torch mode to off */ rv = regmap_update_bits(data->regmap, MT6362_REG_FLEDEN, MT6362_FLEDTORCHEN_MASK, 0); if (rv) return rv; } return 0; } #if IS_ENABLED(CONFIG_MTK_FLASHLIGHT_DLPT) flashlight_kicker_pbm(1); #endif #if IS_ENABLED(CONFIG_MTK_FLASHLIGHT_PT) if (flashlight_pt_is_low()) { dev_info(cdev->dev, "pt is low\n"); return 0; } #endif brightness -= 1; rv = regmap_update_bits(data->regmap, mtcdev->torch_bright_reg, mtcdev->torch_bright_mask, brightness << shift); if (rv) return rv; rv = regmap_update_bits(data->regmap, mtcdev->source_enable_reg, mtcdev->source_enable_mask, mtcdev->source_enable_mask); if (rv) return rv; /* config torch mode */ rv = regmap_update_bits(data->regmap, MT6362_REG_FLEDEN, MT6362_FLEDTORCHEN_MASK, MT6362_FLEDTORCHEN_MASK); if (rv) return rv; mtcdev->faults = 0; set_bit(mtcdev->idx, &data->fl_torch_flags); return 0; } static enum led_brightness mt6362_fled_brightness_get(struct led_classdev *cdev) { struct mt6362_leds_data *data = dev_get_drvdata(cdev->dev->parent); struct mt6362_flash_cdev *mtcdev = (void *)lcdev_to_flcdev(cdev); unsigned int val = 0; int rv; if (!test_bit(mtcdev->idx, &data->fl_torch_flags)) return LED_OFF; rv = regmap_read(data->regmap, mtcdev->torch_bright_reg, &val); if (rv) return rv; val &= mtcdev->torch_bright_mask; val >>= (ffs(mtcdev->torch_bright_mask) - 1); return (val + 1); } static int _mt6362_fled_flash_brightness_set(struct led_classdev_flash *flcdev, u32 brightness) { struct led_classdev *lcdev = &flcdev->led_cdev; struct mt6362_leds_data *data = dev_get_drvdata(lcdev->dev->parent); struct mt6362_flash_cdev *mtcdev = (void *)flcdev; const struct led_flash_setting *fs = &flcdev->brightness; int val; dev_info(lcdev->dev, "%s brightness:%d\n", __func__, brightness); if (brightness > fs->max) brightness = fs->max; val = (brightness - fs->min) / fs->step; if (val < MT6362_STRBUSTRB_BDSTEP) val |= MT6362_FLEDUISTRB_MASK; else val = (val - MT6362_STRBUSTRB_BDSTEP + 1) / 2; return regmap_write(data->regmap, mtcdev->strobe_bright_reg, val); } static int mt6362_fled_flash_brightness_set(struct led_classdev_flash *flcdev, u32 brightness) { struct led_classdev *lcdev = &flcdev->led_cdev; dev_info(lcdev->dev, "%s brightness:%d\n", __func__, brightness); return 0; } static int mt6362_fled_strobe_set(struct led_classdev_flash *flcdev, bool state) { struct led_classdev *lcdev = &flcdev->led_cdev; struct mt6362_leds_data *data = dev_get_drvdata(lcdev->dev->parent); const struct led_flash_setting *fs = &flcdev->brightness; struct mt6362_flash_cdev *mtcdev = (void *)flcdev; int rv; #if IS_ENABLED(CONFIG_MTK_CHARGER) union charger_propval chg_propval; #endif dev_info(lcdev->dev, "%s state:%d\n", __func__, state); if (!(state ^ test_bit(mtcdev->idx, &data->fl_strb_flags))) { dev_dbg(lcdev->dev, "strobe no change [%lu]\n", data->fl_strb_flags); return 0; } if (data->fl_torch_flags) { dev_err(lcdev->dev, "Disable all leds torch [%lu]\n", data->fl_torch_flags); return -EINVAL; } #if IS_ENABLED(CONFIG_MTK_FLASHLIGHT_DLPT) flashlight_kicker_pbm(state); #endif #if IS_ENABLED(CONFIG_MTK_FLASHLIGHT_PT) if (flashlight_pt_is_low()) { dev_info(lcdev->dev, "pt is low\n"); return 0; } #endif rv = regmap_update_bits(data->regmap, mtcdev->source_enable_reg, mtcdev->source_enable_mask, state ? mtcdev->source_enable_mask : 0); if (rv) return rv; if (state) { rv = _mt6362_fled_flash_brightness_set(flcdev, fs->val); if (rv) return rv; if (!data->fl_strb_flags) { #if IS_ENABLED(CONFIG_MTK_CHARGER) chg_propval.intval = 1; rv = charger_dev_set_property(data->chg_dev, CHARGER_PROP_BLEED_DISCHARGE, &chg_propval); if (rv) return rv; #endif rv = regmap_update_bits(data->regmap, MT6362_REG_FLEDEN, MT6362_FLEDSTRBEN_MASK, MT6362_FLEDSTRBEN_MASK); if (rv) return rv; usleep_range(5000, 6000); } mtcdev->faults = 0; set_bit(mtcdev->idx, &data->fl_strb_flags); } else { clear_bit(mtcdev->idx, &data->fl_strb_flags); if (!data->fl_strb_flags) { rv = regmap_update_bits(data->regmap, MT6362_REG_FLEDEN, MT6362_FLEDSTRBEN_MASK, 0); if (rv) return rv; usleep_range(400, 500); #if IS_ENABLED(CONFIG_MTK_CHARGER) chg_propval.intval = 0; rv = charger_dev_set_property(data->chg_dev, CHARGER_PROP_BLEED_DISCHARGE, &chg_propval); if (rv) return rv; #endif } rv = _mt6362_fled_flash_brightness_set(flcdev, fs->min); if (rv) return rv; } return 0; } static int mt6362_fled_strobe_get(struct led_classdev_flash *flcdev, bool *state) { struct led_classdev *lcdev = &flcdev->led_cdev; struct mt6362_leds_data *data = dev_get_drvdata(lcdev->dev->parent); struct mt6362_flash_cdev *mtcdev = (void *)flcdev; *state = test_bit(mtcdev->idx, &data->fl_strb_flags); return 0; } static int mt6362_fled_timeout_set(struct led_classdev_flash *flcdev, u32 timeout) { struct led_classdev *lcdev = &flcdev->led_cdev; struct mt6362_leds_data *data = dev_get_drvdata(lcdev->dev->parent); const struct led_flash_setting *fs = &flcdev->timeout; int shift = ffs(MT6362_FLEDSTRBTO_MASK) - 1, val; dev_info(lcdev->dev, "%s timeout:%u\n", __func__, timeout); if (timeout > fs->max) timeout = fs->max; val = (timeout - fs->min) / fs->step; return regmap_update_bits(data->regmap, MT6362_REG_FLEDSTRBTO, MT6362_FLEDSTRBTO_MASK, val << shift); } static int mt6362_fled_fault_get(struct led_classdev_flash *flcdev, u32 *fault) { struct mt6362_flash_cdev *mtcdev = (void *)flcdev; *fault = mtcdev->faults; return 0; } static const struct led_flash_ops mt6362_fled_ctrl_ops = { .flash_brightness_set = mt6362_fled_flash_brightness_set, .strobe_set = mt6362_fled_strobe_set, .strobe_get = mt6362_fled_strobe_get, .timeout_set = mt6362_fled_timeout_set, .fault_get = mt6362_fled_fault_get, }; static int mt6362_fled_external_strobe_set(struct v4l2_flash *v4l2_flash, bool enable) { struct led_classdev_flash *flcdev = v4l2_flash->fled_cdev; struct mt6362_flash_cdev *mtcdev = (void *)flcdev; struct led_classdev *lcdev = &flcdev->led_cdev; struct mt6362_leds_data *data = dev_get_drvdata(lcdev->dev->parent); unsigned int val; int rv; if (!(enable ^ test_bit(mtcdev->idx, &data->fl_strb_flags))) { dev_dbg(lcdev->dev, "strobe no change [%lu]\n", data->fl_strb_flags); return 0; } if (data->fl_torch_flags) { dev_err(lcdev->dev, "Disable all leds torch [%lu]\n", data->fl_torch_flags); return -EINVAL; } val = enable ? mtcdev->source_enable_mask : 0; rv = regmap_update_bits(data->regmap, mtcdev->source_enable_reg, mtcdev->source_enable_mask, val); if (rv) return rv; if (enable) { set_bit(mtcdev->idx, &data->fl_strb_flags); mtcdev->faults = 0; } else clear_bit(mtcdev->idx, &data->fl_strb_flags); return 0; } static const struct v4l2_flash_ops v4l2_flash_ops = { .external_strobe_set = mt6362_fled_external_strobe_set, }; #define MT6362_INDICATOR_DESC(_id) \ {\ .cdev = {\ .name = "mt6362_isink" #_id,\ .max_brightness = MT6362_ILED##_id##_BRIGHTMAX,\ .brightness_set_blocking = mt6362_iled_brightness_set,\ .brightness_get = mt6362_iled_brightness_get,\ .blink_set = mt6362_iled_blink_set,\ },\ .idx = MT6362_INDICATOR_LED##_id,\ .enable_reg = MT6362_REG_RGBEN,\ .enable_mask = MT6362_ILED##_id##EN_MASK,\ .currsel_reg = MT6362_REG_ILED##_id##CURR,\ .currsel_mask = MT6362_ILED##_id##CURR_MASK,\ .mode_reg = MT6362_REG_ILED##_id##MODE,\ .mode_mask = MT6362_ILED##_id##MODE_MASK,\ } /* MT6362 only support ISINK1 -> CHGIND, ISINK4 -> MoonLight */ static const struct mt6362_indicator_cdev default_ileds[] = { MT6362_INDICATOR_DESC(1), MT6362_INDICATOR_DESC(4), }; #define MT6362_FLASH_DESC(_id) \ {\ .flash = {\ .led_cdev = {\ .name = "mt6362_flash_ch" #_id,\ .max_brightness = 7,\ .brightness_set_blocking = mt6362_fled_brightness_set,\ .brightness_get = mt6362_fled_brightness_get,\ .flags = LED_DEV_CAP_FLASH,\ },\ .brightness = {\ .min = MT6362_STRBCURR_MIN,\ .step = MT6362_STRBCURR_STEP,\ .max = MT6362_STRBCURR_MAX,\ },\ .timeout = {\ .min = MT6362_STRBTIMEOUT_MIN,\ .step = MT6362_STRBTIMEOUT_STEP,\ .max = MT6362_STRBTIMEOUT_MAX,\ },\ .ops = &mt6362_fled_ctrl_ops,\ },\ .idx = MT6362_FLASH_LED##_id,\ .source_enable_reg = MT6362_REG_FLEDEN,\ .source_enable_mask = MT6362_FLED##_id##CSEN_MASK,\ .torch_bright_reg = MT6362_REG_FLED##_id##ITORCH,\ .torch_bright_mask = MT6362_FLEDITORCH_MASK,\ .strobe_bright_reg = MT6362_REG_FLED##_id##ISTRB,\ .strobe_bright_mask = MT6362_FLEDISTRB_MASK,\ } static const struct mt6362_flash_cdev default_fleds[] = { MT6362_FLASH_DESC(1), MT6362_FLASH_DESC(2), }; #define MT6362_FLED_IRQH(_name, _ledbits, _event) \ static irqreturn_t mt6362_##_name##_irq_handler(int irq, void *irqdata)\ {\ struct mt6362_leds_data *data = irqdata;\ unsigned long ledbits = _ledbits, offset;\ for_each_set_bit(offset, &ledbits, MT6362_FLASH_LEDMAX) {\ struct mt6362_flash_cdev *mtcdev = data->flashleds + offset;\ mtcdev->faults |= _event;\ } \ return IRQ_HANDLED;\ } MT6362_FLED_IRQH(fled_lvf_evt, BIT(MT6362_FLASH_LED1) | BIT(MT6362_FLASH_LED2), LED_FAULT_OVER_CURRENT); MT6362_FLED_IRQH(fled_lbp_evt, BIT(MT6362_FLASH_LED1) | BIT(MT6362_FLASH_LED2), LED_FAULT_INPUT_VOLTAGE); MT6362_FLED_IRQH(fled_chgvinovp_evt, BIT(MT6362_FLASH_LED1) | BIT(MT6362_FLASH_LED2), LED_FAULT_INPUT_VOLTAGE); MT6362_FLED_IRQH(fled1_short_evt, BIT(MT6362_FLASH_LED1), LED_FAULT_SHORT_CIRCUIT); MT6362_FLED_IRQH(fled2_short_evt, BIT(MT6362_FLASH_LED2), LED_FAULT_SHORT_CIRCUIT); MT6362_FLED_IRQH(fled1_strbto_evt, BIT(MT6362_FLASH_LED1), LED_FAULT_TIMEOUT); MT6362_FLED_IRQH(fled2_strbto_evt, BIT(MT6362_FLASH_LED2), LED_FAULT_TIMEOUT); #define MT6362_IRQ_DECLARE(_name) \ {\ .name = #_name,\ .irqh = mt6362_##_name##_irq_handler,\ } static const struct mt6362_led_irqt irqts[] = { MT6362_IRQ_DECLARE(fled_lvf_evt), MT6362_IRQ_DECLARE(fled_lbp_evt), MT6362_IRQ_DECLARE(fled_chgvinovp_evt), MT6362_IRQ_DECLARE(fled1_short_evt), MT6362_IRQ_DECLARE(fled2_short_evt), MT6362_IRQ_DECLARE(fled1_strbto_evt), MT6362_IRQ_DECLARE(fled2_strbto_evt), }; static int mt6362_leds_irq_register(struct platform_device *pdev, void *irqdata) { int i, irq, rv; for (i = 0; i < ARRAY_SIZE(irqts); i++) { irq = platform_get_irq_byname(pdev, irqts[i].name); if (irq <= 0) continue; rv = devm_request_threaded_irq(&pdev->dev, irq, NULL, irqts[i].irqh, 0, NULL, irqdata); if (rv) { dev_err(&pdev->dev, "failed to request irq [%s]\n", irqts[i].name); return rv; } } return 0; } static void mt6362_init_v4l2_flash_config(struct led_classdev_flash *flcdev, struct v4l2_flash_config *v4l2_config) { struct led_classdev *lcdev = &flcdev->led_cdev; struct led_flash_setting *s = &v4l2_config->intensity; snprintf(v4l2_config->dev_name, sizeof(v4l2_config->dev_name), "%s", lcdev->name); s->min = MT6362_TORCHCURR_MIN; s->step = MT6362_TORCHCURR_STEP; s->val = s->max = MT6362_TORCHCURR_MIN + (lcdev->max_brightness - 1) * MT6362_TORCHCURR_STEP; v4l2_config->flash_faults = LED_FAULT_OVER_CURRENT | LED_FAULT_INPUT_VOLTAGE | LED_FAULT_SHORT_CIRCUIT | LED_FAULT_TIMEOUT; v4l2_config->has_external_strobe = 1; } static void mt6362_init_flash_config(struct led_classdev_flash *flcdev) { struct led_flash_setting *s; s = &flcdev->brightness; s->val = s->max; s = &flcdev->timeout; s->val = s->max; } static enum led_brightness mt6362_indicator_brightness_level(unsigned int id, int max_uA) { int rv; switch (id) { default: case MT6362_INDICATOR_LED1: if (max_uA > MT6362_ILED1_MAXUA) max_uA = MT6362_ILED1_MAXUA; rv = max_uA / MT6362_ILED1_STEPUA; break; case MT6362_INDICATOR_LED4: if (max_uA > MT6362_ILED4_MAXUA) max_uA = MT6362_ILED4_MAXUA; rv = max_uA / MT6362_ILED4_STEPUA; break; } /* 0 -> off, plus 1 to return back */ return (rv + 1); } static enum led_brightness mt6362_torch_brightness_level(unsigned int id, int max_uA) { if (max_uA > MT6362_TORCHCURR_MAX) max_uA = MT6362_TORCHCURR_MAX; /* 0 -> off, plus 1 to return back */ return (max_uA - MT6362_TORCHCURR_MIN) / MT6362_TORCHCURR_STEP + 1; } #if IS_ENABLED(CONFIG_MTK_FLASHLIGHT) /****************************************************************************** * Charger power supply class *****************************************************************************/ static int mt6362_high_voltage_supply(int enable) { union power_supply_propval prop; static struct power_supply *chg_psy; int ret; if (chg_psy == NULL) chg_psy = power_supply_get_by_name("mtk-master-charger"); if (chg_psy == NULL || IS_ERR(chg_psy)) { pr_notice("%s Couldn't get chg_psy\n", __func__); ret = -1; } else { prop.intval = enable; ret = power_supply_set_property(chg_psy, POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop); pr_notice("%s enable_hv:%d\n", __func__, prop.intval); power_supply_changed(chg_psy); } return ret; } static int mt6362_set_scenario(int scenario) { mutex_lock(&mt6362_mutex); if (scenario & FLASHLIGHT_SCENARIO_CAMERA_MASK) { if (!is_decrease_voltage) { pr_info("Decrease voltage level.\n"); mt6362_high_voltage_supply(0); is_decrease_voltage = 1; } } else { if (is_decrease_voltage) { pr_info("Increase voltage level.\n"); mt6362_high_voltage_supply(1); is_decrease_voltage = 0; } } mutex_unlock(&mt6362_mutex); return 0; } static int mt6362_open(void) { mutex_lock(&mt6362_mutex); fd_use_count++; pr_debug("open driver: %d\n", fd_use_count); mutex_unlock(&mt6362_mutex); return 0; } static int mt6362_release(void) { mutex_lock(&mt6362_mutex); fd_use_count--; pr_debug("close driver: %d\n", fd_use_count); /* If camera NE, we need to enable pe by ourselves*/ if (fd_use_count == 0 && is_decrease_voltage) { pr_info("Increase voltage level.\n"); mt6362_high_voltage_supply(1); is_decrease_voltage = 0; } mutex_unlock(&mt6362_mutex); return 0; } static int mt6362_ioctl(unsigned int cmd, unsigned long arg) { struct flashlight_dev_arg *fl_arg; int channel; struct led_classdev_flash *flcdev; struct led_classdev *lcdev; fl_arg = (struct flashlight_dev_arg *)arg; channel = fl_arg->channel; if (channel >= MT6362_FLASH_LEDMAX || channel < 0) { pr_info("Failed with error channel\n"); return -EINVAL; } flcdev = mt6362_flash_class[channel]; if (flcdev == NULL) { pr_info("Get flcdev failed\n"); return -EINVAL; } lcdev = &flcdev->led_cdev; if (lcdev == NULL) { pr_info("Get lcdev failed\n"); return -EINVAL; } switch (cmd) { case FLASH_IOC_SET_ONOFF: pr_info("FLASH_IOC_SET_ONOFF(%d): %d\n", channel, (int)fl_arg->arg); mt6362_fled_brightness_set(lcdev, (int)fl_arg->arg); break; case FLASH_IOC_SET_SCENARIO: pr_debug("FLASH_IOC_SET_SCENARIO(%d): %d\n", channel, (int)fl_arg->arg); mt6362_set_scenario(fl_arg->arg); break; default: dev_info(lcdev->dev, "No such command and arg(%d): (%d, %d)\n", channel, _IOC_NR(cmd), (int)fl_arg->arg); return -ENOTTY; } return 0; } static ssize_t mt6362_strobe_store(struct flashlight_arg arg) { struct led_classdev_flash *flcdev; struct led_classdev *lcdev; flcdev = mt6362_flash_class[arg.channel]; lcdev = &flcdev->led_cdev; mt6362_fled_brightness_set(lcdev, 1); msleep(arg.dur); mt6362_fled_brightness_set(lcdev, 0); return 0; } static int mt6362_set_driver(int set) { return 0; } static struct flashlight_operations mt6362_ops = { mt6362_open, mt6362_release, mt6362_ioctl, mt6362_strobe_store, mt6362_set_driver }; #endif static int mt6362_leds_parse_dt(struct platform_device *pdev, struct mt6362_leds_data *data) { struct device_node *np = pdev->dev.of_node, *child; const char *iled_name = "indicator", *fled_name = "flash"; int rv; dev_info(&pdev->dev, "mt6362 led parse dt\n"); if (!np) return 0; /* indicator led dt parsing */ for (child = of_get_child_by_name(np, iled_name); child; child = of_find_node_by_name(child, iled_name)) { struct mt6362_indicator_cdev *mtcdev; u32 reg = 0, max_uA = 0; rv = of_property_read_u32(child, "reg", ®); if (rv) continue; if (reg >= MT6362_INDICATOR_LEDMAX) { dev_err(&pdev->dev, "not valid reg property\n"); return -EINVAL; } mtcdev = data->indicators + reg; mtcdev->np = child; mtcdev->fwnode = &child->fwnode; of_property_read_string(child, "label", &mtcdev->cdev.name); of_property_read_string(child, "linux,default-trigger", &mtcdev->cdev.default_trigger); rv = of_property_read_u32(child, "led-max-microamp", &max_uA); if (rv == 0) { mtcdev->cdev.max_brightness = mt6362_indicator_brightness_level(reg, max_uA); } } for (child = of_get_child_by_name(np, fled_name); child; child = of_find_node_by_name(child, fled_name)) { struct mt6362_flash_cdev *mtcdev; struct led_classdev_flash *flcdev; u32 reg = 0, max_uA = 0; rv = of_property_read_u32(child, "reg", ®); if (rv) continue; if (reg >= MT6362_FLASH_LEDMAX) { dev_err(&pdev->dev, "not valid reg property\n"); return -EINVAL; } mtcdev = data->flashleds + reg; flcdev = &mtcdev->flash; mtcdev->np = child; mtcdev->fwnode = &child->fwnode; of_property_read_string(child, "label", &flcdev->led_cdev.name); of_property_read_string(child, "linux,default-trigger", &flcdev->led_cdev.default_trigger); rv = of_property_read_u32(child, "led-max-microamp", &max_uA); if (rv == 0) { flcdev->led_cdev.max_brightness = mt6362_torch_brightness_level(reg, max_uA); } of_property_read_u32(child, "flash-max-microamp", &flcdev->brightness.max); of_property_read_u32(child, "flash-max-timeout-us", &flcdev->timeout.max); #if IS_ENABLED(CONFIG_MTK_FLASHLIGHT) of_property_read_u32(child, "type", &mtcdev->dev_id.type); of_property_read_u32(child, "ct", &mtcdev->dev_id.ct); of_property_read_u32(child, "part", &mtcdev->dev_id.part); snprintf(mtcdev->dev_id.name, FLASHLIGHT_NAME_SIZE, flcdev->led_cdev.name); mtcdev->dev_id.channel = reg; mt6362_flash_class[reg] = flcdev; mtcdev->dev_id.decouple = 0; dev_info(&pdev->dev, "Parse dt (type,ct,part,name,channel,decouple)=(%d,%d,%d,%s,%d,%d).\n", mtcdev->dev_id.type, mtcdev->dev_id.ct, mtcdev->dev_id.part, mtcdev->dev_id.name, mtcdev->dev_id.channel, mtcdev->dev_id.decouple); if (flashlight_dev_register_by_device_id(&mtcdev->dev_id, &mt6362_ops)) return -EFAULT; #endif } dev_info(&pdev->dev, "mt6362 led parse dt done\n"); return 0; } static int mt6362_leds_probe(struct platform_device *pdev) { struct mt6362_leds_data *data; struct led_init_data init_data = {}; int i, rv; dev_info(&pdev->dev, "mt6362 led probe\n"); data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->dev = &pdev->dev; memcpy(data->indicators, default_ileds, sizeof(default_ileds)); memcpy(data->flashleds, default_fleds, sizeof(default_fleds)); platform_set_drvdata(pdev, data); data->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!data->regmap) { dev_err(&pdev->dev, "failed to allocate regmap\n"); return -ENODEV; } rv = mt6362_leds_parse_dt(pdev, data); if (rv) { dev_err(&pdev->dev, "faled to parse dt\n"); return rv; } /* indicator led register */ for (i = 0; i < MT6362_INDICATOR_LEDMAX; i++) { struct mt6362_indicator_cdev *mtcdev = data->indicators + i; init_data.fwnode = mtcdev->fwnode; rv = devm_led_classdev_register_ext(&pdev->dev, &mtcdev->cdev, &init_data); if (rv) { dev_err(&pdev->dev, "failed to register %d ileds\n", i); return rv; } } /* flash led register */ for (i = 0; i < MT6362_FLASH_LEDMAX; i++) { struct mt6362_flash_cdev *mtcdev = data->flashleds + i; struct led_classdev_flash *flcdev = &mtcdev->flash; struct v4l2_flash_config v4l2_config; /* config strobe default bright to mininum 25mA */ rv = regmap_write(data->regmap, mtcdev->strobe_bright_reg, MT6362_FLEDUISTRB_MASK); if (rv) return rv; mt6362_init_flash_config(flcdev); rv = led_classdev_flash_register(&pdev->dev, flcdev); if (rv) { dev_err(&pdev->dev, "failed to register %d fleds\n", i); return rv; } mt6362_init_v4l2_flash_config(flcdev, &v4l2_config); mtcdev->v4l2_flash = v4l2_flash_init(&pdev->dev, of_fwnode_handle(mtcdev->np), &mtcdev->flash, &v4l2_flash_ops, &v4l2_config); if (IS_ERR(mtcdev->v4l2_flash)) { dev_err(&pdev->dev, "failed to register %d v4l2\n", i); rv = PTR_ERR(mtcdev->v4l2_flash); return rv; } } #if IS_ENABLED(CONFIG_MTK_FLASHLIGHT) /* clear attributes */ fd_use_count = 0; is_decrease_voltage = 0; #endif rv = mt6362_leds_irq_register(pdev, data); if (rv) { dev_err(&pdev->dev, "failed to register led irqs\n"); return rv; } #if IS_ENABLED(CONFIG_MTK_CHARGER) data->chg_dev = get_charger_by_name("primary_chg"); if (!data->chg_dev) { dev_err(&pdev->dev, "%s: can't find primary charger\n", __func__); return -EINVAL; } #endif dev_info(&pdev->dev, "mt6362 probe done\n"); return 0; } static int mt6362_leds_remove(struct platform_device *pdev) { #if IS_ENABLED(CONFIG_MTK_FLASHLIGHT) struct mt6362_leds_data *data = platform_get_drvdata(pdev); struct mt6362_flash_cdev *mtcdev; mtcdev = data->flashleds + MT6362_FLASH_LED1; flashlight_dev_unregister_by_device_id(&mtcdev->dev_id); mtcdev = data->flashleds + MT6362_FLASH_LED2; flashlight_dev_unregister_by_device_id(&mtcdev->dev_id); #endif return 0; } static const struct of_device_id __maybe_unused mt6362_leds_ofid_tbls[] = { { .compatible = "mediatek,mt6362-leds", }, { }, }; MODULE_DEVICE_TABLE(of, mt6362_leds_ofid_tbls); static struct platform_driver mt6362_leds_driver = { .driver = { .name = "mt6362-leds", .of_match_table = of_match_ptr(mt6362_leds_ofid_tbls), }, .probe = mt6362_leds_probe, .remove = mt6362_leds_remove, }; module_platform_driver(mt6362_leds_driver); MODULE_AUTHOR("ChiYuan Huang "); MODULE_DESCRIPTION("MT6362 SPMI LEDS Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION("1.0.0");