linux/drivers/leds/leds-pm8058.c
Thomas Gleixner 97fb5e8d9b treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 284
Based on 1 normalized pattern(s):

  this program is free software you can redistribute it and or modify
  it under the terms of the gnu general public license version 2 and
  only version 2 as published by the free software foundation this
  program is distributed in the hope that it will be useful but
  without any warranty without even the implied warranty of
  merchantability or fitness for a particular purpose see the gnu
  general public license for more details

extracted by the scancode license scanner the SPDX license identifier

  GPL-2.0-only

has been chosen to replace the boilerplate/reference in 294 file(s).

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Allison Randal <allison@lohutok.net>
Reviewed-by: Alexios Zavras <alexios.zavras@intel.com>
Cc: linux-spdx@vger.kernel.org
Link: https://lkml.kernel.org/r/20190529141900.825281744@linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-06-05 17:36:37 +02:00

184 lines
4.3 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2010, 2011, 2016 The Linux Foundation. All rights reserved.
*/
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#define PM8058_LED_TYPE_COMMON 0x00
#define PM8058_LED_TYPE_KEYPAD 0x01
#define PM8058_LED_TYPE_FLASH 0x02
#define PM8058_LED_TYPE_COMMON_MASK 0xf8
#define PM8058_LED_TYPE_KEYPAD_MASK 0xf0
#define PM8058_LED_TYPE_COMMON_SHIFT 3
#define PM8058_LED_TYPE_KEYPAD_SHIFT 4
struct pm8058_led {
struct regmap *map;
u32 reg;
u32 ledtype;
struct led_classdev cdev;
};
static void pm8058_led_set(struct led_classdev *cled,
enum led_brightness value)
{
struct pm8058_led *led;
int ret = 0;
unsigned int mask = 0;
unsigned int val = 0;
led = container_of(cled, struct pm8058_led, cdev);
switch (led->ledtype) {
case PM8058_LED_TYPE_COMMON:
mask = PM8058_LED_TYPE_COMMON_MASK;
val = value << PM8058_LED_TYPE_COMMON_SHIFT;
break;
case PM8058_LED_TYPE_KEYPAD:
case PM8058_LED_TYPE_FLASH:
mask = PM8058_LED_TYPE_KEYPAD_MASK;
val = value << PM8058_LED_TYPE_KEYPAD_SHIFT;
break;
default:
break;
}
ret = regmap_update_bits(led->map, led->reg, mask, val);
if (ret)
pr_err("Failed to set LED brightness\n");
}
static enum led_brightness pm8058_led_get(struct led_classdev *cled)
{
struct pm8058_led *led;
int ret;
unsigned int val;
led = container_of(cled, struct pm8058_led, cdev);
ret = regmap_read(led->map, led->reg, &val);
if (ret) {
pr_err("Failed to get LED brightness\n");
return LED_OFF;
}
switch (led->ledtype) {
case PM8058_LED_TYPE_COMMON:
val &= PM8058_LED_TYPE_COMMON_MASK;
val >>= PM8058_LED_TYPE_COMMON_SHIFT;
break;
case PM8058_LED_TYPE_KEYPAD:
case PM8058_LED_TYPE_FLASH:
val &= PM8058_LED_TYPE_KEYPAD_MASK;
val >>= PM8058_LED_TYPE_KEYPAD_SHIFT;
break;
default:
val = LED_OFF;
break;
}
return val;
}
static int pm8058_led_probe(struct platform_device *pdev)
{
struct pm8058_led *led;
struct device_node *np = pdev->dev.of_node;
int ret;
struct regmap *map;
const char *state;
enum led_brightness maxbright;
led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
if (!led)
return -ENOMEM;
led->ledtype = (u32)(unsigned long)of_device_get_match_data(&pdev->dev);
map = dev_get_regmap(pdev->dev.parent, NULL);
if (!map) {
dev_err(&pdev->dev, "Parent regmap unavailable.\n");
return -ENXIO;
}
led->map = map;
ret = of_property_read_u32(np, "reg", &led->reg);
if (ret) {
dev_err(&pdev->dev, "no register offset specified\n");
return -EINVAL;
}
/* Use label else node name */
led->cdev.name = of_get_property(np, "label", NULL) ? : np->name;
led->cdev.default_trigger =
of_get_property(np, "linux,default-trigger", NULL);
led->cdev.brightness_set = pm8058_led_set;
led->cdev.brightness_get = pm8058_led_get;
if (led->ledtype == PM8058_LED_TYPE_COMMON)
maxbright = 31; /* 5 bits */
else
maxbright = 15; /* 4 bits */
led->cdev.max_brightness = maxbright;
state = of_get_property(np, "default-state", NULL);
if (state) {
if (!strcmp(state, "keep")) {
led->cdev.brightness = pm8058_led_get(&led->cdev);
} else if (!strcmp(state, "on")) {
led->cdev.brightness = maxbright;
pm8058_led_set(&led->cdev, maxbright);
} else {
led->cdev.brightness = LED_OFF;
pm8058_led_set(&led->cdev, LED_OFF);
}
}
if (led->ledtype == PM8058_LED_TYPE_KEYPAD ||
led->ledtype == PM8058_LED_TYPE_FLASH)
led->cdev.flags = LED_CORE_SUSPENDRESUME;
ret = devm_led_classdev_register(&pdev->dev, &led->cdev);
if (ret) {
dev_err(&pdev->dev, "unable to register led \"%s\"\n",
led->cdev.name);
return ret;
}
return 0;
}
static const struct of_device_id pm8058_leds_id_table[] = {
{
.compatible = "qcom,pm8058-led",
.data = (void *)PM8058_LED_TYPE_COMMON
},
{
.compatible = "qcom,pm8058-keypad-led",
.data = (void *)PM8058_LED_TYPE_KEYPAD
},
{
.compatible = "qcom,pm8058-flash-led",
.data = (void *)PM8058_LED_TYPE_FLASH
},
{ },
};
MODULE_DEVICE_TABLE(of, pm8058_leds_id_table);
static struct platform_driver pm8058_led_driver = {
.probe = pm8058_led_probe,
.driver = {
.name = "pm8058-leds",
.of_match_table = pm8058_leds_id_table,
},
};
module_platform_driver(pm8058_led_driver);
MODULE_DESCRIPTION("PM8058 LEDs driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:pm8058-leds");