mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
203 lines
5.0 KiB
203 lines
5.0 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
// LED Multicolor class interface |
|
// Copyright (C) 2019-20 Texas Instruments Incorporated - http://www.ti.com/ |
|
// Author: Dan Murphy <[email protected]> |
|
|
|
#include <linux/device.h> |
|
#include <linux/init.h> |
|
#include <linux/led-class-multicolor.h> |
|
#include <linux/module.h> |
|
#include <linux/slab.h> |
|
#include <linux/uaccess.h> |
|
|
|
#include "leds.h" |
|
|
|
int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev, |
|
enum led_brightness brightness) |
|
{ |
|
struct led_classdev *led_cdev = &mcled_cdev->led_cdev; |
|
int i; |
|
|
|
for (i = 0; i < mcled_cdev->num_colors; i++) |
|
mcled_cdev->subled_info[i].brightness = brightness * |
|
mcled_cdev->subled_info[i].intensity / |
|
led_cdev->max_brightness; |
|
|
|
return 0; |
|
} |
|
EXPORT_SYMBOL_GPL(led_mc_calc_color_components); |
|
|
|
static ssize_t multi_intensity_store(struct device *dev, |
|
struct device_attribute *intensity_attr, |
|
const char *buf, size_t size) |
|
{ |
|
struct led_classdev *led_cdev = dev_get_drvdata(dev); |
|
struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev); |
|
int nrchars, offset = 0; |
|
int intensity_value[LED_COLOR_ID_MAX]; |
|
int i; |
|
ssize_t ret; |
|
|
|
mutex_lock(&led_cdev->led_access); |
|
|
|
for (i = 0; i < mcled_cdev->num_colors; i++) { |
|
ret = sscanf(buf + offset, "%i%n", |
|
&intensity_value[i], &nrchars); |
|
if (ret != 1) { |
|
ret = -EINVAL; |
|
goto err_out; |
|
} |
|
offset += nrchars; |
|
} |
|
|
|
offset++; |
|
if (offset < size) { |
|
ret = -EINVAL; |
|
goto err_out; |
|
} |
|
|
|
for (i = 0; i < mcled_cdev->num_colors; i++) |
|
mcled_cdev->subled_info[i].intensity = intensity_value[i]; |
|
|
|
led_set_brightness(led_cdev, led_cdev->brightness); |
|
ret = size; |
|
err_out: |
|
mutex_unlock(&led_cdev->led_access); |
|
return ret; |
|
} |
|
|
|
static ssize_t multi_intensity_show(struct device *dev, |
|
struct device_attribute *intensity_attr, |
|
char *buf) |
|
{ |
|
struct led_classdev *led_cdev = dev_get_drvdata(dev); |
|
struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev); |
|
int len = 0; |
|
int i; |
|
|
|
for (i = 0; i < mcled_cdev->num_colors; i++) { |
|
len += sprintf(buf + len, "%d", |
|
mcled_cdev->subled_info[i].intensity); |
|
if (i < mcled_cdev->num_colors - 1) |
|
len += sprintf(buf + len, " "); |
|
} |
|
|
|
buf[len++] = '\n'; |
|
return len; |
|
} |
|
static DEVICE_ATTR_RW(multi_intensity); |
|
|
|
static ssize_t multi_index_show(struct device *dev, |
|
struct device_attribute *multi_index_attr, |
|
char *buf) |
|
{ |
|
struct led_classdev *led_cdev = dev_get_drvdata(dev); |
|
struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev); |
|
int len = 0; |
|
int index; |
|
int i; |
|
|
|
for (i = 0; i < mcled_cdev->num_colors; i++) { |
|
index = mcled_cdev->subled_info[i].color_index; |
|
len += sprintf(buf + len, "%s", led_colors[index]); |
|
if (i < mcled_cdev->num_colors - 1) |
|
len += sprintf(buf + len, " "); |
|
} |
|
|
|
buf[len++] = '\n'; |
|
return len; |
|
} |
|
static DEVICE_ATTR_RO(multi_index); |
|
|
|
static struct attribute *led_multicolor_attrs[] = { |
|
&dev_attr_multi_intensity.attr, |
|
&dev_attr_multi_index.attr, |
|
NULL, |
|
}; |
|
ATTRIBUTE_GROUPS(led_multicolor); |
|
|
|
int led_classdev_multicolor_register_ext(struct device *parent, |
|
struct led_classdev_mc *mcled_cdev, |
|
struct led_init_data *init_data) |
|
{ |
|
struct led_classdev *led_cdev; |
|
|
|
if (!mcled_cdev) |
|
return -EINVAL; |
|
|
|
if (mcled_cdev->num_colors <= 0) |
|
return -EINVAL; |
|
|
|
if (mcled_cdev->num_colors > LED_COLOR_ID_MAX) |
|
return -EINVAL; |
|
|
|
led_cdev = &mcled_cdev->led_cdev; |
|
mcled_cdev->led_cdev.groups = led_multicolor_groups; |
|
|
|
return led_classdev_register_ext(parent, led_cdev, init_data); |
|
} |
|
EXPORT_SYMBOL_GPL(led_classdev_multicolor_register_ext); |
|
|
|
void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev) |
|
{ |
|
if (!mcled_cdev) |
|
return; |
|
|
|
led_classdev_unregister(&mcled_cdev->led_cdev); |
|
} |
|
EXPORT_SYMBOL_GPL(led_classdev_multicolor_unregister); |
|
|
|
static void devm_led_classdev_multicolor_release(struct device *dev, void *res) |
|
{ |
|
led_classdev_multicolor_unregister(*(struct led_classdev_mc **)res); |
|
} |
|
|
|
int devm_led_classdev_multicolor_register_ext(struct device *parent, |
|
struct led_classdev_mc *mcled_cdev, |
|
struct led_init_data *init_data) |
|
{ |
|
struct led_classdev_mc **dr; |
|
int ret; |
|
|
|
dr = devres_alloc(devm_led_classdev_multicolor_release, |
|
sizeof(*dr), GFP_KERNEL); |
|
if (!dr) |
|
return -ENOMEM; |
|
|
|
ret = led_classdev_multicolor_register_ext(parent, mcled_cdev, |
|
init_data); |
|
if (ret) { |
|
devres_free(dr); |
|
return ret; |
|
} |
|
|
|
*dr = mcled_cdev; |
|
devres_add(parent, dr); |
|
|
|
return 0; |
|
} |
|
EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_register_ext); |
|
|
|
static int devm_led_classdev_multicolor_match(struct device *dev, |
|
void *res, void *data) |
|
{ |
|
struct led_classdev_mc **p = res; |
|
|
|
if (WARN_ON(!p || !*p)) |
|
return 0; |
|
|
|
return *p == data; |
|
} |
|
|
|
void devm_led_classdev_multicolor_unregister(struct device *dev, |
|
struct led_classdev_mc *mcled_cdev) |
|
{ |
|
WARN_ON(devres_release(dev, |
|
devm_led_classdev_multicolor_release, |
|
devm_led_classdev_multicolor_match, mcled_cdev)); |
|
} |
|
EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_unregister); |
|
|
|
MODULE_AUTHOR("Dan Murphy <[email protected]>"); |
|
MODULE_DESCRIPTION("Multicolor LED class interface"); |
|
MODULE_LICENSE("GPL v2");
|
|
|