Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Backlight driver for ArcticSand ARC_X_C_0N_0N Devices
0004  *
0005  * Copyright 2016 ArcticSand, Inc.
0006  * Author : Brian Dodge <bdodge@arcticsand.com>
0007  */
0008 
0009 #include <linux/backlight.h>
0010 #include <linux/err.h>
0011 #include <linux/i2c.h>
0012 #include <linux/module.h>
0013 #include <linux/of.h>
0014 #include <linux/slab.h>
0015 
0016 enum arcxcnn_chip_id {
0017     ARC2C0608
0018 };
0019 
0020 /**
0021  * struct arcxcnn_platform_data
0022  * @name        : Backlight driver name (NULL will use default)
0023  * @initial_brightness  : initial value of backlight brightness
0024  * @leden       : initial LED string enables, upper bit is global on/off
0025  * @led_config_0    : fading speed (period between intensity steps)
0026  * @led_config_1    : misc settings, see datasheet
0027  * @dim_freq        : pwm dimming frequency if in pwm mode
0028  * @comp_config     : misc config, see datasheet
0029  * @filter_config   : RC/PWM filter config, see datasheet
0030  * @trim_config     : full scale current trim, see datasheet
0031  */
0032 struct arcxcnn_platform_data {
0033     const char *name;
0034     u16 initial_brightness;
0035     u8  leden;
0036     u8  led_config_0;
0037     u8  led_config_1;
0038     u8  dim_freq;
0039     u8  comp_config;
0040     u8  filter_config;
0041     u8  trim_config;
0042 };
0043 
0044 #define ARCXCNN_CMD     0x00    /* Command Register */
0045 #define ARCXCNN_CMD_STDBY   0x80    /*   I2C Standby */
0046 #define ARCXCNN_CMD_RESET   0x40    /*   Reset */
0047 #define ARCXCNN_CMD_BOOST   0x10    /*   Boost */
0048 #define ARCXCNN_CMD_OVP_MASK    0x0C    /*   --- Over Voltage Threshold */
0049 #define ARCXCNN_CMD_OVP_XXV 0x0C    /*   <rsvrd> Over Voltage Threshold */
0050 #define ARCXCNN_CMD_OVP_20V 0x08    /*   20v Over Voltage Threshold */
0051 #define ARCXCNN_CMD_OVP_24V 0x04    /*   24v Over Voltage Threshold */
0052 #define ARCXCNN_CMD_OVP_31V 0x00    /*   31.4v Over Voltage Threshold */
0053 #define ARCXCNN_CMD_EXT_COMP    0x01    /*   part (0) or full (1) ext. comp */
0054 
0055 #define ARCXCNN_CONFIG      0x01    /* Configuration */
0056 #define ARCXCNN_STATUS1     0x02    /* Status 1 */
0057 #define ARCXCNN_STATUS2     0x03    /* Status 2 */
0058 #define ARCXCNN_FADECTRL    0x04    /* Fading Control */
0059 #define ARCXCNN_ILED_CONFIG 0x05    /* ILED Configuration */
0060 #define ARCXCNN_ILED_DIM_PWM    0x00    /*   config dim mode pwm */
0061 #define ARCXCNN_ILED_DIM_INT    0x04    /*   config dim mode internal */
0062 #define ARCXCNN_LEDEN       0x06    /* LED Enable Register */
0063 #define ARCXCNN_LEDEN_ISETEXT   0x80    /*   Full-scale current set extern */
0064 #define ARCXCNN_LEDEN_MASK  0x3F    /*   LED string enables mask */
0065 #define ARCXCNN_LEDEN_BITS  0x06    /*   Bits of LED string enables */
0066 #define ARCXCNN_LEDEN_LED1  0x01
0067 #define ARCXCNN_LEDEN_LED2  0x02
0068 #define ARCXCNN_LEDEN_LED3  0x04
0069 #define ARCXCNN_LEDEN_LED4  0x08
0070 #define ARCXCNN_LEDEN_LED5  0x10
0071 #define ARCXCNN_LEDEN_LED6  0x20
0072 
0073 #define ARCXCNN_WLED_ISET_LSB   0x07    /* LED ISET LSB (in upper nibble) */
0074 #define ARCXCNN_WLED_ISET_LSB_SHIFT 0x04  /* ISET LSB Left Shift */
0075 #define ARCXCNN_WLED_ISET_MSB   0x08    /* LED ISET MSB (8 bits) */
0076 
0077 #define ARCXCNN_DIMFREQ     0x09
0078 #define ARCXCNN_COMP_CONFIG 0x0A
0079 #define ARCXCNN_FILT_CONFIG 0x0B
0080 #define ARCXCNN_IMAXTUNE    0x0C
0081 #define ARCXCNN_ID_MSB      0x1E
0082 #define ARCXCNN_ID_LSB      0x1F
0083 
0084 #define MAX_BRIGHTNESS      4095
0085 #define INIT_BRIGHT     60
0086 
0087 struct arcxcnn {
0088     struct i2c_client *client;
0089     struct backlight_device *bl;
0090     struct device *dev;
0091     struct arcxcnn_platform_data *pdata;
0092 };
0093 
0094 static int arcxcnn_update_field(struct arcxcnn *lp, u8 reg, u8 mask, u8 data)
0095 {
0096     int ret;
0097     u8 tmp;
0098 
0099     ret = i2c_smbus_read_byte_data(lp->client, reg);
0100     if (ret < 0) {
0101         dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
0102         return ret;
0103     }
0104 
0105     tmp = (u8)ret;
0106     tmp &= ~mask;
0107     tmp |= data & mask;
0108 
0109     return i2c_smbus_write_byte_data(lp->client, reg, tmp);
0110 }
0111 
0112 static int arcxcnn_set_brightness(struct arcxcnn *lp, u32 brightness)
0113 {
0114     int ret;
0115     u8 val;
0116 
0117     /* lower nibble of brightness goes in upper nibble of LSB register */
0118     val = (brightness & 0xF) << ARCXCNN_WLED_ISET_LSB_SHIFT;
0119     ret = i2c_smbus_write_byte_data(lp->client,
0120         ARCXCNN_WLED_ISET_LSB, val);
0121     if (ret < 0)
0122         return ret;
0123 
0124     /* remaining 8 bits of brightness go in MSB register */
0125     val = (brightness >> 4);
0126     return i2c_smbus_write_byte_data(lp->client,
0127         ARCXCNN_WLED_ISET_MSB, val);
0128 }
0129 
0130 static int arcxcnn_bl_update_status(struct backlight_device *bl)
0131 {
0132     struct arcxcnn *lp = bl_get_data(bl);
0133     u32 brightness = bl->props.brightness;
0134     int ret;
0135 
0136     if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
0137         brightness = 0;
0138 
0139     ret = arcxcnn_set_brightness(lp, brightness);
0140     if (ret)
0141         return ret;
0142 
0143     /* set power-on/off/save modes */
0144     return arcxcnn_update_field(lp, ARCXCNN_CMD, ARCXCNN_CMD_STDBY,
0145         (bl->props.power == 0) ? 0 : ARCXCNN_CMD_STDBY);
0146 }
0147 
0148 static const struct backlight_ops arcxcnn_bl_ops = {
0149     .options = BL_CORE_SUSPENDRESUME,
0150     .update_status = arcxcnn_bl_update_status,
0151 };
0152 
0153 static int arcxcnn_backlight_register(struct arcxcnn *lp)
0154 {
0155     struct backlight_properties *props;
0156     const char *name = lp->pdata->name ? : "arctic_bl";
0157 
0158     props = devm_kzalloc(lp->dev, sizeof(*props), GFP_KERNEL);
0159     if (!props)
0160         return -ENOMEM;
0161 
0162     props->type = BACKLIGHT_PLATFORM;
0163     props->max_brightness = MAX_BRIGHTNESS;
0164 
0165     if (lp->pdata->initial_brightness > props->max_brightness)
0166         lp->pdata->initial_brightness = props->max_brightness;
0167 
0168     props->brightness = lp->pdata->initial_brightness;
0169 
0170     lp->bl = devm_backlight_device_register(lp->dev, name, lp->dev, lp,
0171                        &arcxcnn_bl_ops, props);
0172     return PTR_ERR_OR_ZERO(lp->bl);
0173 }
0174 
0175 static void arcxcnn_parse_dt(struct arcxcnn *lp)
0176 {
0177     struct device *dev = lp->dev;
0178     struct device_node *node = dev->of_node;
0179     u32 prog_val, num_entry, entry, sources[ARCXCNN_LEDEN_BITS];
0180     int ret;
0181 
0182     /* device tree entry isn't required, defaults are OK */
0183     if (!node)
0184         return;
0185 
0186     ret = of_property_read_string(node, "label", &lp->pdata->name);
0187     if (ret < 0)
0188         lp->pdata->name = NULL;
0189 
0190     ret = of_property_read_u32(node, "default-brightness", &prog_val);
0191     if (ret == 0)
0192         lp->pdata->initial_brightness = prog_val;
0193 
0194     ret = of_property_read_u32(node, "arc,led-config-0", &prog_val);
0195     if (ret == 0)
0196         lp->pdata->led_config_0 = (u8)prog_val;
0197 
0198     ret = of_property_read_u32(node, "arc,led-config-1", &prog_val);
0199     if (ret == 0)
0200         lp->pdata->led_config_1 = (u8)prog_val;
0201 
0202     ret = of_property_read_u32(node, "arc,dim-freq", &prog_val);
0203     if (ret == 0)
0204         lp->pdata->dim_freq = (u8)prog_val;
0205 
0206     ret = of_property_read_u32(node, "arc,comp-config", &prog_val);
0207     if (ret == 0)
0208         lp->pdata->comp_config = (u8)prog_val;
0209 
0210     ret = of_property_read_u32(node, "arc,filter-config", &prog_val);
0211     if (ret == 0)
0212         lp->pdata->filter_config = (u8)prog_val;
0213 
0214     ret = of_property_read_u32(node, "arc,trim-config", &prog_val);
0215     if (ret == 0)
0216         lp->pdata->trim_config = (u8)prog_val;
0217 
0218     ret = of_property_count_u32_elems(node, "led-sources");
0219     if (ret < 0) {
0220         lp->pdata->leden = ARCXCNN_LEDEN_MASK; /* all on is default */
0221     } else {
0222         num_entry = ret;
0223         if (num_entry > ARCXCNN_LEDEN_BITS)
0224             num_entry = ARCXCNN_LEDEN_BITS;
0225 
0226         ret = of_property_read_u32_array(node, "led-sources", sources,
0227                     num_entry);
0228         if (ret < 0) {
0229             dev_err(dev, "led-sources node is invalid.\n");
0230             return;
0231         }
0232 
0233         lp->pdata->leden = 0;
0234 
0235         /* for each enable in source, set bit in led enable */
0236         for (entry = 0; entry < num_entry; entry++) {
0237             u8 onbit = 1 << sources[entry];
0238 
0239             lp->pdata->leden |= onbit;
0240         }
0241     }
0242 }
0243 
0244 static int arcxcnn_probe(struct i2c_client *cl, const struct i2c_device_id *id)
0245 {
0246     struct arcxcnn *lp;
0247     int ret;
0248 
0249     if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
0250         return -EIO;
0251 
0252     lp = devm_kzalloc(&cl->dev, sizeof(*lp), GFP_KERNEL);
0253     if (!lp)
0254         return -ENOMEM;
0255 
0256     lp->client = cl;
0257     lp->dev = &cl->dev;
0258     lp->pdata = dev_get_platdata(&cl->dev);
0259 
0260     /* reset the device */
0261     ret = i2c_smbus_write_byte_data(lp->client,
0262         ARCXCNN_CMD, ARCXCNN_CMD_RESET);
0263     if (ret)
0264         goto probe_err;
0265 
0266     if (!lp->pdata) {
0267         lp->pdata = devm_kzalloc(lp->dev,
0268                 sizeof(*lp->pdata), GFP_KERNEL);
0269         if (!lp->pdata)
0270             return -ENOMEM;
0271 
0272         /* Setup defaults based on power-on defaults */
0273         lp->pdata->name = NULL;
0274         lp->pdata->initial_brightness = INIT_BRIGHT;
0275         lp->pdata->leden = ARCXCNN_LEDEN_MASK;
0276 
0277         lp->pdata->led_config_0 = i2c_smbus_read_byte_data(
0278             lp->client, ARCXCNN_FADECTRL);
0279 
0280         lp->pdata->led_config_1 = i2c_smbus_read_byte_data(
0281             lp->client, ARCXCNN_ILED_CONFIG);
0282         /* insure dim mode is not default pwm */
0283         lp->pdata->led_config_1 |= ARCXCNN_ILED_DIM_INT;
0284 
0285         lp->pdata->dim_freq = i2c_smbus_read_byte_data(
0286             lp->client, ARCXCNN_DIMFREQ);
0287 
0288         lp->pdata->comp_config = i2c_smbus_read_byte_data(
0289             lp->client, ARCXCNN_COMP_CONFIG);
0290 
0291         lp->pdata->filter_config = i2c_smbus_read_byte_data(
0292             lp->client, ARCXCNN_FILT_CONFIG);
0293 
0294         lp->pdata->trim_config = i2c_smbus_read_byte_data(
0295             lp->client, ARCXCNN_IMAXTUNE);
0296 
0297         if (IS_ENABLED(CONFIG_OF))
0298             arcxcnn_parse_dt(lp);
0299     }
0300 
0301     i2c_set_clientdata(cl, lp);
0302 
0303     /* constrain settings to what is possible */
0304     if (lp->pdata->initial_brightness > MAX_BRIGHTNESS)
0305         lp->pdata->initial_brightness = MAX_BRIGHTNESS;
0306 
0307     /* set initial brightness */
0308     ret = arcxcnn_set_brightness(lp, lp->pdata->initial_brightness);
0309     if (ret)
0310         goto probe_err;
0311 
0312     /* set other register values directly */
0313     ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_FADECTRL,
0314         lp->pdata->led_config_0);
0315     if (ret)
0316         goto probe_err;
0317 
0318     ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_ILED_CONFIG,
0319         lp->pdata->led_config_1);
0320     if (ret)
0321         goto probe_err;
0322 
0323     ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_DIMFREQ,
0324         lp->pdata->dim_freq);
0325     if (ret)
0326         goto probe_err;
0327 
0328     ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_COMP_CONFIG,
0329         lp->pdata->comp_config);
0330     if (ret)
0331         goto probe_err;
0332 
0333     ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_FILT_CONFIG,
0334         lp->pdata->filter_config);
0335     if (ret)
0336         goto probe_err;
0337 
0338     ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_IMAXTUNE,
0339         lp->pdata->trim_config);
0340     if (ret)
0341         goto probe_err;
0342 
0343     /* set initial LED Enables */
0344     arcxcnn_update_field(lp, ARCXCNN_LEDEN,
0345         ARCXCNN_LEDEN_MASK, lp->pdata->leden);
0346 
0347     ret = arcxcnn_backlight_register(lp);
0348     if (ret)
0349         goto probe_register_err;
0350 
0351     backlight_update_status(lp->bl);
0352 
0353     return 0;
0354 
0355 probe_register_err:
0356     dev_err(lp->dev,
0357         "failed to register backlight.\n");
0358 
0359 probe_err:
0360     dev_err(lp->dev,
0361         "failure ret: %d\n", ret);
0362     return ret;
0363 }
0364 
0365 static int arcxcnn_remove(struct i2c_client *cl)
0366 {
0367     struct arcxcnn *lp = i2c_get_clientdata(cl);
0368 
0369     /* disable all strings (ignore errors) */
0370     i2c_smbus_write_byte_data(lp->client,
0371         ARCXCNN_LEDEN, 0x00);
0372     /* reset the device (ignore errors) */
0373     i2c_smbus_write_byte_data(lp->client,
0374         ARCXCNN_CMD, ARCXCNN_CMD_RESET);
0375 
0376     lp->bl->props.brightness = 0;
0377 
0378     backlight_update_status(lp->bl);
0379 
0380     return 0;
0381 }
0382 
0383 static const struct of_device_id arcxcnn_dt_ids[] = {
0384     { .compatible = "arc,arc2c0608" },
0385     { }
0386 };
0387 MODULE_DEVICE_TABLE(of, arcxcnn_dt_ids);
0388 
0389 static const struct i2c_device_id arcxcnn_ids[] = {
0390     {"arc2c0608", ARC2C0608},
0391     { }
0392 };
0393 MODULE_DEVICE_TABLE(i2c, arcxcnn_ids);
0394 
0395 static struct i2c_driver arcxcnn_driver = {
0396     .driver = {
0397         .name = "arcxcnn_bl",
0398         .of_match_table = of_match_ptr(arcxcnn_dt_ids),
0399     },
0400     .probe = arcxcnn_probe,
0401     .remove = arcxcnn_remove,
0402     .id_table = arcxcnn_ids,
0403 };
0404 module_i2c_driver(arcxcnn_driver);
0405 
0406 MODULE_LICENSE("GPL v2");
0407 MODULE_AUTHOR("Brian Dodge <bdodge@arcticsand.com>");
0408 MODULE_DESCRIPTION("ARCXCNN Backlight driver");