// SPDX-License-Identifier: GPL-2.0 /* * MAX6958/6959 7-segment LED display controller * Datasheet: * https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6958-MAX6959.pdf * * Copyright (c) 2024, Intel Corporation. * Author: Andy Shevchenko */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "line-display.h" /* Registers */ #define REG_DECODE_MODE 0x01 #define REG_INTENSITY 0x02 #define REG_SCAN_LIMIT 0x03 #define REG_CONFIGURATION 0x04 #define REG_CONFIGURATION_S_BIT BIT(0) #define REG_DIGIT(x) (0x20 + (x)) #define REG_DIGIT0 0x20 #define REG_DIGIT1 0x21 #define REG_DIGIT2 0x22 #define REG_DIGIT3 0x23 #define REG_SEGMENTS 0x24 #define REG_MAX REG_SEGMENTS struct max6959_priv { struct linedisp linedisp; struct delayed_work work; struct regmap *regmap; }; static void max6959_disp_update(struct work_struct *work) { struct max6959_priv *priv = container_of(work, struct max6959_priv, work.work); struct linedisp *linedisp = &priv->linedisp; struct linedisp_map *map = linedisp->map; char *s = linedisp->buf; u8 buf[4]; /* Map segments according to datasheet */ buf[0] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1; buf[1] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1; buf[2] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1; buf[3] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1; regmap_bulk_write(priv->regmap, REG_DIGIT(0), buf, ARRAY_SIZE(buf)); } static int max6959_linedisp_get_map_type(struct linedisp *linedisp) { struct max6959_priv *priv = container_of(linedisp, struct max6959_priv, linedisp); INIT_DELAYED_WORK(&priv->work, max6959_disp_update); return LINEDISP_MAP_SEG7; } static void max6959_linedisp_update(struct linedisp *linedisp) { struct max6959_priv *priv = container_of(linedisp, struct max6959_priv, linedisp); schedule_delayed_work(&priv->work, 0); } static const struct linedisp_ops max6959_linedisp_ops = { .get_map_type = max6959_linedisp_get_map_type, .update = max6959_linedisp_update, }; static int max6959_enable(struct max6959_priv *priv, bool enable) { u8 mask = REG_CONFIGURATION_S_BIT; u8 value = enable ? mask : 0; return regmap_update_bits(priv->regmap, REG_CONFIGURATION, mask, value); } static void max6959_power_off(void *priv) { max6959_enable(priv, false); } static int max6959_power_on(struct max6959_priv *priv) { struct device *dev = regmap_get_device(priv->regmap); int ret; ret = max6959_enable(priv, true); if (ret) return ret; return devm_add_action_or_reset(dev, max6959_power_off, priv); } static const struct regmap_config max6959_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = REG_MAX, .cache_type = REGCACHE_MAPLE, }; static int max6959_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct max6959_priv *priv; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->regmap = devm_regmap_init_i2c(client, &max6959_regmap_config); if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap); ret = max6959_power_on(priv); if (ret) return ret; ret = linedisp_register(&priv->linedisp, dev, 4, &max6959_linedisp_ops); if (ret) return ret; i2c_set_clientdata(client, priv); return 0; } static void max6959_i2c_remove(struct i2c_client *client) { struct max6959_priv *priv = i2c_get_clientdata(client); cancel_delayed_work_sync(&priv->work); linedisp_unregister(&priv->linedisp); } static int max6959_suspend(struct device *dev) { return max6959_enable(dev_get_drvdata(dev), false); } static int max6959_resume(struct device *dev) { return max6959_enable(dev_get_drvdata(dev), true); } static DEFINE_SIMPLE_DEV_PM_OPS(max6959_pm_ops, max6959_suspend, max6959_resume); static const struct i2c_device_id max6959_i2c_id[] = { { "max6959" }, { } }; MODULE_DEVICE_TABLE(i2c, max6959_i2c_id); static const struct of_device_id max6959_of_table[] = { { .compatible = "maxim,max6959" }, { } }; MODULE_DEVICE_TABLE(of, max6959_of_table); static struct i2c_driver max6959_i2c_driver = { .driver = { .name = "max6959", .pm = pm_sleep_ptr(&max6959_pm_ops), .of_match_table = max6959_of_table, }, .probe = max6959_i2c_probe, .remove = max6959_i2c_remove, .id_table = max6959_i2c_id, }; module_i2c_driver(max6959_i2c_driver); MODULE_DESCRIPTION("MAX6958/6959 7-segment LED controller"); MODULE_AUTHOR("Andy Shevchenko "); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("LINEDISP");