blob: 37a1411bc5147e36d139152698de4a1afce87491 [file] [log] [blame]
/*
* LED driver for Samsung ET6034A
*
* Copyright (C) 2018 Samsung Electronics
* Author: Hyuk Kang
*
*/
#define pr_fmt(fmt) "sled_et6034a: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include "led-sep.h"
#define SW_RSTCTR_ON 0X00
#define SW_RSTCTR_OFF 0x01
#define RGB_OE_MASK 0x33
enum et6034a_reg {
ET6034A_RSTCTR = 0,
ET6034A_RGB_OE,
};
struct et6034a_led {
struct i2c_client *i2c;
u8 oe_state; /* backup of RGB_OE register */
};
/* FIXBRIT_LEDx base address */
#if 0
static int fixbrit[4] = { 0x02, 0x05, 0x0e, 0x11 };
static int oe_shift[4] = { 0, 1, 4, 5 };
#else
static int fixbrit[4] = { 0x11, 0x0e, 0x05, 0x2 };
static int oe_shift[4] = { 5, 4, 1, 0 };
#endif
#define ET6034A_REGS 18
static u8 backup_regs[ET6034A_REGS];
static void dump_reg(void)
{
u8 i = 0;
pr_info("DUMP START ============================================\n");
for (i = 0; i < ET6034A_REGS; i++)
pr_info("et6034a 0x%x: 0x%x\n", i, backup_regs[i]);
pr_info("DUMP END ============================================\n");
}
static int et6034a_i2c_write(struct i2c_client *i2c, u8 reg, u8 value)
{
int ret;
pr_info(" %s 0x%x, 0x%x\n", __func__, reg, value);
if (reg >= ET6034A_REGS)
return -EINVAL;
backup_regs[reg] = value;
ret = i2c_smbus_write_byte_data(i2c, reg, value);
if (ret < 0)
pr_info("%s 0x%x, ret(%d)\n", __func__, reg, ret);
return ret;
}
static int et6034a_write(struct et6034a_led *led, u8 reg, u8 value)
{
if (led && led->i2c)
return et6034a_i2c_write(led->i2c, reg, value);
return -ENODEV;
}
static int led_test(struct et6034a_led *led)
{
int ret = 0;
u8 i = 0;
for (i = 2; i < 8; i++)
ret = et6034a_write(led, i, 0x7f);
for (i = 12; i < ET6034A_REGS; i++)
ret = et6034a_write(led, i, 0x7f);
return ret;
}
static int et6034a_power(bool on)
{
struct regulator *ldo = NULL;
int ret = 0;
pr_info("%s: %s\n", __func__, on ? "on" : "off");
ldo = regulator_get(NULL, "m_vdd_ldo29");
if (IS_ERR_OR_NULL(ldo)) {
pr_err("%s: Failed to get %s regulator.\n",
__func__, "m_vdd_ldo29");
ret = PTR_ERR(ldo);
goto error;
}
if (on) {
ret = regulator_enable(ldo);
if (ret)
pr_err("%s: Failed to enable LDO: %d\n", __func__, ret);
} else
regulator_disable(ldo);
error:
regulator_put(ldo);
return ret;
}
static int et6034a_color(struct et6034a_led *led,
int led_num, int color)
{
int ret = 0;
pr_info("%s: led %d color 0x%x\n", __func__,
led_num, color);
ret = et6034a_write(led, fixbrit[led_num], LED_R(color));
if (ret < 0)
return ret;
ret = et6034a_write(led, fixbrit[led_num] + 1, LED_G(color));
if (ret < 0)
return ret;
ret = et6034a_write(led, fixbrit[led_num] + 2, LED_B(color));
if (ret < 0)
return ret;
return ret;
}
static inline void et6034a_update_oe(struct et6034a_led *led, int id, int onoff)
{
int on = !!onoff;
u8 oe_value = led->oe_state;
oe_value &= ~(1 << oe_shift[id]);
oe_value |= on << oe_shift[id];
pr_info("%s: 0x%x -> 0x%x\n", __func__, led->oe_state, oe_value);
led->oe_state = oe_value;
}
static inline int et6034a_set_oe(struct et6034a_led *led)
{
return et6034a_write(led, ET6034A_RGB_OE, led->oe_state);
}
static int et6034a_light(struct led_sep_ops *ops,
int id, int on, int color)
{
struct et6034a_led *led = led_sep_get_opsdata(ops);
int i, ret = 0;
pr_info("%s: id %x, on %d color 0x%x\n", __func__,
id, on, color);
for (i = 0; i < 4; i++) {
if (id & (1 << i)) {
et6034a_color(led, i, color);
et6034a_update_oe(led, i, on);
}
}
et6034a_set_oe(led);
return ret;
}
static int et6034a_test(struct led_sep_ops *ops, int reg, int value)
{
int ret = 0;
struct et6034a_led *led = led_sep_get_opsdata(ops);
pr_info("%s 0x%x, 0x%x\n", __func__, reg, value);
ret = et6034a_write(led, reg, value);
if (ret < 0)
pr_info("%s 0x%x, ret(%d)\n", __func__, reg, ret);
return ret;
}
static int et6034a_reset(struct led_sep_ops *ops)
{
int reg, ret = 0;
struct et6034a_led *led = led_sep_get_opsdata(ops);
pr_info("%s\n", __func__);
for (reg = 0; reg < 0x13 ; reg++) {
ret = et6034a_write(led, reg, 0x00);
if (ret < 0)
pr_info("%s 0x%x, ret(%d)\n", __func__, reg, ret);
}
return ret;
}
static int et6034a_pattern(struct led_sep_ops *ops, int mode)
{
int ret = 0;
pr_info("%s: %s\n", __func__, pattern_string(mode));
switch (mode) {
case PATTERN_OFF:
break;
case POWERING:
break;
default:
break;
}
dump_reg();
pr_info("%s: %s Done\n", __func__, pattern_string(mode));
return ret;
}
static struct led_sep_ops et6034a_ops = {
.name = "ET6034A",
.current_max = 127,
.current_lowpower = 127,
.light = et6034a_light,
.test = et6034a_test,
.reset = et6034a_reset,
.pattern = et6034a_pattern,
.capability = (SEP_NODE_PATTERN | SEP_NODE_CONTROL),
};
static int et6034a_led_probe(struct i2c_client *client,
const struct i2c_device_id *i2c_id)
{
struct et6034a_led *led;
dev_info(&client->dev, "%s\n", __func__);
led = devm_kzalloc(&client->dev,
sizeof(struct et6034a_led),
GFP_KERNEL);
if (!led) {
dev_info(&client->dev, "%s power on -> No memory.\n",
__func__);
return -ENOMEM;
}
led->i2c = client;
i2c_set_clientdata(client, led);
et6034a_power(true);
msleep(100);
et6034a_write(led, ET6034A_RSTCTR, SW_RSTCTR_ON);
et6034a_write(led, ET6034A_RGB_OE, RGB_OE_MASK);
led_test(led);
led_sep_set_opsdata(&et6034a_ops, led);
led_sep_register_device(&et6034a_ops);
dev_info(&client->dev, "%s power on -> Done\n", __func__);
return 0;
}
static int et6034a_led_remove(struct i2c_client *client)
{
dev_info(&client->dev, "%s\n", __func__);
et6034a_i2c_write(client, 0x00, 0x00);
et6034a_power(false);
return 0;
}
static void et6034a_led_shutdown(struct i2c_client *client)
{
et6034a_led_remove(client);
dev_info(&client->dev, "%s\n", __func__);
}
static const struct i2c_device_id et6034a_i2c_id[] = {
{ "ET6034A", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, et6034a_i2c_id);
static const struct of_device_id et6034a_led_of_match[] = {
{ .compatible = "samsung,et6034a", },
{ }
};
static struct i2c_driver et6034a_led_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "ET6034A",
.of_match_table = of_match_ptr(et6034a_led_of_match),
},
.probe = et6034a_led_probe,
.remove = et6034a_led_remove,
.shutdown = et6034a_led_shutdown,
.id_table = et6034a_i2c_id,
};
static int __init et6034a_i2c_init(void)
{
pr_info("%s\n", __func__);
return i2c_add_driver(&et6034a_led_driver);
}
module_init(et6034a_i2c_init);
static void __exit et6034a_i2c_exit(void)
{
i2c_del_driver(&et6034a_led_driver);
}
module_exit(et6034a_i2c_exit);
MODULE_AUTHOR("Samsung EIF Team");