Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'backlight-next-4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/backlight

Pull backlight update from Lee Jones:
"New Arctic Sand ARC2C0608 LED Backlight driver"

* tag 'backlight-next-4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/backlight:
backlight: Add support for Arctic Sand LED backlight driver chips
dt-bindings: backlight: arcxcnn: Supply bindings for Arctic Sand backlight

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