0001
0002
0003
0004
0005
0006
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
0022
0023
0024
0025
0026
0027
0028
0029
0030
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
0045 #define ARCXCNN_CMD_STDBY 0x80
0046 #define ARCXCNN_CMD_RESET 0x40
0047 #define ARCXCNN_CMD_BOOST 0x10
0048 #define ARCXCNN_CMD_OVP_MASK 0x0C
0049 #define ARCXCNN_CMD_OVP_XXV 0x0C
0050 #define ARCXCNN_CMD_OVP_20V 0x08
0051 #define ARCXCNN_CMD_OVP_24V 0x04
0052 #define ARCXCNN_CMD_OVP_31V 0x00
0053 #define ARCXCNN_CMD_EXT_COMP 0x01
0054
0055 #define ARCXCNN_CONFIG 0x01
0056 #define ARCXCNN_STATUS1 0x02
0057 #define ARCXCNN_STATUS2 0x03
0058 #define ARCXCNN_FADECTRL 0x04
0059 #define ARCXCNN_ILED_CONFIG 0x05
0060 #define ARCXCNN_ILED_DIM_PWM 0x00
0061 #define ARCXCNN_ILED_DIM_INT 0x04
0062 #define ARCXCNN_LEDEN 0x06
0063 #define ARCXCNN_LEDEN_ISETEXT 0x80
0064 #define ARCXCNN_LEDEN_MASK 0x3F
0065 #define ARCXCNN_LEDEN_BITS 0x06
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
0074 #define ARCXCNN_WLED_ISET_LSB_SHIFT 0x04
0075 #define ARCXCNN_WLED_ISET_MSB 0x08
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
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
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
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
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;
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
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
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
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
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
0304 if (lp->pdata->initial_brightness > MAX_BRIGHTNESS)
0305 lp->pdata->initial_brightness = MAX_BRIGHTNESS;
0306
0307
0308 ret = arcxcnn_set_brightness(lp, lp->pdata->initial_brightness);
0309 if (ret)
0310 goto probe_err;
0311
0312
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
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
0370 i2c_smbus_write_byte_data(lp->client,
0371 ARCXCNN_LEDEN, 0x00);
0372
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");