// SPDX-License-Identifier: GPL-2.0 /* * Awinic AW20036/AW20054/AW20072/AW20108 LED driver * * Copyright (c) 2023, SberDevices. All Rights Reserved. * * Author: Martin Kurbanov */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define AW200XX_LEDS_MAX 108 #define AW200XX_PATTERN_MAX 3 #define AW200XX_DIM_MAX (BIT(6) - 1) #define AW200XX_FADE_MAX (BIT(8) - 1) #define AW200XX_IMAX_DEFAULT_uA 60000 #define AW200XX_IMAX_MAX_uA 160000 #define AW200XX_IMAX_MIN_uA 3300 /* Page 0 */ #define AW200XX_REG_PAGE0_BASE 0xc000 /* Select page register */ #define AW200XX_REG_PAGE 0xF0 #define AW200XX_PAGE_MASK (GENMASK(7, 6) | GENMASK(2, 0)) #define AW200XX_PAGE_SHIFT 0 #define AW200XX_NUM_PAGES 6 #define AW200XX_PAGE_SIZE 256 #define AW200XX_REG(page, reg) \ (AW200XX_REG_PAGE0_BASE + (page) * AW200XX_PAGE_SIZE + (reg)) #define AW200XX_REG_MAX \ AW200XX_REG(AW200XX_NUM_PAGES - 1, AW200XX_PAGE_SIZE - 1) #define AW200XX_PAGE0 0 #define AW200XX_PAGE1 1 #define AW200XX_PAGE2 2 #define AW200XX_PAGE3 3 #define AW200XX_PAGE4 4 #define AW200XX_PAGE5 5 /* Chip ID register */ #define AW200XX_REG_IDR AW200XX_REG(AW200XX_PAGE0, 0x00) #define AW200XX_IDR_CHIPID 0x18 /* Sleep mode register */ #define AW200XX_REG_SLPCR AW200XX_REG(AW200XX_PAGE0, 0x01) #define AW200XX_SLPCR_ACTIVE 0x00 /* Reset register */ #define AW200XX_REG_RSTR AW200XX_REG(AW200XX_PAGE0, 0x02) #define AW200XX_RSTR_RESET 0x01 /* Global current configuration register */ #define AW200XX_REG_GCCR AW200XX_REG(AW200XX_PAGE0, 0x03) #define AW200XX_GCCR_IMAX_MASK GENMASK(7, 4) #define AW200XX_GCCR_IMAX(x) ((x) << 4) #define AW200XX_GCCR_ALLON BIT(3) /* Fast clear display control register */ #define AW200XX_REG_FCD AW200XX_REG(AW200XX_PAGE0, 0x04) #define AW200XX_FCD_CLEAR 0x01 /* Interrupt status register */ #define AW200XX_REG_ISRFLT AW200XX_REG(AW200XX_PAGE0, 0x0B) #define AW200XX_ISRFLT_PATIS_MASK GENMASK(6, 4) /* Pattern enable control register */ #define AW200XX_REG_PATCR AW200XX_REG(AW200XX_PAGE0, 0x43) #define AW200XX_PATCR_PAT_IE_MASK GENMASK(6, 4) #define AW200XX_PATCR_PAT_IE_ALL AW200XX_PATCR_PAT_IE_MASK #define AW200XX_PATCR_PAT_ENABLE(x) BIT(x) /* * Maximum breathing level registers * For patterns 0 - 0x44, 1 - 0x45, 2 - 0x46 (step 1) */ #define AW200XX_REG_PAT0_MAX_BREATH AW200XX_REG(AW200XX_PAGE0, 0x44) /* * Minimum breathing level registers * For patterns 0 - 0x47, 1 - 0x48, 2 - 0x49 (step 1) */ #define AW200XX_REG_PAT0_MIN_BREATH AW200XX_REG(AW200XX_PAGE0, 0x47) /* * Template 1 (rise-time) & template 2 (on-time) configuration register * For patterns 0 - 0x4A, 1 - 0x4E, 2 - 0x52 (step 4) */ #define AW200XX_REG_PAT0_T0 AW200XX_REG(AW200XX_PAGE0, 0x4A) /* * Template 3 (fall-time) & template 4 (off-time) configuration register * For patterns 0 - 0x4B, 1 - 0x4F, 2 - 0x53 (step 4) */ #define AW200XX_REG_PAT0_T1 AW200XX_REG(AW200XX_PAGE0, 0x4B) /* * Loop configuration registers: * loop end point setting (LE) * loop beginning point setting (LB) * MSB of loop times (LT) * For patterns 0 - 0x4C, 1 - 0x50, 2 - 0x54 (step 4) */ #define AW200XX_REG_PAT0_T2 AW200XX_REG(AW200XX_PAGE0, 0x4C) #define AW200XX_REG_PATX_T2(x) (AW200XX_REG_PAT0_T2 + (x)) /* * Loop configuration registers: * LSB of loop times (LT) * For patterns 0 - 0x4D, 1 - 0x51, 2 - 0x55 (step 4) */ #define AW200XX_REG_PAT0_T3 AW200XX_REG(AW200XX_PAGE0, 0x4D) #define AW200XX_REG_PATX_T3(x) (AW200XX_REG_PAT0_T3 + (x)) #define AW200XX_PAT_T2_LE_MASK GENMASK(7, 6) #define AW200XX_PAT_T2_LB_MASK GENMASK(5, 4) #define AW200XX_PAT_T2_LT_MASK GENMASK(3, 0) #define AW200XX_PAT_T3_LT_MASK GENMASK(7, 0) #define AW200XX_PAT0_T2_LT_MSB(x) ((x) >> 8) #define AW200XX_PAT0_T3_LT_LSB(x) ((x) & 0xFF) #define AW200XX_PAT0_T_LT(msb, lsb) ((msb) << 8 | (lsb)) #define AW200XX_PAT0_T_LT_MAX (BIT(12) - 1) #define AW200XX_PAT_T_STEP 4 #define AW200XX_PAT_T1_T3_MASK GENMASK(7, 4) #define AW200XX_PAT_T2_T4_MASK GENMASK(3, 0) #define AW200XX_TEMPLATE_TIME_MAX (BIT(4) - 1) /* * Pattern mode configuration register * For patterns 0 - 0x56, 1 - 0x57, 2 - 0x58 (step 1) */ #define AW200XX_REG_PAT0_CFG AW200XX_REG(AW200XX_PAGE0, 0x56) #define AW200XX_PAT_CFG_MODE_MASK BIT(0) #define AW200XX_PAT_CFG_RAMP_MASK BIT(1) #define AW200XX_PAT_CFG_SWITCH_MASK BIT(2) /* Start pattern register */ #define AW200XX_REG_PATGO AW200XX_REG(AW200XX_PAGE0, 0x59) #define AW200XX_PATGO(x) BIT(x) #define AW200XX_PATGO_RUN(x, run) ((run) << (x)) #define AW200XX_PATGO_STATE(x) BIT((x) + 4) /* Display size configuration */ #define AW200XX_REG_DSIZE AW200XX_REG(AW200XX_PAGE0, 0x80) #define AW200XX_DSIZE_COLUMNS_MAX 12 #define AW200XX_LED2REG(x, columns) \ ((x) + (((x) / (columns)) * (AW200XX_DSIZE_COLUMNS_MAX - (columns)))) /* DIM current configuration register on page 1 */ #define AW200XX_REG_DIM_PAGE1(x, columns) \ AW200XX_REG(AW200XX_PAGE1, AW200XX_LED2REG(x, columns)) /* Pattern selection register*/ #define AW200XX_REG_PAT_SELECT(x, columns) \ AW200XX_REG(AW200XX_PAGE3, AW200XX_LED2REG(x, columns)) #define AW200XX_PATX_SELECT(x) ((x) + 1) /* * DIM current configuration register (page 4). * The even address for current DIM configuration. * The odd address for current FADE configuration */ #define AW200XX_REG_DIM(x, columns) \ AW200XX_REG(AW200XX_PAGE4, AW200XX_LED2REG(x, columns) * 2) #define AW200XX_REG_DIM2FADE(x) ((x) + 1) #define AW200XX_REG_FADE2DIM(fade) \ DIV_ROUND_UP((fade) * AW200XX_DIM_MAX, AW200XX_FADE_MAX) #define AW200XX_SCALE_FADE(fade, scale) \ DIV_ROUND_UP((fade) * (scale), AW200XX_FADE_MAX) /* * Duty ratio of display scan (see p.15 of datasheet for formula): * duty = (592us / 600.5us) * (1 / (display_rows + 1)) * * Multiply to 1000 (MILLI) to improve the accuracy of calculations. */ #define AW200XX_DUTY_RATIO(rows) \ (((592UL * USEC_PER_SEC) / 600500UL) * (MILLI / (rows)) / MILLI) struct aw200xx_chipdef { u32 channels; u32 display_size_rows_max; u32 display_size_columns; }; struct aw200xx_page { u8 dirty_start; u8 dirty_end; u8 *buf; u32 page; }; #define FB_BUFFER_COUNT 2 struct aw200xx_fb { int width; int height; int num_leds; u8 *map; atomic_t open_count; struct fb_info *info; u32 refresh_rate; u8 *buffer; u8 min_reg; u8 max_reg; int offset_y; struct backlight_device *bl; u8 fade_scale; }; struct aw200xx_led { struct led_classdev cdev; struct aw200xx *chip; int dim; u32 num; }; struct aw200xx { const struct aw200xx_chipdef *cdef; struct i2c_client *client; struct regmap *regmap; struct mutex mutex; DECLARE_BITMAP(pattern_leds[AW200XX_PATTERN_MAX], AW200XX_LEDS_MAX); u32 num_leds; u32 num_pattern_leds; u32 display_rows; struct aw200xx_page dim_page; struct aw200xx_page fade_page; struct delayed_work drawer; bool queue_mode; struct aw200xx_fb fb; u32 imax; struct gpio_desc *hwen; struct aw200xx_led leds[] __counted_by(num_leds); }; struct aw200xx_led_data { int num; u32 imax; enum led_default_state default_state; }; struct aw200xx_attribute { struct device_attribute dev_attr; u32 reg; u32 mask; u32 max; }; #define to_aw200xx_attr(attr) \ container_of(attr, struct aw200xx_attribute, dev_attr) #define AW200XX_ATTR(_n, _m, _sh, _st, _r, _msk, _max) { \ .dev_attr = __ATTR(_n, _m, _sh, _st), \ .reg = _r, \ .mask = _msk, \ .max = _max, \ } #define AW200XX_DEVICE_ATTR_RW(_v, _n, _sh, _st, _r, _msk, _max) \ struct aw200xx_attribute _v##_attr = \ AW200XX_ATTR(_n, 0644, _sh, _st, \ _r, _msk, _max) #define AW200XX_DEVICE_ATTR_RO(_v, _n, _sh, _r, _msk, _max) \ struct aw200xx_attribute _v##_attr = \ AW200XX_ATTR(_n, 0444, _sh, NULL, \ _r, _msk, _max) static ssize_t aw200xx_store_internal(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { const struct aw200xx_attribute *attr = to_aw200xx_attr(devattr); struct aw200xx *chip = i2c_get_clientdata(to_i2c_client(dev)); u32 val; int ret; ret = kstrtouint(buf, 0, &val); if (ret < 0 || val > attr->max) return -EINVAL; val <<= __ffs(attr->mask); mutex_lock(&chip->mutex); ret = regmap_update_bits(chip->regmap, attr->reg, attr->mask, val); mutex_unlock(&chip->mutex); if (ret) return ret; return count; } static ssize_t aw200xx_show_internal(struct device *dev, struct device_attribute *devattr, char *buf) { const struct aw200xx_attribute *attr = to_aw200xx_attr(devattr); struct aw200xx *chip = i2c_get_clientdata(to_i2c_client(dev)); u32 val; int ret; mutex_lock(&chip->mutex); ret = regmap_read(chip->regmap, attr->reg, &val); mutex_unlock(&chip->mutex); if (ret) return ret; val = (val & attr->mask) >> __ffs(attr->mask); return sysfs_emit(buf, "%u\n", val); } static ssize_t aw200xx_template_time_show(struct device *dev, struct device_attribute *devattr, char *buf) { static const u32 ttimes_ms[] = { 0, 130, 260, 380, 510, 770, 1040, 1600, 2100, 2600, 3100, 4200, 5200, 6200, 7300, 8300, }; const struct aw200xx_attribute *attr = to_aw200xx_attr(devattr); struct aw200xx *chip = i2c_get_clientdata(to_i2c_client(dev)); u32 ttime; int ret; mutex_lock(&chip->mutex); ret = regmap_read(chip->regmap, attr->reg, &ttime); mutex_unlock(&chip->mutex); if (ret) return ret; ttime = (ttime & attr->mask) >> __ffs(attr->mask); if (ttime >= ARRAY_SIZE(ttimes_ms)) return -EIO; ttime = ttimes_ms[ttime]; /* For On & Off time minimum is 40ms */ if (ttime == 0 && attr->mask == AW200XX_PAT_T2_T4_MASK) ttime = 40; return sysfs_emit(buf, "%ums\n", ttime); } static ssize_t aw200xx_pattern_leds_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count, bool clear) { const struct aw200xx_attribute *attr = to_aw200xx_attr(devattr); struct aw200xx *chip = i2c_get_clientdata(to_i2c_client(dev)); unsigned long *pattern_leds = chip->pattern_leds[attr->reg]; u32 columns = chip->cdef->display_size_columns; DECLARE_BITMAP(leds, AW200XX_LEDS_MAX); u32 val = clear ? 0 : AW200XX_PATX_SELECT(attr->reg); u32 i; ssize_t ret; if (sysfs_streq(buf, "all")) { bitmap_fill(leds, chip->num_pattern_leds); } else { ret = bitmap_parse(buf, count, leds, chip->num_pattern_leds); if (ret) return -EINVAL; } mutex_lock(&chip->mutex); if (!clear && atomic_read(&chip->fb.open_count) && find_next_bit(leds, chip->num_pattern_leds, chip->num_leds) != chip->num_pattern_leds) { ret = -EBUSY; goto out_unlock; } for_each_set_bit(i, leds, chip->num_pattern_leds) { const u32 num = i < chip->num_leds ? chip->leds[i].num : chip->fb.map[i - chip->num_leds]; ret = regmap_write(chip->regmap, AW200XX_REG_PAT_SELECT(num, columns), val); if (ret) goto out_unlock; if (clear) __clear_bit(i, pattern_leds); else __set_bit(i, pattern_leds); } ret = count; out_unlock: mutex_unlock(&chip->mutex); return ret; } static ssize_t aw200xx_pattern_select_leds_show(struct device *dev, struct device_attribute *devattr, char *buf) { const struct aw200xx_attribute *attr = to_aw200xx_attr(devattr); struct aw200xx *chip = i2c_get_clientdata(to_i2c_client(dev)); int ret; mutex_lock(&chip->mutex); ret = sysfs_emit(buf, "%*pb\n", chip->num_pattern_leds, chip->pattern_leds[attr->reg]); mutex_unlock(&chip->mutex); return ret; } static ssize_t aw200xx_pattern_select_leds_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { return aw200xx_pattern_leds_store(dev, devattr, buf, count, false); } static ssize_t aw200xx_pattern_clear_leds_show(struct device *dev, struct device_attribute *devattr, char *buf) { const struct aw200xx_attribute *attr = to_aw200xx_attr(devattr); struct aw200xx *chip = i2c_get_clientdata(to_i2c_client(dev)); DECLARE_BITMAP(leds, AW200XX_LEDS_MAX); int ret; mutex_lock(&chip->mutex); bitmap_fill(leds, chip->num_pattern_leds); bitmap_xor(leds, leds, chip->pattern_leds[attr->reg], chip->num_pattern_leds); ret = scnprintf(buf, PAGE_SIZE, "%*pb", chip->num_pattern_leds, leds); mutex_unlock(&chip->mutex); return ret; } static ssize_t aw200xx_pattern_clear_leds_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { return aw200xx_pattern_leds_store(dev, devattr, buf, count, true); } static ssize_t aw200xx_pattern_start_show(struct device *dev, struct device_attribute *devattr, char *buf) { const struct aw200xx_attribute *attr = to_aw200xx_attr(devattr); struct aw200xx *chip = i2c_get_clientdata(to_i2c_client(dev)); u32 start = 0; u32 val; int ret; mutex_lock(&chip->mutex); ret = regmap_read(chip->regmap, AW200XX_REG_PATCR, &val); if (ret) goto out_unlock; if (val & AW200XX_PATCR_PAT_ENABLE(attr->reg)) { ret = regmap_read(chip->regmap, AW200XX_REG_PATGO, &val); if (ret) goto out_unlock; start = !!(val & AW200XX_PATGO(attr->reg)); } ret = sysfs_emit(buf, "%u\n", start); out_unlock: mutex_unlock(&chip->mutex); return ret; } static ssize_t aw200xx_pattern_start_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { const struct aw200xx_attribute *attr = to_aw200xx_attr(devattr); struct aw200xx *chip = i2c_get_clientdata(to_i2c_client(dev)); u32 start; ssize_t ret; ret = kstrtouint(buf, 0, &start); if (ret < 0 || start > attr->max) return -EINVAL; start = AW200XX_PATGO_RUN(attr->reg, start); mutex_lock(&chip->mutex); ret = regmap_update_bits(chip->regmap, AW200XX_REG_PATCR, AW200XX_PATCR_PAT_ENABLE(attr->reg), start); if (ret) goto out_unlock; ret = regmap_update_bits(chip->regmap, AW200XX_REG_PATGO, AW200XX_PATGO(attr->reg), start); if (ret) goto out_unlock; ret = count; out_unlock: mutex_unlock(&chip->mutex); return ret; } static ssize_t aw200xx_pattern_running_show(struct device *dev, struct device_attribute *devattr, char *buf) { const struct aw200xx_attribute *attr = to_aw200xx_attr(devattr); struct aw200xx *chip = i2c_get_clientdata(to_i2c_client(dev)); u32 running; int ret; mutex_lock(&chip->mutex); ret = regmap_read(chip->regmap, AW200XX_REG_PATGO, &running); if (ret) goto out_unlock; running &= AW200XX_PATGO_STATE(attr->reg); ret = sysfs_emit(buf, "%u\n", !!running); out_unlock: mutex_unlock(&chip->mutex); return ret; } static ssize_t aw200xx_pattern_repeat_show(struct device *dev, struct device_attribute *devattr, char *buf) { const struct aw200xx_attribute *attr = to_aw200xx_attr(devattr); struct aw200xx *chip = i2c_get_clientdata(to_i2c_client(dev)); u32 repeat_msb; u32 repeat_lsb; int ret; mutex_lock(&chip->mutex); ret = regmap_read(chip->regmap, AW200XX_REG_PATX_T2(attr->reg), &repeat_msb); if (ret) goto out_unlock; ret = regmap_read(chip->regmap, AW200XX_REG_PATX_T3(attr->reg), &repeat_lsb); if (ret) goto out_unlock; repeat_msb &= AW200XX_PAT_T2_LT_MASK; repeat_lsb &= AW200XX_PAT_T3_LT_MASK; ret = sysfs_emit(buf, "%u\n", AW200XX_PAT0_T_LT(repeat_msb, repeat_lsb)); out_unlock: mutex_unlock(&chip->mutex); return ret; } static ssize_t aw200xx_pattern_repeat_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { const struct aw200xx_attribute *attr = to_aw200xx_attr(devattr); struct aw200xx *chip = i2c_get_clientdata(to_i2c_client(dev)); u32 repeat; ssize_t ret; ret = kstrtouint(buf, 0, &repeat); if (ret < 0 || repeat > attr->max) return -EINVAL; mutex_lock(&chip->mutex); ret = regmap_update_bits(chip->regmap, AW200XX_REG_PATX_T2(attr->reg), AW200XX_PAT_T2_LT_MASK, AW200XX_PAT0_T2_LT_MSB(repeat)); if (ret) goto out_unlock; ret = regmap_update_bits(chip->regmap, AW200XX_REG_PATX_T3(attr->reg), AW200XX_PAT_T3_LT_MASK, AW200XX_PAT0_T3_LT_LSB(repeat)); if (ret) goto out_unlock; ret = count; out_unlock: mutex_unlock(&chip->mutex); return ret; } #define AW200XX_DEVICE_ATTR_PAT_RW(_n, _sh, _st, _r, _step, _msk, _max) \ static AW200XX_DEVICE_ATTR_RW(_n##0, _n, _sh, _st, \ _r, _msk, _max); \ static AW200XX_DEVICE_ATTR_RW(_n##1, _n, _sh, _st, \ _r + (1 * (_step)), _msk, _max); \ static AW200XX_DEVICE_ATTR_RW(_n##2, _n, _sh, _st, \ _r + (2 * (_step)), _msk, _max) #define AW200XX_DEVICE_ATTR_PAT_RO(_n, _sh, _r, _step, _msk, _max) \ static AW200XX_DEVICE_ATTR_RO(_n##0, _n, _sh, \ _r, _msk, _max); \ static AW200XX_DEVICE_ATTR_RO(_n##1, _n, _sh, \ _r + (1 * (_step)), _msk, _max); \ static AW200XX_DEVICE_ATTR_RO(_n##2, _n, _sh, \ _r + (2 * (_step)), _msk, _max) #define AW200XX_DEFINE_ATTR_GROUP(_idx, _a0, _a1, _a2, _a3, _a4, _a5, \ _a6, _a7, _a8, _a9, _a10, _a11, _a12, _a13, _a14, _a15) \ static struct attribute *aw200xx_pattern##_idx##_attributes[] = { \ &_a0##_idx##_attr.dev_attr.attr, \ &_a1##_idx##_attr.dev_attr.attr, \ &_a2##_idx##_attr.dev_attr.attr, \ &_a3##_idx##_attr.dev_attr.attr, \ &_a4##_idx##_attr.dev_attr.attr, \ &_a5##_idx##_attr.dev_attr.attr, \ &_a6##_idx##_attr.dev_attr.attr, \ &_a7##_idx##_attr.dev_attr.attr, \ &_a8##_idx##_attr.dev_attr.attr, \ &_a9##_idx##_attr.dev_attr.attr, \ &_a10##_idx##_attr.dev_attr.attr, \ &_a11##_idx##_attr.dev_attr.attr, \ &_a12##_idx##_attr.dev_attr.attr, \ &_a13##_idx##_attr.dev_attr.attr, \ &_a14##_idx##_attr.dev_attr.attr, \ &_a15##_idx##_attr.dev_attr.attr, \ NULL}; \ static const struct attribute_group aw200xx_pattern##_idx##_group = { \ .attrs = aw200xx_pattern##_idx##_attributes, \ .name = "pattern"#_idx, \ } #define AW200XX_DEFINE_ATTR_GROUPS(...) \ AW200XX_DEFINE_ATTR_GROUP(0, __VA_ARGS__); \ AW200XX_DEFINE_ATTR_GROUP(1, __VA_ARGS__); \ AW200XX_DEFINE_ATTR_GROUP(2, __VA_ARGS__); \ static const struct attribute_group *aw200xx_pattern_groups[] = { \ &aw200xx_pattern0_group, \ &aw200xx_pattern1_group, \ &aw200xx_pattern2_group, \ NULL} AW200XX_DEVICE_ATTR_PAT_RW(rise_time, aw200xx_template_time_show, aw200xx_store_internal, AW200XX_REG_PAT0_T0, AW200XX_PAT_T_STEP, AW200XX_PAT_T1_T3_MASK, AW200XX_TEMPLATE_TIME_MAX); AW200XX_DEVICE_ATTR_PAT_RW(on_time, aw200xx_template_time_show, aw200xx_store_internal, AW200XX_REG_PAT0_T0, AW200XX_PAT_T_STEP, AW200XX_PAT_T2_T4_MASK, AW200XX_TEMPLATE_TIME_MAX); AW200XX_DEVICE_ATTR_PAT_RW(fall_time, aw200xx_template_time_show, aw200xx_store_internal, AW200XX_REG_PAT0_T1, AW200XX_PAT_T_STEP, AW200XX_PAT_T1_T3_MASK, AW200XX_TEMPLATE_TIME_MAX); AW200XX_DEVICE_ATTR_PAT_RW(off_time, aw200xx_template_time_show, aw200xx_store_internal, AW200XX_REG_PAT0_T1, AW200XX_PAT_T_STEP, AW200XX_PAT_T2_T4_MASK, AW200XX_TEMPLATE_TIME_MAX); AW200XX_DEVICE_ATTR_PAT_RW(mode, aw200xx_show_internal, aw200xx_store_internal, AW200XX_REG_PAT0_CFG, 1, AW200XX_PAT_CFG_MODE_MASK, 1); AW200XX_DEVICE_ATTR_PAT_RW(ramp, aw200xx_show_internal, aw200xx_store_internal, AW200XX_REG_PAT0_CFG, 1, AW200XX_PAT_CFG_RAMP_MASK, 1); AW200XX_DEVICE_ATTR_PAT_RW(toggle, aw200xx_show_internal, aw200xx_store_internal, AW200XX_REG_PAT0_CFG, 1, AW200XX_PAT_CFG_SWITCH_MASK, 1); AW200XX_DEVICE_ATTR_PAT_RW(loop_end_on, aw200xx_show_internal, aw200xx_store_internal, AW200XX_REG_PAT0_T2, AW200XX_PAT_T_STEP, AW200XX_PAT_T2_LE_MASK, 1); AW200XX_DEVICE_ATTR_PAT_RW(loop_begin, aw200xx_show_internal, aw200xx_store_internal, AW200XX_REG_PAT0_T2, AW200XX_PAT_T_STEP, AW200XX_PAT_T2_LB_MASK, 3); AW200XX_DEVICE_ATTR_PAT_RW(max_breathing_level, aw200xx_show_internal, aw200xx_store_internal, AW200XX_REG_PAT0_MAX_BREATH, 1, 0xFF, AW200XX_FADE_MAX); AW200XX_DEVICE_ATTR_PAT_RW(min_breathing_level, aw200xx_show_internal, aw200xx_store_internal, AW200XX_REG_PAT0_MIN_BREATH, 1, 0xFF, AW200XX_FADE_MAX); AW200XX_DEVICE_ATTR_PAT_RW(start, aw200xx_pattern_start_show, aw200xx_pattern_start_store, 0, 1, 1, 1); AW200XX_DEVICE_ATTR_PAT_RO(running, aw200xx_pattern_running_show, 0, 1, 0, 0); AW200XX_DEVICE_ATTR_PAT_RW(repeat, aw200xx_pattern_repeat_show, aw200xx_pattern_repeat_store, 0, AW200XX_PAT_T_STEP, 0, AW200XX_PAT0_T_LT_MAX); AW200XX_DEVICE_ATTR_PAT_RW(select_leds, aw200xx_pattern_select_leds_show, aw200xx_pattern_select_leds_store, 0, 1, 0, 0); AW200XX_DEVICE_ATTR_PAT_RW(clear_leds, aw200xx_pattern_clear_leds_show, aw200xx_pattern_clear_leds_store, 0, 1, 0, 0); AW200XX_DEFINE_ATTR_GROUPS(start, running, mode, ramp, toggle, repeat, loop_end_on, loop_begin, select_leds, clear_leds, max_breathing_level, min_breathing_level, rise_time, on_time, fall_time, off_time); #define IS_PAGE_DIRTY(page) ((page)->dirty_end) static void add_dirty_range(struct aw200xx_page *page, u8 start, u8 end) { if (IS_PAGE_DIRTY(page)) { page->dirty_start = min(page->dirty_start, start); page->dirty_end = max(page->dirty_end, end); } else { page->dirty_start = start; page->dirty_end = end; } } static ssize_t dim_show(struct device *dev, struct device_attribute *devattr, char *buf) { struct led_classdev *cdev = dev_get_drvdata(dev); struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); int dim = led->dim; if (dim < 0) return sysfs_emit(buf, "auto\n"); return sysfs_emit(buf, "%d\n", dim); } static int aw200xx_dim_fill(const char *buf, int *dim) { if (sysfs_streq(buf, "auto")) { *dim = -1; } else { int ret = kstrtoint(buf, 0, dim); if (ret) return ret; if (*dim > AW200XX_DIM_MAX) return -EINVAL; } return 0; } static ssize_t dim_store_queue(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct led_classdev *cdev = dev_get_drvdata(dev); struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); struct aw200xx *chip = led->chip; u32 columns = chip->cdef->display_size_columns; int dim; ssize_t ret; ret = aw200xx_dim_fill(buf, &dim); if (ret < 0) return ret; mutex_lock(&chip->mutex); if (dim >= 0) { const u8 reg = AW200XX_LED2REG(led->num, columns); chip->dim_page.buf[reg] = dim; add_dirty_range(&chip->dim_page, reg, reg + 1); } led->dim = dim; mutex_unlock(&chip->mutex); if (dim >= 0) schedule_delayed_work(&chip->drawer, 0); return count; } static ssize_t dim_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct led_classdev *cdev = dev_get_drvdata(dev); struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); struct aw200xx *chip = led->chip; u32 columns = chip->cdef->display_size_columns; int dim; ssize_t ret; ret = aw200xx_dim_fill(buf, &dim); if (ret < 0) return ret; mutex_lock(&chip->mutex); if (dim >= 0) { ret = regmap_write(chip->regmap, AW200XX_REG_DIM_PAGE1(led->num, columns), dim); if (ret) goto out_unlock; } led->dim = dim; ret = count; out_unlock: mutex_unlock(&chip->mutex); return ret; } static DEVICE_ATTR_RW(dim); static struct attribute *dim_attrs[] = { &dev_attr_dim.attr, NULL }; ATTRIBUTE_GROUPS(dim); static inline void aw200xx_fb_queue(struct aw200xx *chip) { const struct aw200xx_fb *fbdev = &chip->fb; schedule_delayed_work(&chip->drawer, msecs_to_jiffies(HZ / fbdev->refresh_rate)); } static inline void aw200xx_fb_add_screen_dirty_range(struct aw200xx *chip) { const struct aw200xx_fb *fbdev = &chip->fb; const uint8_t *fb_ptr = fbdev->buffer + fbdev->offset_y * fbdev->width; int i; for (i = 0; i < fbdev->num_leds; i++) { const u8 reg = fbdev->map[i]; const u8 fade = AW200XX_SCALE_FADE(fb_ptr[i], fbdev->fade_scale); chip->fade_page.buf[reg] = fade; chip->dim_page.buf[reg] = AW200XX_REG_FADE2DIM(fade); } add_dirty_range(&chip->fade_page, fbdev->min_reg, fbdev->max_reg + 1); add_dirty_range(&chip->dim_page, fbdev->min_reg, fbdev->max_reg + 1); } static int aw200xx_update_dirty_page(struct aw200xx *chip, struct aw200xx_page *page) { int ret; if (!IS_PAGE_DIRTY(page)) return 0; ret = regmap_raw_write(chip->regmap, page->page + page->dirty_start, page->buf + page->dirty_start, page->dirty_end - page->dirty_start); if (ret) return ret; page->dirty_start = 0; page->dirty_end = 0; return 0; } static void aw200xx_update(struct work_struct *work) { struct aw200xx *chip = container_of(work, struct aw200xx, drawer.work); mutex_lock(&chip->mutex); if (atomic_read(&chip->fb.open_count)) aw200xx_fb_add_screen_dirty_range(chip); aw200xx_update_dirty_page(chip, &chip->dim_page); aw200xx_update_dirty_page(chip, &chip->fade_page); if (atomic_read(&chip->fb.open_count)) aw200xx_fb_queue(chip); mutex_unlock(&chip->mutex); } static int aw200xx_brightness_set(struct led_classdev *cdev, enum led_brightness brightness) { struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); struct aw200xx *chip = led->chip; int dim; u32 reg; int ret; mutex_lock(&chip->mutex); reg = AW200XX_REG_DIM(led->num, chip->cdef->display_size_columns); dim = led->dim; if (dim < 0) dim = AW200XX_REG_FADE2DIM(brightness); ret = regmap_write(chip->regmap, reg, dim); if (ret) goto out_unlock; ret = regmap_write(chip->regmap, AW200XX_REG_DIM2FADE(reg), brightness); out_unlock: mutex_unlock(&chip->mutex); return ret; } static int aw200xx_brightness_set_queue(struct led_classdev *cdev, enum led_brightness brightness) { struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev); struct aw200xx *chip = led->chip; u32 reg = AW200XX_LED2REG(led->num, chip->cdef->display_size_columns); mutex_lock(&chip->mutex); if (led->dim < 0) { int dim = AW200XX_REG_FADE2DIM(brightness); chip->dim_page.buf[reg] = dim; add_dirty_range(&chip->dim_page, reg, reg + 1); } chip->fade_page.buf[reg] = brightness; add_dirty_range(&chip->fade_page, reg, reg + 1); mutex_unlock(&chip->mutex); schedule_delayed_work(&chip->drawer, 0); return 0; } static irqreturn_t aw200xx_irq_thread(int irq, void *dev_id) { struct aw200xx *chip = dev_id; unsigned long pattern_state; u32 interrupt_state; int i; int ret; mutex_lock(&chip->mutex); ret = regmap_read(chip->regmap, AW200XX_REG_ISRFLT, &interrupt_state); mutex_unlock(&chip->mutex); if (ret) { dev_err(&chip->client->dev, "Failed to get interrupt status: %d\n", ret); return IRQ_HANDLED; } pattern_state = FIELD_GET(AW200XX_ISRFLT_PATIS_MASK, interrupt_state); for_each_set_bit(i, &pattern_state, AW200XX_PATTERN_MAX) { char dir[sizeof("patternx")]; snprintf(dir, sizeof(dir), "pattern%d", i); sysfs_notify(&chip->client->dev.kobj, dir, "running"); } return IRQ_HANDLED; } static int aw200xx_setup_interrupts(struct aw200xx *chip) { struct i2c_client *i2c = chip->client; int ret; if (i2c->irq <= 0) return 0; ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, aw200xx_irq_thread, IRQF_ONESHOT, i2c->name, chip); if (ret) return dev_err_probe(&i2c->dev, ret, "Failed to request irq\n"); ret = regmap_update_bits(chip->regmap, AW200XX_REG_PATCR, AW200XX_PATCR_PAT_IE_MASK, AW200XX_PATCR_PAT_IE_ALL); if (ret) dev_err_probe(&i2c->dev, ret, "Failed to enable interrupts\n"); return ret; } static u32 aw200xx_imax_from_global(const struct aw200xx *const chip, u32 global_imax_uA) { u64 led_imax_uA; /* * The output current of each LED (see p.14 of datasheet for formula): * Iled = Imax * (dim / 63) * ((fade + 1) / 256) * duty * * The value of duty is determined by the following formula: * duty = (592us / 600.5us) * (1 / (display_rows + 1)) * * Calculated for the maximum values of fade and dim. * We divide by 1000 because we earlier multiplied by 1000 to improve * accuracy when calculating the duty. */ led_imax_uA = global_imax_uA * AW200XX_DUTY_RATIO(chip->display_rows); do_div(led_imax_uA, MILLI); return led_imax_uA; } static u32 aw200xx_imax_to_global(const struct aw200xx *const chip, u32 led_imax_uA) { u32 duty = AW200XX_DUTY_RATIO(chip->display_rows); /* The output current of each LED (see p.14 of datasheet for formula) */ return (led_imax_uA * 1000U) / duty; } #define AW200XX_IMAX_MULTIPLIER1 10000 #define AW200XX_IMAX_MULTIPLIER2 3333 #define AW200XX_IMAX_BASE_VAL1 0 #define AW200XX_IMAX_BASE_VAL2 8 /* * The AW200XX has a 4-bit register (GCCR) to configure the global current, * which ranges from 3.3mA to 160mA. The following table indicates the values * of the global current, divided into two parts: * * +-----------+-----------------+-----------+-----------------+ * | reg value | global max (mA) | reg value | global max (mA) | * +-----------+-----------------+-----------+-----------------+ * | 0 | 10 | 8 | 3.3 | * | 1 | 20 | 9 | 6.7 | * | 2 | 30 | 10 | 10 | * | 3 | 40 | 11 | 13.3 | * | 4 | 60 | 12 | 20 | * | 5 | 80 | 13 | 26.7 | * | 6 | 120 | 14 | 40 | * | 7 | 160 | 15 | 53.3 | * +-----------+-----------------+-----------+-----------------+ * * The left part with a multiplier of 10, and the right part with a multiplier * of 3.3. * So we have two formulas to calculate the global current: * for the left part of the table: * imax = coefficient * 10 * * for the right part of the table: * imax = coefficient * 3.3 * * The coefficient table consists of the following values: * 1, 2, 3, 4, 6, 8, 12, 16. */ static int aw200xx_set_imax(const struct aw200xx *const chip, u32 led_imax_uA) { u32 g_imax_uA = aw200xx_imax_to_global(chip, led_imax_uA); static const u32 coeff_table[] = {1, 2, 3, 4, 6, 8, 12, 16}; u32 gccr_imax = UINT_MAX; u32 cur_imax = 0; int i; for (i = 0; i < ARRAY_SIZE(coeff_table); i++) { u32 imax; /* select closest ones */ imax = coeff_table[i] * AW200XX_IMAX_MULTIPLIER1; if (g_imax_uA >= imax && imax > cur_imax) { cur_imax = imax; gccr_imax = i + AW200XX_IMAX_BASE_VAL1; } imax = coeff_table[i] * AW200XX_IMAX_MULTIPLIER2; imax = DIV_ROUND_CLOSEST(imax, 100) * 100; if (g_imax_uA >= imax && imax > cur_imax) { cur_imax = imax; gccr_imax = i + AW200XX_IMAX_BASE_VAL2; } } if (gccr_imax == UINT_MAX) return -EINVAL; return regmap_update_bits(chip->regmap, AW200XX_REG_GCCR, AW200XX_GCCR_IMAX_MASK, AW200XX_GCCR_IMAX(gccr_imax)); } static int aw200xx_chip_reset(const struct aw200xx *const chip) { int ret; ret = regmap_write(chip->regmap, AW200XX_REG_RSTR, AW200XX_RSTR_RESET); if (ret) return ret; /* According to the datasheet software reset takes at least 1ms */ fsleep(1000); regcache_mark_dirty(chip->regmap); return regmap_write(chip->regmap, AW200XX_REG_FCD, AW200XX_FCD_CLEAR); } static int aw200xx_chip_init(const struct aw200xx *const chip) { int ret; ret = aw200xx_set_imax(chip, chip->imax); if (ret) return ret; ret = regmap_write(chip->regmap, AW200XX_REG_DSIZE, chip->display_rows - 1); if (ret) return ret; ret = regmap_write(chip->regmap, AW200XX_REG_SLPCR, AW200XX_SLPCR_ACTIVE); if (ret) return ret; return regmap_update_bits(chip->regmap, AW200XX_REG_GCCR, AW200XX_GCCR_ALLON, AW200XX_GCCR_ALLON); } static int aw200xx_chip_check(const struct aw200xx *const chip) { struct device *dev = &chip->client->dev; u32 chipid; int ret; ret = regmap_read(chip->regmap, AW200XX_REG_IDR, &chipid); if (ret) return dev_err_probe(dev, ret, "Failed to read chip ID\n"); if (chipid != AW200XX_IDR_CHIPID) return dev_err_probe(dev, -ENODEV, "Chip reported wrong ID: %x\n", chipid); return 0; } static void aw200xx_enable(const struct aw200xx *const chip) { gpiod_set_value_cansleep(chip->hwen, 1); /* * After HWEN pin set high the chip begins to load the OTP information, * which takes 200us to complete. About 200us wait time is needed for * internal oscillator startup and display SRAM initialization. After * display SRAM initialization, the registers in page1 to page5 can be * configured via i2c interface. */ fsleep(400); } static void aw200xx_disable(const struct aw200xx *const chip) { gpiod_set_value_cansleep(chip->hwen, 0); } static int aw200xx_get_display_rows(struct aw200xx *chip, int max_reg) { return max_reg / chip->cdef->display_size_columns + 1; } static void aw200xx_probe_dt_leds(struct device *dev, struct aw200xx *chip, struct aw200xx_led_data *leds, bool *keep_state) { u32 current_min, current_max, min_uA; int count = 0; int max_source = -1; int ret; int i; min_uA = UINT_MAX; device_for_each_child_node_scoped(dev, child) { struct aw200xx_led_data *led = &leds[count++]; u32 source; led->num = -1; ret = fwnode_property_read_u32(child, "reg", &source); if (ret) { dev_err(dev, "Missing reg property\n"); chip->num_leds--; continue; } if (source >= chip->cdef->channels) { dev_err(dev, "LED reg %u out of range (max %u)\n", source, chip->cdef->channels); chip->num_leds--; continue; } max_source = max_t(int, max_source, source); ret = fwnode_property_read_u32(child, "led-max-microamp", &led->imax); if (ret) { dev_info(&chip->client->dev, "DT property led-max-microamp is missing\n"); } else { min_uA = min(min_uA, led->imax); } led->default_state = led_init_default_state_get(child); led->num = source; } if (max_source < 0) return; chip->display_rows = aw200xx_get_display_rows(chip, max_source); current_max = aw200xx_imax_from_global(chip, AW200XX_IMAX_MAX_uA); current_min = aw200xx_imax_from_global(chip, AW200XX_IMAX_MIN_uA); for (i = 0; i < count; i++) { struct aw200xx_led_data *led = &leds[i]; if (led->num < 0) continue; if (led->imax && (led->imax < current_min || led->imax > current_max)) { dev_err(dev, "Invalid value %u for led-max-microamp\n", led->imax); chip->num_leds--; led->num = -1; continue; } if (led->default_state == LEDS_DEFSTATE_KEEP) *keep_state = true; } if (min_uA == U32_MAX) min_uA = aw200xx_imax_from_global(chip, AW200XX_IMAX_DEFAULT_uA); chip->imax = min_uA; } static void aw200xx_probe_register_leds(struct device *dev, struct aw200xx *chip, const struct aw200xx_led_data *led_data, bool keep_state) { int i = 0, data_index = 0; if (chip->queue_mode) dev_attr_dim.store = dim_store_queue; device_for_each_child_node_scoped(dev, child) { struct led_init_data init_data = {}; struct aw200xx_led *led; const struct aw200xx_led_data *data = &led_data[data_index++]; int ret; if (data->num < 0) continue; led = &chip->leds[i]; led->dim = -1; led->num = data->num; led->chip = chip; led->cdev.brightness_set_blocking = chip->queue_mode ? aw200xx_brightness_set_queue : aw200xx_brightness_set; led->cdev.max_brightness = AW200XX_FADE_MAX; led->cdev.groups = dim_groups; init_data.fwnode = child; switch (led_data->default_state) { case LEDS_DEFSTATE_ON: led->cdev.brightness = AW200XX_FADE_MAX; aw200xx_brightness_set(&led->cdev, AW200XX_FADE_MAX); break; case LEDS_DEFSTATE_OFF: if (keep_state) aw200xx_brightness_set(&led->cdev, 0); break; default: break; } ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data); if (ret) break; i++; } chip->num_pattern_leds += chip->num_leds; return; } static int aw200xx_bl_update_status(struct backlight_device *bl) { struct aw200xx *chip = bl_get_data(bl); int brightness = bl->props.brightness; if (bl->props.power != FB_BLANK_UNBLANK || bl->props.state & BL_CORE_FBBLANK) brightness = 0; mutex_lock(&chip->mutex); chip->fb.fade_scale = brightness; aw200xx_fb_add_screen_dirty_range(chip); mutex_unlock(&chip->mutex); schedule_delayed_work(&chip->drawer, 0); return 0; } static bool aw200xx_bl_controls_device(struct backlight_device *bl, struct device *display_dev) { const struct aw200xx *chip = bl_get_data(bl); return display_dev == &chip->client->dev; } static const struct backlight_ops aw200xx_bl_ops = { .update_status = aw200xx_bl_update_status, .controls_device = aw200xx_bl_controls_device, }; static int aw200xx_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { struct aw200xx *chip = info->par; mutex_lock(&chip->mutex); chip->fb.offset_y = var->yoffset; mutex_unlock(&chip->mutex); return 0; } static int aw200xx_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) { struct aw200xx *chip = info->par; return vm_insert_page(vma, vma->vm_start, virt_to_page(chip->fb.buffer)); } static int aw200xx_fb_open(struct fb_info *info, int user) { struct aw200xx *chip = info->par; int i; mutex_lock(&chip->mutex); for (i = 0; i < AW200XX_PATTERN_MAX; i++) if (find_next_bit(chip->pattern_leds[i], chip->num_pattern_leds, chip->num_leds) != chip->num_pattern_leds) { mutex_unlock(&chip->mutex); return -EBUSY; } atomic_inc(&chip->fb.open_count); mutex_unlock(&chip->mutex); schedule_delayed_work(&chip->drawer, 0); return 0; } static int aw200xx_fb_release(struct fb_info *info, int user) { struct aw200xx *chip = info->par; if (atomic_dec_and_test(&chip->fb.open_count)) { mutex_lock(&chip->mutex); aw200xx_fb_add_screen_dirty_range(chip); aw200xx_fb_queue(chip); mutex_unlock(&chip->mutex); } return 0; } static const struct fb_fix_screeninfo aw200xx_fb_fix = { .id = "aw200xx-fb", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_PSEUDOCOLOR, .accel = FB_ACCEL_NONE, }; static const struct fb_var_screeninfo aw200xx_fb_var = { .bits_per_pixel = 8, .grayscale = 1, .red = { 0, 255, 0 }, .green = { 0, 255, 0 }, .blue = { 0, 255, 0 }, .vmode = FB_VMODE_NONINTERLACED, }; static const struct fb_ops aw200xx_fb_ops = { .owner = THIS_MODULE, .fb_open = aw200xx_fb_open, .fb_release = aw200xx_fb_release, .fb_read = fb_sys_read, .fb_write = fb_sys_write, .fb_fillrect = sys_fillrect, .fb_copyarea = sys_copyarea, .fb_imageblit = sys_imageblit, .fb_mmap = aw200xx_fb_mmap, .fb_pan_display = aw200xx_fb_pan_display, }; static void aw200xx_probe_find_fb_reg_range(struct aw200xx *chip) { struct aw200xx_fb *fbdev = &chip->fb; int i; fbdev->max_reg = 0; fbdev->min_reg = chip->cdef->channels - 1; for (i = 0; i < fbdev->height * fbdev->width; i++) { const uint8_t reg = fbdev->map[i]; fbdev->max_reg = max(fbdev->max_reg, reg); fbdev->min_reg = min(fbdev->min_reg, reg); } } static int aw200xx_probe_add_backlight(struct aw200xx *chip, struct device *dev) { struct aw200xx_fb *fbdev = &chip->fb; struct device_node *np = dev_of_node(dev); struct backlight_properties bl_props; u32 brightness = AW200XX_FADE_MAX; of_property_read_u32(np, "fb-backlight", &brightness); if (brightness > AW200XX_FADE_MAX) { dev_err(dev, "property 'fb-backlight' is out of range [0-%d]\n", (int)AW200XX_FADE_MAX); return -EINVAL; } fbdev->fade_scale = brightness; memset(&bl_props, 0, sizeof(struct backlight_properties)); bl_props.type = BACKLIGHT_RAW; bl_props.max_brightness = AW200XX_FADE_MAX; bl_props.brightness = brightness; fbdev->bl = devm_backlight_device_register(dev, "aw200xx-bl", dev, chip, &aw200xx_bl_ops, &bl_props); if (IS_ERR(fbdev->bl)) { dev_err(dev, "failed to register backlight\n"); return PTR_ERR(fbdev->bl); } return 0; } static int dev_err_prop(struct device *dev, const char *prop_name, int err) { dev_err(dev, "Failed to read '%s' property: %d\n", prop_name, err); return err; } static int aw200xx_probe_dt_fb(struct aw200xx *chip, struct device *dev) { struct aw200xx_fb *fbdev = &chip->fb; struct device_node *np = dev_of_node(dev); u32 imax, size; int ret, display_rows; if (!of_property_present(np, "fb-map")) return 0; ret = of_property_read_u32(np, "fb-width", &fbdev->width); if (ret) return dev_err_prop(dev, "fb-width", -EINVAL); ret = of_property_read_u32(np, "fb-height", &fbdev->height); if (ret) return dev_err_prop(dev, "fb-height", -EINVAL); ret = of_property_read_u32(np, "fb-refresh-rate", &fbdev->refresh_rate); if (ret) return dev_err_prop(dev, "fb-refresh-rate", -EINVAL); size = of_property_count_u8_elems(np, "fb-map"); if (size < 0) return dev_err_prop(dev, "fb-map", -EINVAL); if (size > chip->cdef->channels) { dev_err(dev, "Display size %d is greater than led count\n", size); return -EINVAL; } if (size != fbdev->height * fbdev->width) { dev_err(dev, "Map size (%d) is not equal to display size\n", size); return -EINVAL; } fbdev->map = devm_kzalloc(dev, size, GFP_KERNEL); if (!fbdev->map) return -ENOMEM; ret = of_property_read_u8_array(np, "fb-map", fbdev->map, size); if (ret) return ret; aw200xx_probe_find_fb_reg_range(chip); display_rows = aw200xx_get_display_rows(chip, fbdev->max_reg); chip->display_rows = max(display_rows, chip->display_rows); ret = of_property_read_u32(np, "fb-led-max-microamp", &imax); if (!ret) { const u32 current_min = aw200xx_imax_from_global(chip, AW200XX_IMAX_MIN_uA); const u32 current_max = aw200xx_imax_from_global(chip, AW200XX_IMAX_MAX_uA); if (imax < current_min || imax > current_max) { dev_err(dev, "Framebuffer led-max-microamp is out of range\n"); return -EINVAL; } chip->imax = min(chip->imax, imax); } else { dev_info(dev, "DT property led-max-microamp is missing\n"); } chip->queue_mode = true; chip->fb.num_leds = size; return 0; } static int aw200xx_probe_register_fb(struct device *dev, struct aw200xx *chip) { struct aw200xx_fb *fbdev = &chip->fb; const int size = fbdev->width * fbdev->width; int ret; if (!fbdev->num_leds) return 0; if (size * FB_BUFFER_COUNT > PAGE_SIZE) return -EINVAL; fbdev->buffer = (uint8_t *)get_zeroed_page(GFP_KERNEL); if (!fbdev->buffer) return -ENOMEM; fbdev->info = framebuffer_alloc(0, dev); if (!fbdev->info) { ret = -ENOMEM; goto err_fb_alloc; } fbdev->info->fbops = &aw200xx_fb_ops; fbdev->info->screen_buffer = (char __iomem *)fbdev->buffer; fbdev->info->screen_size = size; fbdev->info->fix = aw200xx_fb_fix; fbdev->info->var = aw200xx_fb_var; fbdev->info->pseudo_palette = NULL; fbdev->info->flags = FBINFO_VIRTFB; fbdev->info->par = chip; fbdev->info->fix.smem_len = size * FB_BUFFER_COUNT; fbdev->info->fix.line_length = fbdev->width; fbdev->info->fix.ypanstep = fbdev->height; fbdev->info->var.xres = fbdev->width; fbdev->info->var.yres = fbdev->height; fbdev->info->var.xres_virtual = fbdev->width; fbdev->info->var.yres_virtual = fbdev->height * FB_BUFFER_COUNT; ret = register_framebuffer(fbdev->info); if (ret) { dev_err(dev, "Register framebuffer failed\n"); goto err_register_fb; } ret = aw200xx_probe_add_backlight(chip, dev); if (ret) goto err_probe_bl; chip->num_pattern_leds += fbdev->num_leds; return 0; err_probe_bl: unregister_framebuffer(fbdev->info); err_register_fb: framebuffer_release(fbdev->info); err_fb_alloc: free_page((unsigned long) fbdev->buffer); return ret; } static const struct regmap_range_cfg aw200xx_ranges[] = { { .name = "aw200xx", .range_min = 0, .range_max = AW200XX_REG_MAX, .selector_reg = AW200XX_REG_PAGE, .selector_mask = AW200XX_PAGE_MASK, .selector_shift = AW200XX_PAGE_SHIFT, .window_start = 0, .window_len = AW200XX_PAGE_SIZE, }, }; static const struct regmap_range aw200xx_writeonly_ranges[] = { regmap_reg_range(AW200XX_REG(AW200XX_PAGE1, 0x00), AW200XX_REG_MAX), }; static const struct regmap_access_table aw200xx_readable_table = { .no_ranges = aw200xx_writeonly_ranges, .n_no_ranges = ARRAY_SIZE(aw200xx_writeonly_ranges), }; static const struct regmap_range aw200xx_readonly_ranges[] = { regmap_reg_range(AW200XX_REG_IDR, AW200XX_REG_IDR), regmap_reg_range(AW200XX_REG_ISRFLT, AW200XX_REG_ISRFLT), }; static const struct regmap_access_table aw200xx_writeable_table = { .no_ranges = aw200xx_readonly_ranges, .n_no_ranges = ARRAY_SIZE(aw200xx_readonly_ranges), }; static const struct regmap_range aw200xx_volatile_registers[] = { regmap_reg_range(AW200XX_REG_ISRFLT, AW200XX_REG_ISRFLT), regmap_reg_range(AW200XX_REG_PATGO, AW200XX_REG_PATGO), }; static const struct regmap_access_table aw200xx_volatile_table = { .yes_ranges = aw200xx_volatile_registers, .n_yes_ranges = ARRAY_SIZE(aw200xx_volatile_registers), }; static const struct regmap_config aw200xx_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = AW200XX_REG_MAX, .ranges = aw200xx_ranges, .num_ranges = ARRAY_SIZE(aw200xx_ranges), .rd_table = &aw200xx_readable_table, .wr_table = &aw200xx_writeable_table, .volatile_table = &aw200xx_volatile_table, .cache_type = REGCACHE_MAPLE, .disable_locking = true, }; static void aw200xx_chip_reset_action(void *data) { aw200xx_chip_reset(data); } static void aw200xx_disable_action(void *data) { aw200xx_disable(data); } static int aw200xx_probe(struct i2c_client *client) { const struct aw200xx_chipdef *cdef; struct aw200xx *chip; struct aw200xx_led_data *leds; bool keep_state = false; int count; int ret; cdef = device_get_match_data(&client->dev); if (!cdef) return -ENODEV; count = device_get_child_node_count(&client->dev); if (!count || count > cdef->channels) return dev_err_probe(&client->dev, -EINVAL, "Incorrect number of leds (%d)", count); chip = devm_kzalloc(&client->dev, struct_size(chip, leds, count), GFP_KERNEL); if (!chip) return -ENOMEM; chip->cdef = cdef; chip->num_leds = count; chip->client = client; i2c_set_clientdata(client, chip); leds = devm_kcalloc(&client->dev, count, sizeof(struct aw200xx_led_data), GFP_KERNEL); if (!leds) return -ENOMEM; aw200xx_probe_dt_leds(&client->dev, chip, leds, &keep_state); ret = aw200xx_probe_dt_fb(chip, &client->dev); if (ret) { dev_err_probe(&client->dev, ret, "Framebuffer probe failed\n"); return ret; } if (!chip->num_leds && !chip->fb.num_leds) return dev_err_probe(&client->dev, -EINVAL, "No valid led definitions found\n"); if (chip->queue_mode) { chip->dim_page.buf = devm_kzalloc(&client->dev, chip->cdef->channels, GFP_KERNEL); if (!chip->dim_page.buf) return -ENOMEM; chip->dim_page.page = AW200XX_REG(AW200XX_PAGE1, 0); chip->fade_page.buf = devm_kzalloc(&client->dev, chip->cdef->channels, GFP_KERNEL); if (!chip->fade_page.buf) return -ENOMEM; chip->fade_page.page = AW200XX_REG(AW200XX_PAGE2, 0); INIT_DELAYED_WORK(&chip->drawer, aw200xx_update); } chip->regmap = devm_regmap_init_i2c(client, &aw200xx_regmap_config); if (IS_ERR(chip->regmap)) return PTR_ERR(chip->regmap); chip->hwen = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_HIGH); if (IS_ERR(chip->hwen)) return dev_err_probe(&client->dev, PTR_ERR(chip->hwen), "Cannot get enable GPIO"); aw200xx_enable(chip); ret = devm_add_action(&client->dev, aw200xx_disable_action, chip); if (ret) return ret; ret = aw200xx_chip_check(chip); if (ret) return ret; ret = devm_mutex_init(&client->dev, &chip->mutex); if (ret) return ret; if (!keep_state) { ret = aw200xx_chip_reset(chip); if (ret) return ret; } ret = devm_add_action(&client->dev, aw200xx_chip_reset_action, chip); if (ret) return ret; if (!keep_state) { ret = aw200xx_chip_init(chip); if (ret) return ret; } ret = aw200xx_setup_interrupts(chip); if (ret) return ret; aw200xx_probe_register_leds(&client->dev, chip, leds, keep_state); ret = aw200xx_probe_register_fb(&client->dev, chip); if (ret) return ret; devm_kfree(&client->dev, leds); return 0; } static void aw200xx_remove(struct i2c_client *client) { struct aw200xx *chip = i2c_get_clientdata(client); struct aw200xx_fb *fbdev = &chip->fb; if (fbdev) { mutex_lock(&chip->mutex); devm_backlight_device_unregister(&client->dev, fbdev->bl); unregister_framebuffer(fbdev->info); framebuffer_release(fbdev->info); free_page((unsigned long) fbdev->buffer); mutex_unlock(&chip->mutex); } if (chip->queue_mode) disable_delayed_work_sync(&chip->drawer); } static const struct aw200xx_chipdef aw20036_cdef = { .channels = 36, .display_size_rows_max = 3, .display_size_columns = 12, }; static const struct aw200xx_chipdef aw20054_cdef = { .channels = 54, .display_size_rows_max = 6, .display_size_columns = 9, }; static const struct aw200xx_chipdef aw20072_cdef = { .channels = 72, .display_size_rows_max = 6, .display_size_columns = 12, }; static const struct aw200xx_chipdef aw20108_cdef = { .channels = 108, .display_size_rows_max = 9, .display_size_columns = 12, }; static const struct i2c_device_id aw200xx_id[] = { { "aw20036" }, { "aw20054" }, { "aw20072" }, { "aw20108" }, {} }; MODULE_DEVICE_TABLE(i2c, aw200xx_id); static const struct of_device_id aw200xx_match_table[] = { { .compatible = "awinic,aw20036", .data = &aw20036_cdef, }, { .compatible = "awinic,aw20054", .data = &aw20054_cdef, }, { .compatible = "awinic,aw20072", .data = &aw20072_cdef, }, { .compatible = "awinic,aw20108", .data = &aw20108_cdef, }, {} }; MODULE_DEVICE_TABLE(of, aw200xx_match_table); static struct i2c_driver aw200xx_driver = { .driver = { .name = "aw200xx", .of_match_table = aw200xx_match_table, .dev_groups = aw200xx_pattern_groups, }, .probe = aw200xx_probe, .remove = aw200xx_remove, .id_table = aw200xx_id, }; module_i2c_driver(aw200xx_driver); MODULE_AUTHOR("Martin Kurbanov "); MODULE_DESCRIPTION("AW200XX LED driver"); MODULE_LICENSE("GPL");