cregit-Linux how code gets into the kernel

Release 4.14 drivers/leds/leds-cpcap.c

Directory: drivers/leds
/*
 * Copyright (c) 2017 Sebastian Reichel <sre@kernel.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 or
 * later 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.
 */

#include <linux/leds.h>
#include <linux/mfd/motorola-cpcap.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>


#define CPCAP_LED_NO_CURRENT 0x0001


struct cpcap_led_info {
	
u16 reg;
	
u16 mask;
	
u16 limit;
	
u16 init_mask;
	
u16 init_val;
};


static const struct cpcap_led_info cpcap_led_red = {
	.reg	= CPCAP_REG_REDC,
	.mask	= 0x03FF,
	.limit	= 31,
};


static const struct cpcap_led_info cpcap_led_green = {
	.reg	= CPCAP_REG_GREENC,
	.mask	= 0x03FF,
	.limit	= 31,
};


static const struct cpcap_led_info cpcap_led_blue = {
	.reg	= CPCAP_REG_BLUEC,
	.mask	= 0x03FF,
	.limit	= 31,
};

/* aux display light */

static const struct cpcap_led_info cpcap_led_adl = {
	.reg		= CPCAP_REG_ADLC,
	.mask		= 0x000F,
	.limit		= 1,
	.init_mask	= 0x7FFF,
	.init_val	= 0x5FF0,
};

/* camera privacy led */

static const struct cpcap_led_info cpcap_led_cp = {
	.reg		= CPCAP_REG_CLEDC,
	.mask		= 0x0007,
	.limit		= 1,
	.init_mask	= 0x03FF,
	.init_val	= 0x0008,
};


struct cpcap_led {
	
struct led_classdev led;
	
const struct cpcap_led_info *info;
	
struct device *dev;
	
struct regmap *regmap;
	
struct mutex update_lock;
	
struct regulator *vdd;
	
bool powered;

	
u32 current_limit;
};


static u16 cpcap_led_val(u8 current_limit, u8 duty_cycle) { current_limit &= 0x1f; /* 5 bit */ duty_cycle &= 0x0f; /* 4 bit */ return current_limit << 4 | duty_cycle; }

Contributors

PersonTokensPropCommitsCommitProp
Sebastian Reichel29100.00%1100.00%
Total29100.00%1100.00%


static int cpcap_led_set_power(struct cpcap_led *led, bool status) { int err; if (status == led->powered) return 0; if (status) err = regulator_enable(led->vdd); else err = regulator_disable(led->vdd); if (err) { dev_err(led->dev, "regulator failure: %d", err); return err; } led->powered = status; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Sebastian Reichel80100.00%1100.00%
Total80100.00%1100.00%


static int cpcap_led_set(struct led_classdev *ledc, enum led_brightness value) { struct cpcap_led *led = container_of(ledc, struct cpcap_led, led); int brightness; int err; mutex_lock(&led->update_lock); if (value > LED_OFF) { err = cpcap_led_set_power(led, true); if (err) goto exit; } if (value == LED_OFF) { /* Avoid HW issue by turning off current before duty cycle */ err = regmap_update_bits(led->regmap, led->info->reg, led->info->mask, CPCAP_LED_NO_CURRENT); if (err) { dev_err(led->dev, "regmap failed: %d", err); goto exit; } brightness = cpcap_led_val(value, LED_OFF); } else { brightness = cpcap_led_val(value, LED_ON); } err = regmap_update_bits(led->regmap, led->info->reg, led->info->mask, brightness); if (err) { dev_err(led->dev, "regmap failed: %d", err); goto exit; } if (value == LED_OFF) { err = cpcap_led_set_power(led, false); if (err) goto exit; } exit: mutex_unlock(&led->update_lock); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Sebastian Reichel221100.00%1100.00%
Total221100.00%1100.00%

static const struct of_device_id cpcap_led_of_match[] = { { .compatible = "motorola,cpcap-led-red", .data = &cpcap_led_red }, { .compatible = "motorola,cpcap-led-green", .data = &cpcap_led_green }, { .compatible = "motorola,cpcap-led-blue", .data = &cpcap_led_blue }, { .compatible = "motorola,cpcap-led-adl", .data = &cpcap_led_adl }, { .compatible = "motorola,cpcap-led-cp", .data = &cpcap_led_cp }, {}, }; MODULE_DEVICE_TABLE(of, cpcap_led_of_match);
static int cpcap_led_probe(struct platform_device *pdev) { const struct of_device_id *match; struct cpcap_led *led; int err; match = of_match_device(of_match_ptr(cpcap_led_of_match), &pdev->dev); if (!match || !match->data) return -EINVAL; led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; platform_set_drvdata(pdev, led); led->info = match->data; led->dev = &pdev->dev; if (led->info->reg == 0x0000) { dev_err(led->dev, "Unsupported LED"); return -ENODEV; } led->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!led->regmap) return -ENODEV; led->vdd = devm_regulator_get(&pdev->dev, "vdd"); if (IS_ERR(led->vdd)) { err = PTR_ERR(led->vdd); dev_err(led->dev, "Couldn't get regulator: %d", err); return err; } err = device_property_read_string(&pdev->dev, "label", &led->led.name); if (err) { dev_err(led->dev, "Couldn't read LED label: %d", err); return err; } if (led->info->init_mask) { err = regmap_update_bits(led->regmap, led->info->reg, led->info->init_mask, led->info->init_val); if (err) { dev_err(led->dev, "regmap failed: %d", err); return err; } } mutex_init(&led->update_lock); led->led.max_brightness = led->info->limit; led->led.brightness_set_blocking = cpcap_led_set; err = devm_led_classdev_register(&pdev->dev, &led->led); if (err) { dev_err(led->dev, "Couldn't register LED: %d", err); return err; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Sebastian Reichel366100.00%1100.00%
Total366100.00%1100.00%

static struct platform_driver cpcap_led_driver = { .probe = cpcap_led_probe, .driver = { .name = "cpcap-led", .of_match_table = cpcap_led_of_match, }, }; module_platform_driver(cpcap_led_driver); MODULE_DESCRIPTION("CPCAP LED driver"); MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
Sebastian Reichel1052100.00%1100.00%
Total1052100.00%1100.00%
Directory: drivers/leds
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.