root/drivers/video/backlight/arcxcnn_bl.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. arcxcnn_update_field
  2. arcxcnn_set_brightness
  3. arcxcnn_bl_update_status
  4. arcxcnn_backlight_register
  5. arcxcnn_parse_dt
  6. arcxcnn_probe
  7. arcxcnn_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Backlight driver for ArcticSand ARC_X_C_0N_0N Devices
   4  *
   5  * Copyright 2016 ArcticSand, Inc.
   6  * Author : Brian Dodge <bdodge@arcticsand.com>
   7  */
   8 
   9 #include <linux/backlight.h>
  10 #include <linux/err.h>
  11 #include <linux/i2c.h>
  12 #include <linux/module.h>
  13 #include <linux/of.h>
  14 #include <linux/slab.h>
  15 
  16 enum arcxcnn_chip_id {
  17         ARC2C0608
  18 };
  19 
  20 /**
  21  * struct arcxcnn_platform_data
  22  * @name                : Backlight driver name (NULL will use default)
  23  * @initial_brightness  : initial value of backlight brightness
  24  * @leden               : initial LED string enables, upper bit is global on/off
  25  * @led_config_0        : fading speed (period between intensity steps)
  26  * @led_config_1        : misc settings, see datasheet
  27  * @dim_freq            : pwm dimming frequency if in pwm mode
  28  * @comp_config         : misc config, see datasheet
  29  * @filter_config       : RC/PWM filter config, see datasheet
  30  * @trim_config         : full scale current trim, see datasheet
  31  */
  32 struct arcxcnn_platform_data {
  33         const char *name;
  34         u16 initial_brightness;
  35         u8      leden;
  36         u8      led_config_0;
  37         u8      led_config_1;
  38         u8      dim_freq;
  39         u8      comp_config;
  40         u8      filter_config;
  41         u8      trim_config;
  42 };
  43 
  44 #define ARCXCNN_CMD             0x00    /* Command Register */
  45 #define ARCXCNN_CMD_STDBY       0x80    /*   I2C Standby */
  46 #define ARCXCNN_CMD_RESET       0x40    /*   Reset */
  47 #define ARCXCNN_CMD_BOOST       0x10    /*   Boost */
  48 #define ARCXCNN_CMD_OVP_MASK    0x0C    /*   --- Over Voltage Threshold */
  49 #define ARCXCNN_CMD_OVP_XXV     0x0C    /*   <rsvrd> Over Voltage Threshold */
  50 #define ARCXCNN_CMD_OVP_20V     0x08    /*   20v Over Voltage Threshold */
  51 #define ARCXCNN_CMD_OVP_24V     0x04    /*   24v Over Voltage Threshold */
  52 #define ARCXCNN_CMD_OVP_31V     0x00    /*   31.4v Over Voltage Threshold */
  53 #define ARCXCNN_CMD_EXT_COMP    0x01    /*   part (0) or full (1) ext. comp */
  54 
  55 #define ARCXCNN_CONFIG          0x01    /* Configuration */
  56 #define ARCXCNN_STATUS1         0x02    /* Status 1 */
  57 #define ARCXCNN_STATUS2         0x03    /* Status 2 */
  58 #define ARCXCNN_FADECTRL        0x04    /* Fading Control */
  59 #define ARCXCNN_ILED_CONFIG     0x05    /* ILED Configuration */
  60 #define ARCXCNN_ILED_DIM_PWM    0x00    /*   config dim mode pwm */
  61 #define ARCXCNN_ILED_DIM_INT    0x04    /*   config dim mode internal */
  62 #define ARCXCNN_LEDEN           0x06    /* LED Enable Register */
  63 #define ARCXCNN_LEDEN_ISETEXT   0x80    /*   Full-scale current set extern */
  64 #define ARCXCNN_LEDEN_MASK      0x3F    /*   LED string enables mask */
  65 #define ARCXCNN_LEDEN_BITS      0x06    /*   Bits of LED string enables */
  66 #define ARCXCNN_LEDEN_LED1      0x01
  67 #define ARCXCNN_LEDEN_LED2      0x02
  68 #define ARCXCNN_LEDEN_LED3      0x04
  69 #define ARCXCNN_LEDEN_LED4      0x08
  70 #define ARCXCNN_LEDEN_LED5      0x10
  71 #define ARCXCNN_LEDEN_LED6      0x20
  72 
  73 #define ARCXCNN_WLED_ISET_LSB   0x07    /* LED ISET LSB (in upper nibble) */
  74 #define ARCXCNN_WLED_ISET_LSB_SHIFT 0x04  /* ISET LSB Left Shift */
  75 #define ARCXCNN_WLED_ISET_MSB   0x08    /* LED ISET MSB (8 bits) */
  76 
  77 #define ARCXCNN_DIMFREQ         0x09
  78 #define ARCXCNN_COMP_CONFIG     0x0A
  79 #define ARCXCNN_FILT_CONFIG     0x0B
  80 #define ARCXCNN_IMAXTUNE        0x0C
  81 #define ARCXCNN_ID_MSB          0x1E
  82 #define ARCXCNN_ID_LSB          0x1F
  83 
  84 #define MAX_BRIGHTNESS          4095
  85 #define INIT_BRIGHT             60
  86 
  87 struct arcxcnn {
  88         struct i2c_client *client;
  89         struct backlight_device *bl;
  90         struct device *dev;
  91         struct arcxcnn_platform_data *pdata;
  92 };
  93 
  94 static int arcxcnn_update_field(struct arcxcnn *lp, u8 reg, u8 mask, u8 data)
  95 {
  96         int ret;
  97         u8 tmp;
  98 
  99         ret = i2c_smbus_read_byte_data(lp->client, reg);
 100         if (ret < 0) {
 101                 dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
 102                 return ret;
 103         }
 104 
 105         tmp = (u8)ret;
 106         tmp &= ~mask;
 107         tmp |= data & mask;
 108 
 109         return i2c_smbus_write_byte_data(lp->client, reg, tmp);
 110 }
 111 
 112 static int arcxcnn_set_brightness(struct arcxcnn *lp, u32 brightness)
 113 {
 114         int ret;
 115         u8 val;
 116 
 117         /* lower nibble of brightness goes in upper nibble of LSB register */
 118         val = (brightness & 0xF) << ARCXCNN_WLED_ISET_LSB_SHIFT;
 119         ret = i2c_smbus_write_byte_data(lp->client,
 120                 ARCXCNN_WLED_ISET_LSB, val);
 121         if (ret < 0)
 122                 return ret;
 123 
 124         /* remaining 8 bits of brightness go in MSB register */
 125         val = (brightness >> 4);
 126         return i2c_smbus_write_byte_data(lp->client,
 127                 ARCXCNN_WLED_ISET_MSB, val);
 128 }
 129 
 130 static int arcxcnn_bl_update_status(struct backlight_device *bl)
 131 {
 132         struct arcxcnn *lp = bl_get_data(bl);
 133         u32 brightness = bl->props.brightness;
 134         int ret;
 135 
 136         if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
 137                 brightness = 0;
 138 
 139         ret = arcxcnn_set_brightness(lp, brightness);
 140         if (ret)
 141                 return ret;
 142 
 143         /* set power-on/off/save modes */
 144         return arcxcnn_update_field(lp, ARCXCNN_CMD, ARCXCNN_CMD_STDBY,
 145                 (bl->props.power == 0) ? 0 : ARCXCNN_CMD_STDBY);
 146 }
 147 
 148 static const struct backlight_ops arcxcnn_bl_ops = {
 149         .options = BL_CORE_SUSPENDRESUME,
 150         .update_status = arcxcnn_bl_update_status,
 151 };
 152 
 153 static int arcxcnn_backlight_register(struct arcxcnn *lp)
 154 {
 155         struct backlight_properties *props;
 156         const char *name = lp->pdata->name ? : "arctic_bl";
 157 
 158         props = devm_kzalloc(lp->dev, sizeof(*props), GFP_KERNEL);
 159         if (!props)
 160                 return -ENOMEM;
 161 
 162         props->type = BACKLIGHT_PLATFORM;
 163         props->max_brightness = MAX_BRIGHTNESS;
 164 
 165         if (lp->pdata->initial_brightness > props->max_brightness)
 166                 lp->pdata->initial_brightness = props->max_brightness;
 167 
 168         props->brightness = lp->pdata->initial_brightness;
 169 
 170         lp->bl = devm_backlight_device_register(lp->dev, name, lp->dev, lp,
 171                                        &arcxcnn_bl_ops, props);
 172         return PTR_ERR_OR_ZERO(lp->bl);
 173 }
 174 
 175 static void arcxcnn_parse_dt(struct arcxcnn *lp)
 176 {
 177         struct device *dev = lp->dev;
 178         struct device_node *node = dev->of_node;
 179         u32 prog_val, num_entry, entry, sources[ARCXCNN_LEDEN_BITS];
 180         int ret;
 181 
 182         /* device tree entry isn't required, defaults are OK */
 183         if (!node)
 184                 return;
 185 
 186         ret = of_property_read_string(node, "label", &lp->pdata->name);
 187         if (ret < 0)
 188                 lp->pdata->name = NULL;
 189 
 190         ret = of_property_read_u32(node, "default-brightness", &prog_val);
 191         if (ret == 0)
 192                 lp->pdata->initial_brightness = prog_val;
 193 
 194         ret = of_property_read_u32(node, "arc,led-config-0", &prog_val);
 195         if (ret == 0)
 196                 lp->pdata->led_config_0 = (u8)prog_val;
 197 
 198         ret = of_property_read_u32(node, "arc,led-config-1", &prog_val);
 199         if (ret == 0)
 200                 lp->pdata->led_config_1 = (u8)prog_val;
 201 
 202         ret = of_property_read_u32(node, "arc,dim-freq", &prog_val);
 203         if (ret == 0)
 204                 lp->pdata->dim_freq = (u8)prog_val;
 205 
 206         ret = of_property_read_u32(node, "arc,comp-config", &prog_val);
 207         if (ret == 0)
 208                 lp->pdata->comp_config = (u8)prog_val;
 209 
 210         ret = of_property_read_u32(node, "arc,filter-config", &prog_val);
 211         if (ret == 0)
 212                 lp->pdata->filter_config = (u8)prog_val;
 213 
 214         ret = of_property_read_u32(node, "arc,trim-config", &prog_val);
 215         if (ret == 0)
 216                 lp->pdata->trim_config = (u8)prog_val;
 217 
 218         ret = of_property_count_u32_elems(node, "led-sources");
 219         if (ret < 0) {
 220                 lp->pdata->leden = ARCXCNN_LEDEN_MASK; /* all on is default */
 221         } else {
 222                 num_entry = ret;
 223                 if (num_entry > ARCXCNN_LEDEN_BITS)
 224                         num_entry = ARCXCNN_LEDEN_BITS;
 225 
 226                 ret = of_property_read_u32_array(node, "led-sources", sources,
 227                                         num_entry);
 228                 if (ret < 0) {
 229                         dev_err(dev, "led-sources node is invalid.\n");
 230                         return;
 231                 }
 232 
 233                 lp->pdata->leden = 0;
 234 
 235                 /* for each enable in source, set bit in led enable */
 236                 for (entry = 0; entry < num_entry; entry++) {
 237                         u8 onbit = 1 << sources[entry];
 238 
 239                         lp->pdata->leden |= onbit;
 240                 }
 241         }
 242 }
 243 
 244 static int arcxcnn_probe(struct i2c_client *cl, const struct i2c_device_id *id)
 245 {
 246         struct arcxcnn *lp;
 247         int ret;
 248 
 249         if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 250                 return -EIO;
 251 
 252         lp = devm_kzalloc(&cl->dev, sizeof(*lp), GFP_KERNEL);
 253         if (!lp)
 254                 return -ENOMEM;
 255 
 256         lp->client = cl;
 257         lp->dev = &cl->dev;
 258         lp->pdata = dev_get_platdata(&cl->dev);
 259 
 260         /* reset the device */
 261         ret = i2c_smbus_write_byte_data(lp->client,
 262                 ARCXCNN_CMD, ARCXCNN_CMD_RESET);
 263         if (ret)
 264                 goto probe_err;
 265 
 266         if (!lp->pdata) {
 267                 lp->pdata = devm_kzalloc(lp->dev,
 268                                 sizeof(*lp->pdata), GFP_KERNEL);
 269                 if (!lp->pdata)
 270                         return -ENOMEM;
 271 
 272                 /* Setup defaults based on power-on defaults */
 273                 lp->pdata->name = NULL;
 274                 lp->pdata->initial_brightness = INIT_BRIGHT;
 275                 lp->pdata->leden = ARCXCNN_LEDEN_MASK;
 276 
 277                 lp->pdata->led_config_0 = i2c_smbus_read_byte_data(
 278                         lp->client, ARCXCNN_FADECTRL);
 279 
 280                 lp->pdata->led_config_1 = i2c_smbus_read_byte_data(
 281                         lp->client, ARCXCNN_ILED_CONFIG);
 282                 /* insure dim mode is not default pwm */
 283                 lp->pdata->led_config_1 |= ARCXCNN_ILED_DIM_INT;
 284 
 285                 lp->pdata->dim_freq = i2c_smbus_read_byte_data(
 286                         lp->client, ARCXCNN_DIMFREQ);
 287 
 288                 lp->pdata->comp_config = i2c_smbus_read_byte_data(
 289                         lp->client, ARCXCNN_COMP_CONFIG);
 290 
 291                 lp->pdata->filter_config = i2c_smbus_read_byte_data(
 292                         lp->client, ARCXCNN_FILT_CONFIG);
 293 
 294                 lp->pdata->trim_config = i2c_smbus_read_byte_data(
 295                         lp->client, ARCXCNN_IMAXTUNE);
 296 
 297                 if (IS_ENABLED(CONFIG_OF))
 298                         arcxcnn_parse_dt(lp);
 299         }
 300 
 301         i2c_set_clientdata(cl, lp);
 302 
 303         /* constrain settings to what is possible */
 304         if (lp->pdata->initial_brightness > MAX_BRIGHTNESS)
 305                 lp->pdata->initial_brightness = MAX_BRIGHTNESS;
 306 
 307         /* set initial brightness */
 308         ret = arcxcnn_set_brightness(lp, lp->pdata->initial_brightness);
 309         if (ret)
 310                 goto probe_err;
 311 
 312         /* set other register values directly */
 313         ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_FADECTRL,
 314                 lp->pdata->led_config_0);
 315         if (ret)
 316                 goto probe_err;
 317 
 318         ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_ILED_CONFIG,
 319                 lp->pdata->led_config_1);
 320         if (ret)
 321                 goto probe_err;
 322 
 323         ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_DIMFREQ,
 324                 lp->pdata->dim_freq);
 325         if (ret)
 326                 goto probe_err;
 327 
 328         ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_COMP_CONFIG,
 329                 lp->pdata->comp_config);
 330         if (ret)
 331                 goto probe_err;
 332 
 333         ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_FILT_CONFIG,
 334                 lp->pdata->filter_config);
 335         if (ret)
 336                 goto probe_err;
 337 
 338         ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_IMAXTUNE,
 339                 lp->pdata->trim_config);
 340         if (ret)
 341                 goto probe_err;
 342 
 343         /* set initial LED Enables */
 344         arcxcnn_update_field(lp, ARCXCNN_LEDEN,
 345                 ARCXCNN_LEDEN_MASK, lp->pdata->leden);
 346 
 347         ret = arcxcnn_backlight_register(lp);
 348         if (ret)
 349                 goto probe_register_err;
 350 
 351         backlight_update_status(lp->bl);
 352 
 353         return 0;
 354 
 355 probe_register_err:
 356         dev_err(lp->dev,
 357                 "failed to register backlight.\n");
 358 
 359 probe_err:
 360         dev_err(lp->dev,
 361                 "failure ret: %d\n", ret);
 362         return ret;
 363 }
 364 
 365 static int arcxcnn_remove(struct i2c_client *cl)
 366 {
 367         struct arcxcnn *lp = i2c_get_clientdata(cl);
 368 
 369         /* disable all strings (ignore errors) */
 370         i2c_smbus_write_byte_data(lp->client,
 371                 ARCXCNN_LEDEN, 0x00);
 372         /* reset the device (ignore errors) */
 373         i2c_smbus_write_byte_data(lp->client,
 374                 ARCXCNN_CMD, ARCXCNN_CMD_RESET);
 375 
 376         lp->bl->props.brightness = 0;
 377 
 378         backlight_update_status(lp->bl);
 379 
 380         return 0;
 381 }
 382 
 383 static const struct of_device_id arcxcnn_dt_ids[] = {
 384         { .compatible = "arc,arc2c0608" },
 385         { }
 386 };
 387 MODULE_DEVICE_TABLE(of, arcxcnn_dt_ids);
 388 
 389 static const struct i2c_device_id arcxcnn_ids[] = {
 390         {"arc2c0608", ARC2C0608},
 391         { }
 392 };
 393 MODULE_DEVICE_TABLE(i2c, arcxcnn_ids);
 394 
 395 static struct i2c_driver arcxcnn_driver = {
 396         .driver = {
 397                 .name = "arcxcnn_bl",
 398                 .of_match_table = of_match_ptr(arcxcnn_dt_ids),
 399         },
 400         .probe = arcxcnn_probe,
 401         .remove = arcxcnn_remove,
 402         .id_table = arcxcnn_ids,
 403 };
 404 module_i2c_driver(arcxcnn_driver);
 405 
 406 MODULE_LICENSE("GPL v2");
 407 MODULE_AUTHOR("Brian Dodge <bdodge@arcticsand.com>");
 408 MODULE_DESCRIPTION("ARCXCNN Backlight driver");

/* [<][>][^][v][top][bottom][index][help] */