#include #include #include #include #include "hui_internal.h" #include "hui_generic.h" #define ID_LC882x 0 struct lc882x_chain; struct lc882x_instance { struct hui_generic_led led; struct lc882x_chain *chain; u8 *buf; unsigned int index; }; struct lc882x_chain { struct spi_device *dev; struct delayed_work work; unsigned int count; u8 *buf; struct lc882x_instance insts[]; }; static void lc882x_chain_flush(struct work_struct *_work) { struct lc882x_chain *chain = container_of(_work, struct lc882x_chain, work.work); spi_write(chain->dev, chain->buf, 4 * (chain->count + 2)); print_hex_dump_bytes("send : ", DUMP_PREFIX_NONE, chain->buf, 4 * (chain->count + 2)); } static int lc882x_out(struct hui_generic_led *_led, struct led_color color) { struct lc882x_instance *inst = container_of(_led, struct lc882x_instance, led); inst->buf[0] = 0xC0 | (color.brightness >> 3); inst->buf[1] = color.blue; inst->buf[2] = color.green; inst->buf[3] = color.red; schedule_delayed_work(&inst->chain->work, msecs_to_jiffies(10)); return 0; } static const struct of_device_id avm_hui_lc882x_of_ids[] = { { .compatible = "avm,hui-generic-lc882x", .data = (void *)ID_LC882x, }, {}, }; static int avm_hui_lc882x_probe(struct spi_device *spi) { struct device_node *def; const struct of_device_id *match; struct lc882x_chain *chain; unsigned int count, i; match = of_match_device(avm_hui_lc882x_of_ids, &spi->dev); if (!match) { pr_err("Did not match DT!\n"); return -ENODEV; } count = of_get_child_count(spi->dev.of_node); if (count <= 0) return -EINVAL; chain = devm_kzalloc(&spi->dev, sizeof(struct lc882x_chain) + count * sizeof(struct lc882x_instance), GFP_KERNEL); if (!chain) return -ENOMEM; chain->dev = spi; chain->count = count; chain->buf = devm_kzalloc(&spi->dev, sizeof(u32) * (count + 2), GFP_KERNEL); if (!chain->buf) return -ENOMEM; memset(chain->buf + (count + 1) * 4, 0xFF, 4); INIT_DELAYED_WORK(&chain->work, lc882x_chain_flush); i = 0; for_each_child_of_node(spi->dev.of_node, def) { const char *name = def->name; const char *type; int err; struct lc882x_instance *inst = &chain->insts[i]; inst->chain = chain; inst->led.dev = &spi->dev; inst->led.name = name; inst->led.code = BLINK_CODE(.type = blink_off); inst->led.out = lc882x_out; err = of_property_read_string(def, "led-type", &type); if (err == -EINVAL) type = name; else if (err) return err; err = of_property_read_u32(def, "index", &inst->index); if (err) return err; if (inst->index > count) { pr_err("index larger than count\n"); return -EINVAL; } inst->buf = chain->buf + (inst->index + 1) * 4; inst->led.handle = avm_hui_add_led( name, type, def, &hui_generic_ops, &inst->led); // TODO add as res to free later if (IS_ERR(inst->led.handle)) { return PTR_ERR(inst->led.handle); } err = hui_generic_blink_add_led(&inst->led); if (err) return err; i++; } return 0; } static const struct spi_device_id avm_hui_lc882x_ids[] = { { "avm_hui_lc882x", ID_LC882x }, {}, }; MODULE_DEVICE_TABLE(spi, avm_hui_lc882x_ids); struct spi_driver hui_generic_lc882x = { .driver = { .name = "avm_hui_lc882x", .of_match_table = of_match_ptr(avm_hui_lc882x_of_ids), }, .id_table = avm_hui_lc882x_ids, .probe = avm_hui_lc882x_probe, };