2 * Backlight driver for ArcticSand ARC_X_C_0N_0N Devices
4 * Copyright 2016 ArcticSand, Inc.
5 * Author : Brian Dodge <bdodge@arcticsand.com>
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.
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.
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/>.
20 #include <linux/backlight.h>
21 #include <linux/err.h>
22 #include <linux/i2c.h>
23 #include <linux/module.h>
25 #include <linux/slab.h>
27 enum arcxcnn_chip_id
{
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
43 struct arcxcnn_platform_data
{
45 u16 initial_brightness
;
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 */
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
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) */
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
95 #define MAX_BRIGHTNESS 4095
96 #define INIT_BRIGHT 60
99 struct i2c_client
*client
;
100 struct backlight_device
*bl
;
102 struct arcxcnn_platform_data
*pdata
;
105 static int arcxcnn_update_field(struct arcxcnn
*lp
, u8 reg
, u8 mask
, u8 data
)
110 ret
= i2c_smbus_read_byte_data(lp
->client
, reg
);
112 dev_err(lp
->dev
, "failed to read 0x%.2x\n", reg
);
120 return i2c_smbus_write_byte_data(lp
->client
, reg
, tmp
);
123 static int arcxcnn_set_brightness(struct arcxcnn
*lp
, u32 brightness
)
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
);
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
);
141 static int arcxcnn_bl_update_status(struct backlight_device
*bl
)
143 struct arcxcnn
*lp
= bl_get_data(bl
);
144 u32 brightness
= bl
->props
.brightness
;
147 if (bl
->props
.state
& (BL_CORE_SUSPENDED
| BL_CORE_FBBLANK
))
150 ret
= arcxcnn_set_brightness(lp
, brightness
);
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
);
159 static const struct backlight_ops arcxcnn_bl_ops
= {
160 .options
= BL_CORE_SUSPENDRESUME
,
161 .update_status
= arcxcnn_bl_update_status
,
164 static int arcxcnn_backlight_register(struct arcxcnn
*lp
)
166 struct backlight_properties
*props
;
167 const char *name
= lp
->pdata
->name
? : "arctic_bl";
169 props
= devm_kzalloc(lp
->dev
, sizeof(*props
), GFP_KERNEL
);
173 props
->type
= BACKLIGHT_PLATFORM
;
174 props
->max_brightness
= MAX_BRIGHTNESS
;
176 if (lp
->pdata
->initial_brightness
> props
->max_brightness
)
177 lp
->pdata
->initial_brightness
= props
->max_brightness
;
179 props
->brightness
= lp
->pdata
->initial_brightness
;
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
);
186 static void arcxcnn_parse_dt(struct arcxcnn
*lp
)
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
];
193 /* device tree entry isn't required, defaults are OK */
197 ret
= of_property_read_string(node
, "label", &lp
->pdata
->name
);
199 lp
->pdata
->name
= NULL
;
201 ret
= of_property_read_u32(node
, "default-brightness", &prog_val
);
203 lp
->pdata
->initial_brightness
= prog_val
;
205 ret
= of_property_read_u32(node
, "arc,led-config-0", &prog_val
);
207 lp
->pdata
->led_config_0
= (u8
)prog_val
;
209 ret
= of_property_read_u32(node
, "arc,led-config-1", &prog_val
);
211 lp
->pdata
->led_config_1
= (u8
)prog_val
;
213 ret
= of_property_read_u32(node
, "arc,dim-freq", &prog_val
);
215 lp
->pdata
->dim_freq
= (u8
)prog_val
;
217 ret
= of_property_read_u32(node
, "arc,comp-config", &prog_val
);
219 lp
->pdata
->comp_config
= (u8
)prog_val
;
221 ret
= of_property_read_u32(node
, "arc,filter-config", &prog_val
);
223 lp
->pdata
->filter_config
= (u8
)prog_val
;
225 ret
= of_property_read_u32(node
, "arc,trim-config", &prog_val
);
227 lp
->pdata
->trim_config
= (u8
)prog_val
;
229 ret
= of_property_count_u32_elems(node
, "led-sources");
231 lp
->pdata
->leden
= ARCXCNN_LEDEN_MASK
; /* all on is default */
234 if (num_entry
> ARCXCNN_LEDEN_BITS
)
235 num_entry
= ARCXCNN_LEDEN_BITS
;
237 ret
= of_property_read_u32_array(node
, "led-sources", sources
,
240 dev_err(dev
, "led-sources node is invalid.\n");
244 lp
->pdata
->leden
= 0;
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
];
250 lp
->pdata
->leden
|= onbit
;
255 static int arcxcnn_probe(struct i2c_client
*cl
, const struct i2c_device_id
*id
)
260 if (!i2c_check_functionality(cl
->adapter
, I2C_FUNC_SMBUS_BYTE_DATA
))
263 lp
= devm_kzalloc(&cl
->dev
, sizeof(*lp
), GFP_KERNEL
);
269 lp
->pdata
= dev_get_platdata(&cl
->dev
);
271 /* reset the device */
272 ret
= i2c_smbus_write_byte_data(lp
->client
,
273 ARCXCNN_CMD
, ARCXCNN_CMD_RESET
);
278 lp
->pdata
= devm_kzalloc(lp
->dev
,
279 sizeof(*lp
->pdata
), GFP_KERNEL
);
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
;
288 lp
->pdata
->led_config_0
= i2c_smbus_read_byte_data(
289 lp
->client
, ARCXCNN_FADECTRL
);
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
;
296 lp
->pdata
->dim_freq
= i2c_smbus_read_byte_data(
297 lp
->client
, ARCXCNN_DIMFREQ
);
299 lp
->pdata
->comp_config
= i2c_smbus_read_byte_data(
300 lp
->client
, ARCXCNN_COMP_CONFIG
);
302 lp
->pdata
->filter_config
= i2c_smbus_read_byte_data(
303 lp
->client
, ARCXCNN_FILT_CONFIG
);
305 lp
->pdata
->trim_config
= i2c_smbus_read_byte_data(
306 lp
->client
, ARCXCNN_IMAXTUNE
);
308 if (IS_ENABLED(CONFIG_OF
))
309 arcxcnn_parse_dt(lp
);
312 i2c_set_clientdata(cl
, lp
);
314 /* constrain settings to what is possible */
315 if (lp
->pdata
->initial_brightness
> MAX_BRIGHTNESS
)
316 lp
->pdata
->initial_brightness
= MAX_BRIGHTNESS
;
318 /* set initial brightness */
319 ret
= arcxcnn_set_brightness(lp
, lp
->pdata
->initial_brightness
);
323 /* set other register values directly */
324 ret
= i2c_smbus_write_byte_data(lp
->client
, ARCXCNN_FADECTRL
,
325 lp
->pdata
->led_config_0
);
329 ret
= i2c_smbus_write_byte_data(lp
->client
, ARCXCNN_ILED_CONFIG
,
330 lp
->pdata
->led_config_1
);
334 ret
= i2c_smbus_write_byte_data(lp
->client
, ARCXCNN_DIMFREQ
,
335 lp
->pdata
->dim_freq
);
339 ret
= i2c_smbus_write_byte_data(lp
->client
, ARCXCNN_COMP_CONFIG
,
340 lp
->pdata
->comp_config
);
344 ret
= i2c_smbus_write_byte_data(lp
->client
, ARCXCNN_FILT_CONFIG
,
345 lp
->pdata
->filter_config
);
349 ret
= i2c_smbus_write_byte_data(lp
->client
, ARCXCNN_IMAXTUNE
,
350 lp
->pdata
->trim_config
);
354 /* set initial LED Enables */
355 arcxcnn_update_field(lp
, ARCXCNN_LEDEN
,
356 ARCXCNN_LEDEN_MASK
, lp
->pdata
->leden
);
358 ret
= arcxcnn_backlight_register(lp
);
360 goto probe_register_err
;
362 backlight_update_status(lp
->bl
);
368 "failed to register backlight.\n");
372 "failure ret: %d\n", ret
);
376 static int arcxcnn_remove(struct i2c_client
*cl
)
378 struct arcxcnn
*lp
= i2c_get_clientdata(cl
);
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
);
387 lp
->bl
->props
.brightness
= 0;
389 backlight_update_status(lp
->bl
);
394 static const struct of_device_id arcxcnn_dt_ids
[] = {
395 { .compatible
= "arc,arc2c0608" },
398 MODULE_DEVICE_TABLE(of
, arcxcnn_dt_ids
);
400 static const struct i2c_device_id arcxcnn_ids
[] = {
401 {"arc2c0608", ARC2C0608
},
404 MODULE_DEVICE_TABLE(i2c
, arcxcnn_ids
);
406 static struct i2c_driver arcxcnn_driver
= {
408 .name
= "arcxcnn_bl",
409 .of_match_table
= of_match_ptr(arcxcnn_dt_ids
),
411 .probe
= arcxcnn_probe
,
412 .remove
= arcxcnn_remove
,
413 .id_table
= arcxcnn_ids
,
415 module_i2c_driver(arcxcnn_driver
);
417 MODULE_LICENSE("GPL v2");
418 MODULE_AUTHOR("Brian Dodge <bdodge@arcticsand.com>");
419 MODULE_DESCRIPTION("ARCXCNN Backlight driver");