// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. */ #include #include #include #include #include #include #include "../inc/mt6360_pmu.h" #include "../inc/mt6360_pmu_fled.h" #include "../inc/mt6360_pmu_chg.h" #include "../../../flashlight/richtek/rtfled.h" #define MT6360_PMU_FLED_DRV_VERSION "1.0.1_MTK" static DEFINE_MUTEX(fled_lock); static bool mt6360_fled_inited; static enum flashlight_mode mt6360_global_mode = FLASHLIGHT_MODE_OFF; static u8 mt6360_fled_on; enum mt6360_fled_dev { MT6360_FLED1 = 0, MT6360_FLED2, MT6360_FLED_NUM, }; struct mt6360_pmu_fled_info { struct rt_fled_dev base; struct device *dev; struct mt6360_pmu_info *mpi; int id; struct platform_device *fled_dev[MT6360_FLED_NUM]; struct platform_device *mt_flash_dev; }; static const struct mt6360_fled_platform_data def_platform_data = { .fled_vmid_track = 0, /* 0: fixed mode, 1: tracking mode */ .fled_strb_tout = 1248, /* 64~2432ms, 32ms/step */ .fled1_tor_cur = 37500, /* 25000~400000uA, 12500uA/step */ /* * 25000~750000uA, 6250uA/step * 750000~1500000uA, 12500uA/step */ .fled1_strb_cur = 800000, .fled2_tor_cur = 37500, /* 25000~400000uA, 12500uA/step */ /* * 25000~750000uA, 6250uA/step * 750000~1500000uA, 12500uA/step */ .fled2_strb_cur = 800000, }; static inline struct mt6360_pmu_fled_info * to_fled_info(struct rt_fled_dev *fled) { return (struct mt6360_pmu_fled_info *)fled; } static const char *flashlight_mode_str[FLASHLIGHT_MODE_MAX] = { "off", "torch", "flash", "mixed", "dual flash", "dual torch", "dual off", }; static irqreturn_t mt6360_pmu_fled_strbpin_evt_handler(int irq, void *data) { struct mt6360_pmu_fled_info *mpfi = data; dev_dbg(mpfi->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6360_pmu_fled_torpin_evt_handler(int irq, void *data) { struct mt6360_pmu_fled_info *mpfi = data; dev_dbg(mpfi->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6360_pmu_fled_tx_evt_handler(int irq, void *data) { struct mt6360_pmu_fled_info *mpfi = data; dev_dbg(mpfi->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6360_pmu_fled_lvf_evt_handler(int irq, void *data) { struct mt6360_pmu_fled_info *mpfi = data; dev_err(mpfi->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6360_pmu_fled2_short_evt_handler(int irq, void *data) { struct mt6360_pmu_fled_info *mpfi = data; dev_err(mpfi->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6360_pmu_fled1_short_evt_handler(int irq, void *data) { struct mt6360_pmu_fled_info *mpfi = data; dev_err(mpfi->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6360_pmu_fled2_strb_evt_handler(int irq, void *data) { struct mt6360_pmu_fled_info *mpfi = data; dev_dbg(mpfi->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6360_pmu_fled1_strb_evt_handler(int irq, void *data) { struct mt6360_pmu_fled_info *mpfi = data; dev_dbg(mpfi->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6360_pmu_fled2_strb_to_evt_handler(int irq, void *data) { struct mt6360_pmu_fled_info *mpfi = data; dev_dbg(mpfi->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6360_pmu_fled1_strb_to_evt_handler(int irq, void *data) { struct mt6360_pmu_fled_info *mpfi = data; dev_dbg(mpfi->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6360_pmu_fled2_tor_evt_handler(int irq, void *data) { struct mt6360_pmu_fled_info *mpfi = data; dev_dbg(mpfi->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6360_pmu_fled1_tor_evt_handler(int irq, void *data) { struct mt6360_pmu_fled_info *mpfi = data; dev_dbg(mpfi->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6360_pmu_fled_chg_vinovp_evt_handler(int irq, void *data) { struct mt6360_pmu_fled_info *mpfi = data; dev_warn(mpfi->dev, "%s\n", __func__); return IRQ_HANDLED; } static struct mt6360_pmu_irq_desc mt6360_pmu_fled_irq_desc[] = { MT6360_PMU_IRQDESC(fled_strbpin_evt), MT6360_PMU_IRQDESC(fled_torpin_evt), MT6360_PMU_IRQDESC(fled_tx_evt), MT6360_PMU_IRQDESC(fled_lvf_evt), MT6360_PMU_IRQDESC(fled2_short_evt), MT6360_PMU_IRQDESC(fled1_short_evt), MT6360_PMU_IRQDESC(fled2_strb_evt), MT6360_PMU_IRQDESC(fled1_strb_evt), MT6360_PMU_IRQDESC(fled2_strb_to_evt), MT6360_PMU_IRQDESC(fled1_strb_to_evt), MT6360_PMU_IRQDESC(fled2_tor_evt), MT6360_PMU_IRQDESC(fled1_tor_evt), MT6360_PMU_IRQDESC(fled_chg_vinovp_evt), }; static void mt6360_pmu_fled_irq_register(struct platform_device *pdev) { struct mt6360_pmu_irq_desc *irq_desc; int i, ret; for (i = 0; i < ARRAY_SIZE(mt6360_pmu_fled_irq_desc); i++) { irq_desc = mt6360_pmu_fled_irq_desc + i; if (unlikely(!irq_desc->name)) continue; ret = platform_get_irq_byname(pdev, irq_desc->name); if (ret < 0) continue; irq_desc->irq = ret; ret = devm_request_threaded_irq(&pdev->dev, irq_desc->irq, NULL, irq_desc->irq_handler, IRQF_TRIGGER_FALLING, irq_desc->name, platform_get_drvdata(pdev)); if (ret < 0) dev_err(&pdev->dev, "request %s irq fail\n", irq_desc->name); } } static inline u32 mt6360_closest_reg(u32 min, u32 max, u32 step, u32 target) { if (target < min) return 0; if (target >= max) return (max - min) / step; return (target - min) / step; } static u32 mt6360_transform_tor_cur(u32 val) { return mt6360_closest_reg(25000, 400000, 12500, val); } static u32 mt6360_transform_strb_cur(u32 val) { if (val <= 750000) { pr_err("%s %d\n", __func__, val); val = mt6360_closest_reg(25000, 750000, 6250, val); val |= 0x80; /* UTRAL_ISTRB */ pr_err("%s 0x%02X\n", __func__, val); return val; } return mt6360_closest_reg(50000, 1500000, 12500, val); } static u32 mt6360_transform_strb_tout(u32 val) { return mt6360_closest_reg(64, 2432, 32, val); } static const struct mt6360_pdata_prop mt6360_pdata_props[] = { MT6360_PDATA_VALPROP(fled_vmid_track, struct mt6360_fled_platform_data, MT6360_PMU_FLED_VMIDTRK_CTRL1, 6, 0x40, NULL, 0), MT6360_PDATA_VALPROP(fled_strb_tout, struct mt6360_fled_platform_data, MT6360_PMU_FLED_STRB_CTRL, 0, 0x7F, mt6360_transform_strb_tout, 0), MT6360_PDATA_VALPROP(fled1_tor_cur, struct mt6360_fled_platform_data, MT6360_PMU_FLED1_TOR_CTRL, 0, 0x1F, mt6360_transform_tor_cur, 0), MT6360_PDATA_VALPROP(fled1_strb_cur, struct mt6360_fled_platform_data, MT6360_PMU_FLED1_STRB_CTRL2, 0, 0x7F, mt6360_transform_strb_cur, 0), MT6360_PDATA_VALPROP(fled2_tor_cur, struct mt6360_fled_platform_data, MT6360_PMU_FLED2_TOR_CTRL, 0, 0x1F, mt6360_transform_tor_cur, 0), MT6360_PDATA_VALPROP(fled2_strb_cur, struct mt6360_fled_platform_data, MT6360_PMU_FLED2_STRB_CTRL2, 0, 0x7F, mt6360_transform_strb_cur, 0), }; static int mt6360_fled_apply_pdata(struct mt6360_pmu_fled_info *mpfi, struct mt6360_fled_platform_data *pdata) { int ret; dev_dbg(mpfi->dev, "%s ++\n", __func__); ret = mt6360_pdata_apply_helper(mpfi->mpi, pdata, mt6360_pdata_props, ARRAY_SIZE(mt6360_pdata_props)); if (ret < 0) return ret; dev_dbg(mpfi->dev, "%s --\n", __func__); return 0; } static const struct mt6360_val_prop mt6360_val_props[] = { MT6360_DT_VALPROP(fled_vmid_track, struct mt6360_fled_platform_data), MT6360_DT_VALPROP(fled_strb_tout, struct mt6360_fled_platform_data), MT6360_DT_VALPROP(fled1_tor_cur, struct mt6360_fled_platform_data), MT6360_DT_VALPROP(fled1_strb_cur, struct mt6360_fled_platform_data), MT6360_DT_VALPROP(fled2_tor_cur, struct mt6360_fled_platform_data), MT6360_DT_VALPROP(fled2_strb_cur, struct mt6360_fled_platform_data), }; static int mt6360_fled_parse_dt_data(struct device *dev, struct mt6360_fled_platform_data *pdata) { struct device_node *np = dev->of_node; dev_dbg(dev, "%s ++\n", __func__); memcpy(pdata, &def_platform_data, sizeof(*pdata)); mt6360_dt_parser_helper(np, (void *)pdata, mt6360_val_props, ARRAY_SIZE(mt6360_val_props)); dev_dbg(dev, "%s --\n", __func__); return 0; } static struct flashlight_properties mt6360_fled_props = { .type = FLASHLIGHT_TYPE_LED, .torch_brightness = 0, .torch_max_brightness = 31, /* 00000 ~ 11110 */ .strobe_brightness = 0, /* * 25 ~ 750mA, step = 6.25mA, 117 steps * 762.5 ~ 1500mA, step = 12.5mA, 60 steps * 117 + 60 = 177 */ .strobe_max_brightness = 177, .strobe_delay = 0, .strobe_timeout = 0, .alias_name = "mt6360-fled", }; static int mt6360_fled_init(struct rt_fled_dev *fled) { return 0; } static int mt6360_fled_suspend(struct rt_fled_dev *fled, pm_message_t state) { return 0; } static int mt6360_fled_resume(struct rt_fled_dev *fled) { return 0; } static inline int mt6360_pmu_reg_test_bit(struct mt6360_pmu_info *mpi, u8 addr, u8 shift, bool *is_one) { int ret = 0; u8 data = 0; ret = mt6360_pmu_reg_read(mpi, addr); if (ret < 0) { *is_one = false; return ret; } data = ret & (1 << shift); *is_one = (data == 0 ? false : true); return 0; } static int mt6360_fled_set_mode(struct rt_fled_dev *fled, enum flashlight_mode mode) { struct mt6360_pmu_fled_info *fi = to_fled_info(fled); u8 val, mask; int ret = 0; u8 en_bit = (fi->id == MT6360_FLED1) ? MT6360_FLCS1_EN_MASK : MT6360_FLCS2_EN_MASK; bool hz_en = false, cfo_en = true; switch (mode) { case FLASHLIGHT_MODE_FLASH: case FLASHLIGHT_MODE_DUAL_FLASH: ret = mt6360_pmu_reg_test_bit(fi->mpi, MT6360_PMU_CHG_CTRL1, MT6360_SHFT_HZ_EN, &hz_en); if (ret >= 0 && hz_en) { dev_err(fi->dev, "%s WARNING\n", __func__); dev_err(fi->dev, "%s set %s mode with HZ=1\n", __func__, flashlight_mode_str[mode]); } ret = mt6360_pmu_reg_test_bit(fi->mpi, MT6360_PMU_CHG_CTRL2, MT6360_SHFT_CFO_EN, &cfo_en); if (ret >= 0 && !cfo_en) { dev_err(fi->dev, "%s WARNING\n", __func__); dev_err(fi->dev, "%s set %s mode with CFO=0\n", __func__, flashlight_mode_str[mode]); } break; default: break; } dev_info(fi->dev, "%s set %s mutex_lock +\n", __func__, flashlight_mode_str[mode]); mutex_lock(&fled_lock); dev_info(fi->dev, "%s set %s mutex_lock -\n", __func__, flashlight_mode_str[mode]); switch (mode) { case FLASHLIGHT_MODE_TORCH: if (mt6360_global_mode == FLASHLIGHT_MODE_FLASH || mt6360_global_mode == FLASHLIGHT_MODE_DUAL_FLASH) goto out; /* Fled en/Strobe off/Torch on */ ret = mt6360_pmu_reg_clr_bits(fi->mpi, MT6360_PMU_FLED_EN, MT6360_FL_STROBE_MASK); if (ret < 0) break; udelay(500); val = en_bit | MT6360_FL_TORCH_MASK; ret = mt6360_pmu_reg_set_bits(fi->mpi, MT6360_PMU_FLED_EN, val); if (ret < 0) break; udelay(500); mt6360_global_mode = mode; mt6360_fled_on |= 1 << fi->id; break; case FLASHLIGHT_MODE_FLASH: /* strobe off */ ret = mt6360_pmu_reg_clr_bits(fi->mpi, MT6360_PMU_FLED_EN, MT6360_FL_STROBE_MASK); if (ret < 0) break; udelay(400); /* fled en/strobe on */ val = en_bit | MT6360_FL_STROBE_MASK; mask = val; ret = mt6360_pmu_reg_update_bits(fi->mpi, MT6360_PMU_FLED_EN, mask, val); if (ret < 0) break; mdelay(5); mt6360_global_mode = mode; mt6360_fled_on |= 1 << fi->id; break; case FLASHLIGHT_MODE_OFF: ret = mt6360_pmu_reg_clr_bits(fi->mpi, MT6360_PMU_FLED_EN, en_bit); if (ret < 0) break; mt6360_fled_on &= ~(1 << fi->id); if (mt6360_fled_on == 0) mt6360_global_mode = mode; break; case FLASHLIGHT_MODE_DUAL_FLASH: if (fi->id == MT6360_FLED2) goto out; /* strobe off */ ret = mt6360_pmu_reg_clr_bits(fi->mpi, MT6360_PMU_FLED_EN, MT6360_FL_STROBE_MASK); if (ret < 0) break; udelay(400); /* fled en/strobe on */ val = en_bit | MT6360_FLCS2_EN_MASK | MT6360_FL_STROBE_MASK; mask = val; ret = mt6360_pmu_reg_update_bits(fi->mpi, MT6360_PMU_FLED_EN, mask, val); if (ret < 0) break; mt6360_global_mode = mode; mt6360_fled_on |= (BIT(MT6360_FLED1) | BIT(MT6360_FLED2)); break; case FLASHLIGHT_MODE_DUAL_TORCH: if (fi->id == MT6360_FLED2) goto out; if (mt6360_global_mode == FLASHLIGHT_MODE_FLASH || mt6360_global_mode == FLASHLIGHT_MODE_DUAL_FLASH) goto out; /* Fled en/Strobe off/Torch on */ ret = mt6360_pmu_reg_clr_bits(fi->mpi, MT6360_PMU_FLED_EN, MT6360_FL_STROBE_MASK); if (ret < 0) break; udelay(500); val = en_bit | MT6360_FLCS2_EN_MASK | MT6360_FL_TORCH_MASK; ret = mt6360_pmu_reg_set_bits(fi->mpi, MT6360_PMU_FLED_EN, val); if (ret < 0) break; udelay(500); mt6360_global_mode = mode; mt6360_fled_on |= (BIT(MT6360_FLED1) | BIT(MT6360_FLED2)); break; case FLASHLIGHT_MODE_DUAL_OFF: if (fi->id == MT6360_FLED2) goto out; ret = mt6360_pmu_reg_clr_bits(fi->mpi, MT6360_PMU_FLED_EN, en_bit | MT6360_FLCS2_EN_MASK); if (ret < 0) break; mt6360_fled_on = 0; mt6360_global_mode = FLASHLIGHT_MODE_OFF; break; default: mutex_unlock(&fled_lock); return -EINVAL; } if (ret < 0) dev_info(fi->dev, "%s set %s mode fail\n", __func__, flashlight_mode_str[mode]); else dev_info(fi->dev, "%s set %s\n", __func__, flashlight_mode_str[mode]); out: mutex_unlock(&fled_lock); return ret; } static int mt6360_fled_get_mode(struct rt_fled_dev *fled) { struct mt6360_pmu_fled_info *fi = to_fled_info(fled); int ret; if (fi->id == MT6360_FLED2) { dev_err(fi->dev, "%s FLED2 not support get mode\n", __func__); return 0; } ret = mt6360_pmu_reg_read(fi->mpi, MT6360_PMU_FLED_EN); if (ret < 0) return -EINVAL; if (ret & MT6360_FL_STROBE_MASK) return FLASHLIGHT_MODE_FLASH; if (ret & MT6360_FL_TORCH_MASK) return FLASHLIGHT_MODE_TORCH; return FLASHLIGHT_MODE_OFF; } static int mt6360_fled_strobe(struct rt_fled_dev *fled) { return mt6360_fled_set_mode(fled, FLASHLIGHT_MODE_FLASH); } static int mt6360_fled_torch_current_list(struct rt_fled_dev *fled, int selector) { if (selector < 0 || selector > fled->init_props->torch_max_brightness) return -EINVAL; return 25000 + selector * 12500; } static int mt6360_fled_strobe_current_list(struct rt_fled_dev *fled, int selector) { if (selector < 0 || selector > fled->init_props->strobe_max_brightness) return -EINVAL; if (selector < 117) return 25000 + selector * 6250; /* make the base 762.5 mA */ selector -= 60; return 750000 + selector * 12500; } #define MT6360_TCL_MAX 8 static int mt6360_fled_timeout_level_list(struct rt_fled_dev *fled, int selector) { if (selector < 0 || selector > MT6360_TCL_MAX) return -EINVAL; return 25000 + selector * 25000; } static int mt6360_fled_strobe_timeout_list(struct rt_fled_dev *fled, int selector) { if (selector < 0 || selector > 74) return -EINVAL; return 64 + selector * 32; } static int mt6360_fled_set_torch_current_sel(struct rt_fled_dev *fled, int selector) { struct mt6360_pmu_fled_info *fi = to_fled_info(fled); return mt6360_pmu_reg_update_bits(fi->mpi, fi->id == MT6360_FLED1 ? MT6360_PMU_FLED1_TOR_CTRL : MT6360_PMU_FLED2_TOR_CTRL, MT6360_ITOR_MASK, selector << MT6360_ITOR_SHIFT); } static int mt6360_fled_set_strobe_current_sel(struct rt_fled_dev *fled, int selector) { struct mt6360_pmu_fled_info *fi = to_fled_info(fled); int ret; u8 reg_strb_cur = (fi->id == MT6360_FLED1 ? MT6360_PMU_FLED1_STRB_CTRL2 : MT6360_PMU_FLED2_STRB_CTRL2); if (selector > 177 || selector < 0) return -EINVAL; if (selector < 117) /* 25 ~ 750mA */ ret = mt6360_pmu_reg_set_bits(fi->mpi, reg_strb_cur, MT6360_UTRAL_ISTRB_MASK); else { /* 762.5 ~ 1500mA */ ret = mt6360_pmu_reg_clr_bits(fi->mpi, reg_strb_cur, MT6360_UTRAL_ISTRB_MASK); selector -= 60; /* make the base 762.5 mA */ } return mt6360_pmu_reg_update_bits(fi->mpi, reg_strb_cur, MT6360_ISTRB_MASK, selector << MT6360_ISTRB_SHIFT); } static int mt6360_fled_set_timeout_level_sel(struct rt_fled_dev *fled, int selector) { struct mt6360_pmu_fled_info *fi = to_fled_info(fled); return mt6360_pmu_reg_update_bits(fi->mpi, fi->id == MT6360_FLED1 ? MT6360_PMU_FLED1_CTRL : MT6360_PMU_FLED2_CTRL, MT6360_TCL_MASK, selector << MT6360_TCL_SHIFT); } static int mt6360_fled_set_strobe_timeout_sel(struct rt_fled_dev *fled, int selector) { struct mt6360_pmu_fled_info *fi = to_fled_info(fled); if (fi->id == MT6360_FLED2) { dev_err(fi->dev, "%s FLED2 not support set strobe timeout\n", __func__); return -EINVAL; } return mt6360_pmu_reg_update_bits(fi->mpi, MT6360_PMU_FLED_STRB_CTRL, MT6360_STRB_TO_MASK, selector << MT6360_STRB_TO_SHIFT); } static int mt6360_fled_get_torch_current_sel(struct rt_fled_dev *fled) { struct mt6360_pmu_fled_info *fi = to_fled_info(fled); int ret; ret = mt6360_pmu_reg_read(fi->mpi, fi->id == MT6360_FLED1 ? MT6360_PMU_FLED1_TOR_CTRL : MT6360_PMU_FLED2_TOR_CTRL); if (ret < 0) { dev_err(fi->dev, "%s fail\n", __func__); return ret; } return (ret & MT6360_ITOR_MASK) >> MT6360_ITOR_SHIFT; } static int mt6360_fled_get_strobe_current_sel(struct rt_fled_dev *fled) { struct mt6360_pmu_fled_info *fi = to_fled_info(fled); int ret; ret = mt6360_pmu_reg_read(fi->mpi, fi->id == MT6360_FLED1 ? MT6360_PMU_FLED1_STRB_CTRL2 : MT6360_PMU_FLED2_STRB_CTRL2); if (ret < 0) { dev_err(fi->dev, "%s fail\n", __func__); return ret; } return (ret & MT6360_ISTRB_MASK) >> MT6360_ISTRB_SHIFT; } static int mt6360_fled_get_timeout_level_sel(struct rt_fled_dev *fled) { struct mt6360_pmu_fled_info *fi = to_fled_info(fled); int ret; ret = mt6360_pmu_reg_read(fi->mpi, fi->id == MT6360_FLED1 ? MT6360_PMU_FLED1_CTRL : MT6360_PMU_FLED2_CTRL); if (ret < 0) { dev_err(fi->dev, "%s fail\n", __func__); return ret; } return (ret & MT6360_TCL_MASK) >> MT6360_TCL_SHIFT; } static int mt6360_fled_get_strobe_timeout_sel(struct rt_fled_dev *fled) { struct mt6360_pmu_fled_info *fi = to_fled_info(fled); int ret; ret = mt6360_pmu_reg_read(fi->mpi, MT6360_PMU_FLED_STRB_CTRL); if (ret < 0) { dev_err(fi->dev, "%s fail\n", __func__); return ret; } return (ret & MT6360_STRB_TO_MASK) >> MT6360_STRB_TO_SHIFT; } static void mt6360_fled_shutdown(struct rt_fled_dev *fled) { mt6360_fled_set_mode(fled, FLASHLIGHT_MODE_OFF); } static int mt6360_fled_is_ready(struct rt_fled_dev *fled) { struct mt6360_pmu_fled_info *fi = to_fled_info(fled); int ret; ret = mt6360_pmu_reg_read(fi->mpi, MT6360_PMU_CHG_STAT2); if (ret < 0) { dev_err(fi->dev, "%s fail\n", __func__); return ret; } /* * CHG_STAT2 bit[3] FLED_CHG_VINOVP, * if OVP = 0 --> V < 5.3, if OVP = 1, V > 5.6 */ return ret & MT6360_FLED_CHG_VINOVP ? 0 : 1; } static struct rt_fled_hal mt6360_fled_hal = { .rt_hal_fled_init = mt6360_fled_init, .rt_hal_fled_suspend = mt6360_fled_suspend, .rt_hal_fled_resume = mt6360_fled_resume, .rt_hal_fled_set_mode = mt6360_fled_set_mode, .rt_hal_fled_get_mode = mt6360_fled_get_mode, .rt_hal_fled_strobe = mt6360_fled_strobe, .rt_hal_fled_get_is_ready = mt6360_fled_is_ready, .rt_hal_fled_torch_current_list = mt6360_fled_torch_current_list, .rt_hal_fled_strobe_current_list = mt6360_fled_strobe_current_list, .rt_hal_fled_timeout_level_list = mt6360_fled_timeout_level_list, /* .fled_lv_protection_list = mt6360_fled_lv_protection_list, */ .rt_hal_fled_strobe_timeout_list = mt6360_fled_strobe_timeout_list, /* method to set */ .rt_hal_fled_set_torch_current_sel = mt6360_fled_set_torch_current_sel, .rt_hal_fled_set_strobe_current_sel = mt6360_fled_set_strobe_current_sel, .rt_hal_fled_set_timeout_level_sel = mt6360_fled_set_timeout_level_sel, .rt_hal_fled_set_strobe_timeout_sel = mt6360_fled_set_strobe_timeout_sel, /* method to get */ .rt_hal_fled_get_torch_current_sel = mt6360_fled_get_torch_current_sel, .rt_hal_fled_get_strobe_current_sel = mt6360_fled_get_strobe_current_sel, .rt_hal_fled_get_timeout_level_sel = mt6360_fled_get_timeout_level_sel, .rt_hal_fled_get_strobe_timeout_sel = mt6360_fled_get_strobe_timeout_sel, /* PM shutdown, optional */ .rt_hal_fled_shutdown = mt6360_fled_shutdown, }; static int mt6360_pmu_fled_probe(struct platform_device *pdev) { struct mt6360_fled_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mt6360_pmu_fled_info *mpfi; bool use_dt = pdev->dev.of_node; int ret, i; pr_info("%s (%s) id = %d\n", __func__, MT6360_PMU_FLED_DRV_VERSION, pdev->id); if (!mt6360_fled_inited) { if (use_dt) { pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; ret = mt6360_fled_parse_dt_data(&pdev->dev, pdata); if (ret < 0) { dev_err(&pdev->dev, "parse dt fail\n"); return ret; } pdev->dev.platform_data = pdata; } if (!pdata) { dev_err(&pdev->dev, "no platform data specified\n"); return -EINVAL; } } mpfi = devm_kzalloc(&pdev->dev, sizeof(*mpfi), GFP_KERNEL); if (!mpfi) return -ENOMEM; mpfi->dev = &pdev->dev; mpfi->mpi = dev_get_drvdata(mt6360_fled_inited ? (pdev->dev.parent)->parent : pdev->dev.parent); platform_set_drvdata(pdev, mpfi); if (!mt6360_fled_inited) { /* apply platform data */ ret = mt6360_fled_apply_pdata(mpfi, pdata); if (ret < 0) { dev_err(&pdev->dev, "apply pdata fail\n"); return ret; } /* irq register */ mt6360_pmu_fled_irq_register(pdev); mt6360_fled_inited = true; for (i = 0; i < MT6360_FLED_NUM; i++) { mpfi->fled_dev[i] = platform_device_register_resndata(mpfi->dev, "mt6360_pmu_fled", i, NULL, 0, NULL, 0); } } else { if (pdev->id >= MT6360_FLED_NUM) { dev_err(mpfi->dev, "%s incorrect dev id\n", __func__); goto out; } mpfi->id = pdev->id; mpfi->base.init_props = &mt6360_fled_props; mpfi->base.hal = &mt6360_fled_hal; if (pdev->id == MT6360_FLED1) mpfi->base.name = "mt-flash-led1"; else mpfi->base.name = "mt-flash-led2"; mpfi->base.chip_name = "mt6360_pmu_fled"; dev_err(mpfi->dev, "%s flash name %s\n", __func__, mpfi->base.name); mpfi->mt_flash_dev = platform_device_register_resndata(mpfi->dev, "rt-flash-led", mpfi->id, NULL, 0, NULL, 0); } dev_info(&pdev->dev, "%s: successfully probed\n", __func__); out: return 0; } static int mt6360_pmu_fled_remove(struct platform_device *pdev) { struct mt6360_pmu_fled_info *mpfi = platform_get_drvdata(pdev); int i; dev_dbg(&pdev->dev, "%s\n", __func__); if (!mpfi) return 0; if (mpfi) { for (i = 0; i < MT6360_FLED_NUM; i++) platform_device_unregister(mpfi->fled_dev[i]); platform_device_unregister(mpfi->mt_flash_dev); } return 0; } static int __maybe_unused mt6360_pmu_fled_suspend(struct device *dev) { return 0; } static int __maybe_unused mt6360_pmu_fled_resume(struct device *dev) { return 0; } static SIMPLE_DEV_PM_OPS(mt6360_pmu_fled_pm_ops, mt6360_pmu_fled_suspend, mt6360_pmu_fled_resume); static const struct of_device_id __maybe_unused mt6360_pmu_fled_of_id[] = { { .compatible = "mediatek,mt6360_pmu_fled", }, {}, }; MODULE_DEVICE_TABLE(of, mt6360_pmu_fled_of_id); static const struct platform_device_id mt6360_pmu_fled_id[] = { { "mt6360_pmu_fled", 0 }, {}, }; MODULE_DEVICE_TABLE(platform, mt6360_pmu_fled_id); static struct platform_driver mt6360_pmu_fled_driver = { .driver = { .name = "mt6360_pmu_fled", .owner = THIS_MODULE, .pm = &mt6360_pmu_fled_pm_ops, .of_match_table = of_match_ptr(mt6360_pmu_fled_of_id), }, .probe = mt6360_pmu_fled_probe, .remove = mt6360_pmu_fled_remove, .id_table = mt6360_pmu_fled_id, }; module_platform_driver(mt6360_pmu_fled_driver); MODULE_AUTHOR("CY_Huang "); MODULE_DESCRIPTION("MT6360 PMU FLED Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(MT6360_PMU_FLED_DRV_VERSION); /* * Version Note * 1.0.1_MTK * (1) Print warnings when strobe mode with HZ=1 or CFO=0 * * 1.0.0_MTK * (1) Initial Release */