// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include "../../flashlight/richtek/rtfled.h" #include "inc/mt6370_pmu.h" #include "inc/mt6370_pmu_bled.h" #define MT6370_PMU_BLED_DRV_VERSION "1.0.2_MTK" struct mt6370_pmu_bled_data { struct rt_fled_dev base; struct mt6370_pmu_chip *chip; struct device *dev; struct platform_device *mt_flash_dev; }; static uint8_t bled_init_data[] = { 0x42, /* MT6370_PMU_REG_BLEN */ 0x89, /* MT6370_PMU_REG_BLBSTCTRL */ 0x00, /* MT6370_PMU_REG_BLPWM */ 0x00, /* MT6370_PMU_REG_BLCTRL */ 0x00, /* MT6370_PMU_REG_BLDIM2 */ 0x00, /* MT6370_PMU_REG_BLDIM1 */ 0x00, /* MT6370_PMU_REG_BLAFH */ 0x00, /* MT6370_PMU_REG_BLFL */ 0x8C, /* MT6370_PMU_REG_BLFLTO */ 0x80, /* MT6370_PMU_REG_BLTORCTRL */ 0xFF, /* MT6370_PMU_REG_BLSTRBCTRL */ 0x00, /* MT6370_PMU_REG_BLAVG */ }; static int mt6370_bled_fled_set_mode(struct rt_fled_dev *fled_dev, enum flashlight_mode mode) { struct mt6370_pmu_bled_data *bled_data = (struct mt6370_pmu_bled_data *)fled_dev; int ret = 0; switch (mode) { case FLASHLIGHT_MODE_OFF: ret = mt6370_pmu_reg_update_bits(bled_data->chip, MT6370_PMU_REG_BLFL, MT6370_BLFLMODE_MASK, 0 << MT6370_BLFLMODE_SHFT); break; case FLASHLIGHT_MODE_TORCH: ret = mt6370_pmu_reg_update_bits(bled_data->chip, MT6370_PMU_REG_BLFL, MT6370_BLFLMODE_MASK, 2 << MT6370_BLFLMODE_SHFT); break; case FLASHLIGHT_MODE_FLASH: break; default: ret = -EINVAL; break; } return ret; } static int mt6370_bled_fled_get_mode(struct rt_fled_dev *fled_dev) { struct mt6370_pmu_bled_data *bled_data = (struct mt6370_pmu_bled_data *)fled_dev; int ret = 0; ret = mt6370_pmu_reg_read(bled_data->chip, MT6370_PMU_REG_BLFL); if (ret < 0) return ret; ret &= MT6370_BLFLMODE_MASK; ret >>= MT6370_BLFLMODE_SHFT; switch (ret) { case 0: ret = FLASHLIGHT_MODE_OFF; break; case 1: ret = FLASHLIGHT_MODE_FLASH; break; case 2: case 3: ret = FLASHLIGHT_MODE_TORCH; break; default: ret = -EINVAL; break; } return ret; } static int mt6370_bled_fled_strobe(struct rt_fled_dev *fled_dev) { struct mt6370_pmu_bled_data *bled_data = (struct mt6370_pmu_bled_data *)fled_dev; int ret = 0; ret = mt6370_bled_fled_set_mode(fled_dev, FLASHLIGHT_MODE_OFF); if (ret < 0) return ret; return mt6370_pmu_reg_update_bits(bled_data->chip, MT6370_PMU_REG_BLFL, MT6370_BLFLMODE_MASK, 1 << MT6370_BLFLMODE_SHFT); } static int mt6370_bled_fled_torch_current_list(struct rt_fled_dev *fled_dev, int selector) { if (selector >= 256) return -EINVAL; return 117 * selector; } static int mt6370_bled_fled_strobe_current_list(struct rt_fled_dev *fled_dev, int selector) { if (selector >= 256) return -EINVAL; return 117 * selector; } static int mt6370_bled_fled_timeout_level_list(struct rt_fled_dev *fled_dev, int selector) { return -EINVAL; } static int mt6370_bled_fled_lv_protection_list(struct rt_fled_dev *fled_dev, int selector) { return -EINVAL; } static int mt6370_bled_fled_strobe_timeout_list(struct rt_fled_dev *fled_dev, int selector) { if (selector >= 128) return -EINVAL; return 16 * selector; } static int mt6370_bled_fled_set_torch_current_sel(struct rt_fled_dev *fled_dev, int selector) { struct mt6370_pmu_bled_data *bled_data = (struct mt6370_pmu_bled_data *)fled_dev; if (selector >= 256) return -EINVAL; return mt6370_pmu_reg_write(bled_data->chip, MT6370_PMU_REG_BLTORCTRL, selector); } static int mt6370_bled_fled_set_strobe_current_sel(struct rt_fled_dev *fled_dev, int selector) { struct mt6370_pmu_bled_data *bled_data = (struct mt6370_pmu_bled_data *)fled_dev; if (selector >= 256) return -EINVAL; return mt6370_pmu_reg_write(bled_data->chip, MT6370_PMU_REG_BLSTRBCTRL, selector); } static int mt6370_bled_fled_set_timeout_level_sel(struct rt_fled_dev *fled_dev, int selector) { return -EINVAL; } static int mt6370_bled_fled_set_lv_protection_sel(struct rt_fled_dev *fled_dev, int selector) { return -EINVAL; } static int mt6370_bled_fled_set_strobe_timeout_sel(struct rt_fled_dev *fled_dev, int selector) { struct mt6370_pmu_bled_data *bled_data = (struct mt6370_pmu_bled_data *)fled_dev; if (selector >= 128) return -EINVAL; return mt6370_pmu_reg_update_bits(bled_data->chip, MT6370_PMU_REG_BLFLTO, MT6370_BLSTRB_TOMASK, selector); } static int mt6370_bled_fled_get_torch_current_sel(struct rt_fled_dev *fled_dev) { struct mt6370_pmu_bled_data *bled_data = (struct mt6370_pmu_bled_data *)fled_dev; return mt6370_pmu_reg_read(bled_data->chip, MT6370_PMU_REG_BLTORCTRL); } static int mt6370_bled_fled_get_strobe_current_sel(struct rt_fled_dev *fled_dev) { struct mt6370_pmu_bled_data *bled_data = (struct mt6370_pmu_bled_data *)fled_dev; return mt6370_pmu_reg_read(bled_data->chip, MT6370_PMU_REG_BLSTRBCTRL); } static int mt6370_bled_fled_get_timeout_level_sel(struct rt_fled_dev *fled_dev) { return -EINVAL; } static int mt6370_bled_fled_get_lv_protection_sel(struct rt_fled_dev *fled_dev) { return -EINVAL; } static int mt6370_bled_fled_get_strobe_timeout_sel(struct rt_fled_dev *fled_dev) { struct mt6370_pmu_bled_data *bled_data = (struct mt6370_pmu_bled_data *)fled_dev; int ret = 0; ret = mt6370_pmu_reg_read(bled_data->chip, MT6370_PMU_REG_BLFLTO); return (ret < 0 ? ret : ret & MT6370_BLSTRB_TOMASK); } static void mt6370_bled_fled_shutdown(struct rt_fled_dev *fled_dev) { mt6370_bled_fled_set_mode(fled_dev, FLASHLIGHT_MODE_OFF); } static struct rt_fled_hal mt6370_bledfl_hal = { .rt_hal_fled_set_mode = mt6370_bled_fled_set_mode, .rt_hal_fled_get_mode = mt6370_bled_fled_get_mode, .rt_hal_fled_strobe = mt6370_bled_fled_strobe, .rt_hal_fled_torch_current_list = mt6370_bled_fled_torch_current_list, .rt_hal_fled_strobe_current_list = mt6370_bled_fled_strobe_current_list, .rt_hal_fled_timeout_level_list = mt6370_bled_fled_timeout_level_list, .rt_hal_fled_lv_protection_list = mt6370_bled_fled_lv_protection_list, .rt_hal_fled_strobe_timeout_list = mt6370_bled_fled_strobe_timeout_list, .rt_hal_fled_set_torch_current_sel = mt6370_bled_fled_set_torch_current_sel, .rt_hal_fled_set_strobe_current_sel = mt6370_bled_fled_set_strobe_current_sel, .rt_hal_fled_set_timeout_level_sel = mt6370_bled_fled_set_timeout_level_sel, .rt_hal_fled_set_lv_protection_sel = mt6370_bled_fled_set_lv_protection_sel, .rt_hal_fled_set_strobe_timeout_sel = mt6370_bled_fled_set_strobe_timeout_sel, .rt_hal_fled_get_torch_current_sel = mt6370_bled_fled_get_torch_current_sel, .rt_hal_fled_get_strobe_current_sel = mt6370_bled_fled_get_strobe_current_sel, .rt_hal_fled_get_timeout_level_sel = mt6370_bled_fled_get_timeout_level_sel, .rt_hal_fled_get_lv_protection_sel = mt6370_bled_fled_get_lv_protection_sel, .rt_hal_fled_get_strobe_timeout_sel = mt6370_bled_fled_get_strobe_timeout_sel, .rt_hal_fled_shutdown = mt6370_bled_fled_shutdown, }; static const struct flashlight_properties mt6370_bledfl_props = { .type = FLASHLIGHT_TYPE_LED, .torch_brightness = 128, /* default torch brightness 15mA */ .torch_max_brightness = 255, .strobe_brightness = 255, /* default strobe brightness 30mA */ .strobe_max_brightness = 255, .strobe_timeout = 208, /* default strobe timeout 208mS */ .alias_name = "mt6370_pmu_bled", }; static void mt6370_pmu_bled_bright_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct mt6370_pmu_bled_data *bled_data = dev_get_drvdata(led_cdev->dev->parent); struct mt6370_pmu_bled_platdata *pdata = dev_get_platdata(bled_data->dev); uint8_t chip_vid = bled_data->chip->chip_vid; uint32_t bright = (pdata->max_bled_brightness << 8) / 255; int ret = 0; dev_dbg(led_cdev->dev, "%s: %d\n", __func__, brightness); bright = (bright * brightness) >> 8; if (chip_vid == MT6372_VENDOR_ID || chip_vid == MT6372C_VENDOR_ID) ret = mt6370_pmu_reg_update_bits(bled_data->chip, MT6370_PMU_REG_BLDIM2, 0x3F, (bright & 0x7) << 3); else ret = mt6370_pmu_reg_update_bits(bled_data->chip, MT6370_PMU_REG_BLDIM2, MT6370_DIM2_MASK, (bright & 0x7)); if (ret < 0) goto out_bright_set; ret = mt6370_pmu_reg_write(bled_data->chip, MT6370_PMU_REG_BLDIM1, (bright >> 3) & MT6370_DIM_MASK); if (ret < 0) goto out_bright_set; /* if choose external enable pin, no effect even config this bit */ ret = mt6370_pmu_reg_update_bits(bled_data->chip, MT6370_PMU_REG_BLEN, MT6370_BLED_EN, brightness > 0 ? MT6370_BLED_EN : ~MT6370_BLED_EN); if (ret < 0) goto out_bright_set; return; out_bright_set: dev_err(led_cdev->dev, "%s error %d\n", __func__, ret); } static struct led_classdev mt6370_pmu_bled_dev = { .name = "mt6370_pmu_bled", .brightness_set = mt6370_pmu_bled_bright_set, }; static irqreturn_t mt6370_pmu_bled_ocp_irq_handler(int irq, void *data) { struct mt6370_pmu_bled_data *bled_data = data; dev_notice(bled_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_bled_ovp_irq_handler(int irq, void *data) { struct mt6370_pmu_bled_data *bled_data = data; dev_notice(bled_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static struct mt6370_pmu_irq_desc mt6370_bled_irq_desc[] = { MT6370_PMU_IRQDESC(bled_ocp), MT6370_PMU_IRQDESC(bled_ovp), }; static void mt6370_pmu_bled_irq_register(struct platform_device *pdev) { struct resource *res; int i, ret = 0; for (i = 0; i < ARRAY_SIZE(mt6370_bled_irq_desc); i++) { if (!mt6370_bled_irq_desc[i].name) continue; res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, mt6370_bled_irq_desc[i].name); if (!res) continue; ret = devm_request_threaded_irq(&pdev->dev, res->start, NULL, mt6370_bled_irq_desc[i].irq_handler, IRQF_TRIGGER_FALLING, mt6370_bled_irq_desc[i].name, platform_get_drvdata(pdev)); if (ret < 0) { dev_err(&pdev->dev, "request %s irq fail\n", res->name); continue; } mt6370_bled_irq_desc[i].irq = res->start; } } static int mt6370_pmu_bled_init_blmode_ctrl( struct mt6370_pmu_bled_data *bled_data) { struct mt6370_pmu_bled_platdata *pdata = dev_get_platdata( bled_data->dev); u8 data = 0; data |= (pdata->bled_curr_scale << MT6370_BLED_CURR_SCALESHFT); data |= (pdata->pwm_lpf_coef << MT6370_PWM_LPF_COEFSHFT); data |= (pdata->pwm_lpf_en << MT6370_PWM_LPF_ENSHFT); data |= (pdata->bled_curr_mode << MT6370_BLED_CURR_MODESHFT); return mt6370_pmu_reg_write(bled_data->chip, MT6370_PMU_REG_BLMODECTRL, data); } static inline int mt6370_pmu_bled_init_register( struct mt6370_pmu_bled_data *bled_data) { int ret = 0; if (bled_data->chip->chip_rev <= 1) bled_init_data[1] |= MT6370_BLED_OVOCSHDNDIS; ret = mt6370_pmu_reg_block_write(bled_data->chip, MT6370_PMU_REG_BLEN, ARRAY_SIZE(bled_init_data), bled_init_data); ret |= mt6370_pmu_bled_init_blmode_ctrl(bled_data); return ret; } static inline int mt6370_pmu_bled_parse_initdata( struct mt6370_pmu_bled_data *bled_data) { struct mt6370_pmu_bled_platdata *pdata = dev_get_platdata( bled_data->dev); uint8_t chip_vid = bled_data->chip->chip_vid; uint32_t bright = (pdata->max_bled_brightness << 8) / 255; if (pdata->ext_en_pin) { bled_init_data[0] &= ~(MT6370_BLED_EN | MT6370_BLED_EXTEN); bled_init_data[0] |= MT6370_BLED_EXTEN; } bled_init_data[0] |= (pdata->chan_en << MT6370_BLED_CHANENSHFT); if (!pdata->map_linear) bled_init_data[0] &= ~(MT6370_BLED_MAPLINEAR); bled_init_data[1] |= (pdata->bl_ovp_level << MT6370_BLED_OVPSHFT); bled_init_data[1] |= (pdata->bl_ocp_level << MT6370_BLED_OCPSHFT); bled_init_data[2] |= (pdata->use_pwm << MT6370_BLED_PWMSHIFT); bled_init_data[2] |= (pdata->pwm_fsample << MT6370_BLED_PWMFSHFT); bled_init_data[2] |= (pdata->pwm_deglitch << MT6370_BLED_PWMDSHFT); bled_init_data[2] |= (pdata->pwm_hys_en << MT6370_BLED_PWMHESHFT); bled_init_data[2] |= (pdata->pwm_hys << MT6370_BLED_PWMHSHFT); bled_init_data[3] |= (pdata->bled_ramptime << MT6370_BLED_RAMPTSHFT); bright = (bright * 255) >> 8; /* Set bled dimension 11 or 14 bits */ if (chip_vid == MT6372_VENDOR_ID || chip_vid == MT6372C_VENDOR_ID) bled_init_data[4] |= ((bright & 0x7) << 3); else bled_init_data[4] |= (bright & 0x7); bled_init_data[5] |= ((bright >> 3) & 0xFF); bled_init_data[7] |= (pdata->bled_flash_ramp << MT6370_BLFLRAMP_SHFT); bled_init_data[10] |= (pdata->pwm_avg_cycle); if (pdata->bled_name) mt6370_pmu_bled_dev.name = pdata->bled_name; return 0; } static inline int mt_parse_dt(struct device *dev) { struct mt6370_pmu_bled_platdata *pdata = dev_get_platdata(dev); struct device_node *np = dev->of_node; const char *name; u32 tmp = 0; if (of_property_read_bool(np, "mt,ext_en_pin")) pdata->ext_en_pin = 1; if (of_property_read_u32(np, "mt,chan_en", &tmp) < 0) pdata->chan_en = 0x0; /* default 4 channel disable */ else pdata->chan_en = tmp; if (of_property_read_bool(np, "mt,map_linear")) pdata->map_linear = 1; if (of_property_read_u32(np, "mt,bl_ovp_level", &tmp) < 0) pdata->bl_ovp_level = 0x3; /* default 29V */ else pdata->bl_ovp_level = tmp; if (of_property_read_u32(np, "mt,bl_ocp_level", &tmp) < 0) pdata->bl_ocp_level = 0x2; /* default 1500mA */ else pdata->bl_ocp_level = tmp; if (of_property_read_bool(np, "mt,use_pwm")) pdata->use_pwm = 1; if (of_property_read_u32(np, "mt,pwm_fsample", &tmp) < 0) pdata->pwm_fsample = 0x1; /* 4MHz */ else pdata->pwm_fsample = tmp; if (of_property_read_u32(np, "mt,pwm_deglitch", &tmp) < 0) pdata->pwm_deglitch = 0x1; else pdata->pwm_deglitch = tmp; if (of_property_read_u32(np, "mt,pwm_hys_en", &tmp) < 0) pdata->pwm_hys_en = 0x1; else pdata->pwm_hys_en = tmp; if (of_property_read_u32(np, "mt,pwm_hys", &tmp) < 0) pdata->pwm_hys = 0x0; /* 1 bit */ else pdata->pwm_hys = tmp; if (of_property_read_u32(np, "mt,pwm_avg_cycle", &tmp) < 0) pdata->pwm_avg_cycle = 0; else pdata->pwm_avg_cycle = tmp; if (of_property_read_u32(np, "mt,bled_ramptime", &tmp) < 0) pdata->bled_ramptime = 0x3; /* default 1ms */ else pdata->bled_ramptime = tmp; if (of_property_read_u32(np, "mt,bled_flash_ramp", &tmp) < 0) pdata->bled_flash_ramp = 0x1; else pdata->bled_flash_ramp = tmp; if (of_property_read_u32(np, "mt,max_bled_brightness", &tmp) < 0) pdata->max_bled_brightness = 1024; else pdata->max_bled_brightness = tmp; if (of_property_read_u32(np, "mt,bled_curr_scale", &tmp) < 0) pdata->bled_curr_scale = 0x0; else pdata->bled_curr_scale = tmp; pr_info("bled_curr_scale = %d\n", pdata->bled_curr_scale); if (of_property_read_u32(np, "mt,pwm_lpf_coef", &tmp) < 0) pdata->pwm_lpf_coef = 0x0; else pdata->pwm_lpf_coef = tmp; if (of_property_read_bool(np, "mt,pwm_lpf_en")) pdata->pwm_lpf_en = 1; if (of_property_read_bool(np, "mt,bled_curr_mode")) pdata->bled_curr_mode = 1; if (of_property_read_string(np, "mt,bled_name", &name) < 0) pdata->bled_name = "mt6370_pmu_bled"; else pdata->bled_name = name; return 0; } static int mt6370_pmu_bled_probe(struct platform_device *pdev) { struct mt6370_pmu_bled_platdata *pdata = dev_get_platdata(&pdev->dev); struct mt6370_pmu_bled_data *bled_data; bool use_dt = pdev->dev.of_node; int ret = 0; pr_info("%s: (%s)\n", __func__, MT6370_PMU_BLED_DRV_VERSION); bled_data = devm_kzalloc(&pdev->dev, sizeof(*bled_data), GFP_KERNEL); if (!bled_data) return -ENOMEM; if (use_dt) { /* DTS used */ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { ret = -ENOMEM; goto out_pdata; } pdev->dev.platform_data = pdata; ret = mt_parse_dt(&pdev->dev); if (ret < 0) { devm_kfree(&pdev->dev, pdata); goto out_pdata; } } else { if (!pdata) { ret = -EINVAL; goto out_pdata; } } bled_data->chip = dev_get_drvdata(pdev->dev.parent); bled_data->dev = &pdev->dev; platform_set_drvdata(pdev, bled_data); ret = mt6370_pmu_bled_parse_initdata(bled_data); if (ret < 0) goto out_init_data; ret = mt6370_pmu_bled_init_register(bled_data); if (ret < 0) goto out_init_reg; if (pdata->use_pwm) mt6370_pmu_bled_dev.default_trigger = "backlight"; mt6370_pmu_bled_dev.brightness = LED_FULL; ret = led_classdev_register(bled_data->dev, &mt6370_pmu_bled_dev); if (ret < 0) { dev_err(&pdev->dev, "register leddev fail\n"); goto out_led_register; } bled_data->base.init_props = &mt6370_bledfl_props; bled_data->base.hal = &mt6370_bledfl_hal; bled_data->base.name = kstrndup(pdata->bled_name, strlen(pdata->bled_name), GFP_KERNEL); bled_data->base.chip_name = "mt6370_pmu_bled"; bled_data->mt_flash_dev = platform_device_register_resndata(bled_data->dev, "rt-flash-led", 2, NULL, 0, NULL, 0); if (IS_ERR(bled_data->mt_flash_dev)) { dev_err(&pdev->dev, "register mt_flash_dev fail\n"); goto out_mt_flash_register; } mt6370_pmu_bled_irq_register(pdev); dev_info(&pdev->dev, "%s successfully\n", __func__); return 0; out_mt_flash_register: led_classdev_unregister(&mt6370_pmu_bled_dev); out_led_register: out_init_reg: out_init_data: out_pdata: devm_kfree(&pdev->dev, bled_data); return ret; } static int mt6370_pmu_bled_remove(struct platform_device *pdev) { struct mt6370_pmu_bled_data *bled_data = platform_get_drvdata(pdev); platform_device_unregister(bled_data->mt_flash_dev); led_classdev_unregister(&mt6370_pmu_bled_dev); dev_info(bled_data->dev, "%s successfully\n", __func__); return 0; } static const struct of_device_id mt_ofid_table[] = { { .compatible = "mediatek,mt6370_pmu_bled", }, { }, }; MODULE_DEVICE_TABLE(of, mt_ofid_table); static const struct platform_device_id mt_id_table[] = { { "mt6370_pmu_bled", 0}, { }, }; MODULE_DEVICE_TABLE(platform, mt_id_table); static struct platform_driver mt6370_pmu_bled = { .driver = { .name = "mt6370_pmu_bled", .owner = THIS_MODULE, .of_match_table = of_match_ptr(mt_ofid_table), }, .probe = mt6370_pmu_bled_probe, .remove = mt6370_pmu_bled_remove, .id_table = mt_id_table, }; module_platform_driver(mt6370_pmu_bled); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MediaTek MT6370 PMU Bled"); MODULE_VERSION(MT6370_PMU_BLED_DRV_VERSION); /* * Release Note * 1.0.2_MTK * (1) Add support for MT6372 * * 1.0.1_MTK * (1) Remove typedef * * 1.0.0_MTK * (1) Initial Release */