Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (c) 2017 Sebastian Reichel <sre@kernel.org>
0004  */
0005 
0006 #include <linux/leds.h>
0007 #include <linux/mfd/motorola-cpcap.h>
0008 #include <linux/module.h>
0009 #include <linux/mutex.h>
0010 #include <linux/of_device.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/regmap.h>
0013 #include <linux/regulator/consumer.h>
0014 
0015 #define CPCAP_LED_NO_CURRENT 0x0001
0016 
0017 struct cpcap_led_info {
0018     u16 reg;
0019     u16 mask;
0020     u16 limit;
0021     u16 init_mask;
0022     u16 init_val;
0023 };
0024 
0025 static const struct cpcap_led_info cpcap_led_red = {
0026     .reg    = CPCAP_REG_REDC,
0027     .mask   = 0x03FF,
0028     .limit  = 31,
0029 };
0030 
0031 static const struct cpcap_led_info cpcap_led_green = {
0032     .reg    = CPCAP_REG_GREENC,
0033     .mask   = 0x03FF,
0034     .limit  = 31,
0035 };
0036 
0037 static const struct cpcap_led_info cpcap_led_blue = {
0038     .reg    = CPCAP_REG_BLUEC,
0039     .mask   = 0x03FF,
0040     .limit  = 31,
0041 };
0042 
0043 /* aux display light */
0044 static const struct cpcap_led_info cpcap_led_adl = {
0045     .reg        = CPCAP_REG_ADLC,
0046     .mask       = 0x000F,
0047     .limit      = 1,
0048     .init_mask  = 0x7FFF,
0049     .init_val   = 0x5FF0,
0050 };
0051 
0052 /* camera privacy led */
0053 static const struct cpcap_led_info cpcap_led_cp = {
0054     .reg        = CPCAP_REG_CLEDC,
0055     .mask       = 0x0007,
0056     .limit      = 1,
0057     .init_mask  = 0x03FF,
0058     .init_val   = 0x0008,
0059 };
0060 
0061 struct cpcap_led {
0062     struct led_classdev led;
0063     const struct cpcap_led_info *info;
0064     struct device *dev;
0065     struct regmap *regmap;
0066     struct mutex update_lock;
0067     struct regulator *vdd;
0068     bool powered;
0069 
0070     u32 current_limit;
0071 };
0072 
0073 static u16 cpcap_led_val(u8 current_limit, u8 duty_cycle)
0074 {
0075     current_limit &= 0x1f; /* 5 bit */
0076     duty_cycle &= 0x0f; /* 4 bit */
0077 
0078     return current_limit << 4 | duty_cycle;
0079 }
0080 
0081 static int cpcap_led_set_power(struct cpcap_led *led, bool status)
0082 {
0083     int err;
0084 
0085     if (status == led->powered)
0086         return 0;
0087 
0088     if (status)
0089         err = regulator_enable(led->vdd);
0090     else
0091         err = regulator_disable(led->vdd);
0092 
0093     if (err) {
0094         dev_err(led->dev, "regulator failure: %d", err);
0095         return err;
0096     }
0097 
0098     led->powered = status;
0099 
0100     return 0;
0101 }
0102 
0103 static int cpcap_led_set(struct led_classdev *ledc, enum led_brightness value)
0104 {
0105     struct cpcap_led *led = container_of(ledc, struct cpcap_led, led);
0106     int brightness;
0107     int err;
0108 
0109     mutex_lock(&led->update_lock);
0110 
0111     if (value > LED_OFF) {
0112         err = cpcap_led_set_power(led, true);
0113         if (err)
0114             goto exit;
0115     }
0116 
0117     if (value == LED_OFF) {
0118         /* Avoid HW issue by turning off current before duty cycle */
0119         err = regmap_update_bits(led->regmap,
0120             led->info->reg, led->info->mask, CPCAP_LED_NO_CURRENT);
0121         if (err) {
0122             dev_err(led->dev, "regmap failed: %d", err);
0123             goto exit;
0124         }
0125 
0126         brightness = cpcap_led_val(value, LED_OFF);
0127     } else {
0128         brightness = cpcap_led_val(value, LED_ON);
0129     }
0130 
0131     err = regmap_update_bits(led->regmap, led->info->reg, led->info->mask,
0132         brightness);
0133     if (err) {
0134         dev_err(led->dev, "regmap failed: %d", err);
0135         goto exit;
0136     }
0137 
0138     if (value == LED_OFF) {
0139         err = cpcap_led_set_power(led, false);
0140         if (err)
0141             goto exit;
0142     }
0143 
0144 exit:
0145     mutex_unlock(&led->update_lock);
0146     return err;
0147 }
0148 
0149 static const struct of_device_id cpcap_led_of_match[] = {
0150     { .compatible = "motorola,cpcap-led-red", .data = &cpcap_led_red },
0151     { .compatible = "motorola,cpcap-led-green", .data = &cpcap_led_green },
0152     { .compatible = "motorola,cpcap-led-blue",  .data = &cpcap_led_blue },
0153     { .compatible = "motorola,cpcap-led-adl", .data = &cpcap_led_adl },
0154     { .compatible = "motorola,cpcap-led-cp", .data = &cpcap_led_cp },
0155     {},
0156 };
0157 MODULE_DEVICE_TABLE(of, cpcap_led_of_match);
0158 
0159 static int cpcap_led_probe(struct platform_device *pdev)
0160 {
0161     struct cpcap_led *led;
0162     int err;
0163 
0164     led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
0165     if (!led)
0166         return -ENOMEM;
0167     platform_set_drvdata(pdev, led);
0168     led->info = device_get_match_data(&pdev->dev);
0169     led->dev = &pdev->dev;
0170 
0171     if (led->info->reg == 0x0000) {
0172         dev_err(led->dev, "Unsupported LED");
0173         return -ENODEV;
0174     }
0175 
0176     led->regmap = dev_get_regmap(pdev->dev.parent, NULL);
0177     if (!led->regmap)
0178         return -ENODEV;
0179 
0180     led->vdd = devm_regulator_get(&pdev->dev, "vdd");
0181     if (IS_ERR(led->vdd)) {
0182         err = PTR_ERR(led->vdd);
0183         dev_err(led->dev, "Couldn't get regulator: %d", err);
0184         return err;
0185     }
0186 
0187     err = device_property_read_string(&pdev->dev, "label", &led->led.name);
0188     if (err) {
0189         dev_err(led->dev, "Couldn't read LED label: %d", err);
0190         return err;
0191     }
0192 
0193     if (led->info->init_mask) {
0194         err = regmap_update_bits(led->regmap, led->info->reg,
0195             led->info->init_mask, led->info->init_val);
0196         if (err) {
0197             dev_err(led->dev, "regmap failed: %d", err);
0198             return err;
0199         }
0200     }
0201 
0202     mutex_init(&led->update_lock);
0203 
0204     led->led.max_brightness = led->info->limit;
0205     led->led.brightness_set_blocking = cpcap_led_set;
0206     err = devm_led_classdev_register(&pdev->dev, &led->led);
0207     if (err) {
0208         dev_err(led->dev, "Couldn't register LED: %d", err);
0209         return err;
0210     }
0211 
0212     return 0;
0213 }
0214 
0215 static struct platform_driver cpcap_led_driver = {
0216     .probe = cpcap_led_probe,
0217     .driver = {
0218         .name = "cpcap-led",
0219         .of_match_table = cpcap_led_of_match,
0220     },
0221 };
0222 module_platform_driver(cpcap_led_driver);
0223 
0224 MODULE_DESCRIPTION("CPCAP LED driver");
0225 MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
0226 MODULE_LICENSE("GPL");