Files
2026-08-24 10:03:24 +08:00

1010 lines
26 KiB
C

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
/*
* leds-aw9523.c aw9523 led module
*
* Version: 1.0.0
*
* Copyright (c) 2017 AWINIC Technology CO., LTD
*
* Author: Nick Li <[email protected]>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/debugfs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/leds.h>
#include "leds-aw9523b.h"
#include <linux/amlogic/pm.h>
/******************************************************
*
* Marco
*
******************************************************/
#define AW9523_I2C_NAME "aw9523_led"
#define MESON_LEDS_CDEV_NAME "i2c_leds"
#define AW9523_VERSION "v2.0.0"
#define AW_I2C_RETRIES 3
#define AW_I2C_RETRY_DELAY 1
#define AW_READ_CHIPID_RETRIES 1
#define AW9523_COLORS_COUNT 3
#define AW9523_MAX_IO 15
#define REG_INPUT_P0 0x00
#define REG_INPUT_P1 0x01
#define REG_OUTPUT_P0 0x02
#define REG_OUTPUT_P1 0x03
#define REG_CONFIG_P0 0x04
#define REG_CONFIG_P1 0x05
#define REG_INT_P0 0x06
#define REG_INT_P1 0x07
#define REG_ID 0x10
#define REG_CTRL 0x11
#define REG_WORK_MODE_P0 0x12
#define REG_WORK_MODE_P1 0x13
#define REG_DIM00 0x20
#define REG_DIM01 0x21
#define REG_DIM02 0x22
#define REG_DIM03 0x23
#define REG_DIM04 0x24
#define REG_DIM05 0x25
#define REG_DIM06 0x26
#define REG_DIM07 0x27
#define REG_DIM08 0x28
#define REG_DIM09 0x29
#define REG_DIM10 0x2a
#define REG_DIM11 0x2b
#define REG_DIM12 0x2c
#define REG_DIM13 0x2d
#define REG_DIM14 0x2e
#define REG_DIM15 0x2f
#define REG_SWRST 0x7F
#define REGISTER_OFFSET 0x20
/* aw9523 register read/write access*/
#define REG_NONE_ACCESS 0
#define REG_RD_ACCESS BIT(0)
#define REG_WR_ACCESS BIT(1)
#define AW9523_REG_MAX (0xFF)
const unsigned char aw9523_reg_access[AW9523_REG_MAX] = {
[REG_INPUT_P0] = REG_RD_ACCESS,
[REG_INPUT_P1] = REG_RD_ACCESS,
[REG_OUTPUT_P0] = REG_RD_ACCESS | REG_WR_ACCESS,
[REG_OUTPUT_P1] = REG_RD_ACCESS | REG_WR_ACCESS,
[REG_CONFIG_P0] = REG_RD_ACCESS | REG_WR_ACCESS,
[REG_CONFIG_P1] = REG_RD_ACCESS | REG_WR_ACCESS,
[REG_INT_P0] = REG_RD_ACCESS | REG_WR_ACCESS,
[REG_INT_P1] = REG_RD_ACCESS | REG_WR_ACCESS,
[REG_ID] = REG_RD_ACCESS,
[REG_CTRL] = REG_RD_ACCESS | REG_WR_ACCESS,
[REG_WORK_MODE_P0] = REG_RD_ACCESS | REG_WR_ACCESS,
[REG_WORK_MODE_P1] = REG_RD_ACCESS | REG_WR_ACCESS,
[REG_DIM01] = REG_WR_ACCESS,
[REG_DIM00] = REG_WR_ACCESS,
[REG_DIM02] = REG_WR_ACCESS,
[REG_DIM03] = REG_WR_ACCESS,
[REG_DIM04] = REG_WR_ACCESS,
[REG_DIM05] = REG_WR_ACCESS,
[REG_DIM06] = REG_WR_ACCESS,
[REG_DIM07] = REG_WR_ACCESS,
[REG_DIM08] = REG_WR_ACCESS,
[REG_DIM09] = REG_WR_ACCESS,
[REG_DIM10] = REG_WR_ACCESS,
[REG_DIM11] = REG_WR_ACCESS,
[REG_DIM12] = REG_WR_ACCESS,
[REG_DIM13] = REG_WR_ACCESS,
[REG_DIM14] = REG_WR_ACCESS,
[REG_DIM15] = REG_WR_ACCESS,
[REG_SWRST] = REG_WR_ACCESS,
};
#define MAX_I2C_BUFFER_SIZE 65536
#define AW9523_ID 0x23
struct meson_aw9523_io {
unsigned int r_io;
unsigned int g_io;
unsigned int b_io;
};
struct meson_aw9523_colors {
unsigned char red;
unsigned char green;
unsigned char blue;
};
struct meson_aw9523 {
struct i2c_client *i2c;
struct device *dev;
struct led_classdev cdev;
struct work_struct brightness_work;
struct work_struct leds_work;
struct workqueue_struct *leds_wq;
int reset_gpio;
unsigned char chipid;
int imax;
int platform;
struct meson_aw9523_io *io;
struct meson_aw9523_colors *colors;
int led_counts;
int ignore_led_suspend;
};
struct meson_aw9523 *aw9523;
static int aw9523_i2c_write(struct meson_aw9523 *aw9523,
unsigned char reg_addr, unsigned char reg_data);
static int aw9523_reset(struct meson_aw9523 *aw9523);
static int *all_data[32] = {0};//[2+16*N]: N command,each command consists of 16 bits
static int length_data[32] = {0};
static unsigned int state_mode;
/*2:S905X2 SEI520, 3:S905X3 SEI600TID; 4:S905X3 SEI610/SEI610F, 6:SEI610FPT, 8:SEI810CPR*/
static unsigned int platform_id;
static unsigned int hwen_id = 3, isel_id = 3;
static unsigned int led_value;
static int time_val, cycles_val, instruct_val;
static u8 tmp_w[1024][16] = {};
static unsigned int tmp_color[5] = {};
static int aw9523_i2c_writes(struct i2c_client *client,
unsigned char sreg_addr, uint8_t length, uint8_t *bufs)
{
int ret = -1;
struct i2c_msg msg;
int i, retries = 0;
u8 data[64];
data[0] = sreg_addr;
for (i = 1; i <= length; i++)
data[i] = bufs[i - 1];
msg.addr = client->addr;
msg.flags = !I2C_M_RD;
msg.len = length + 1;
msg.buf = data;
while (retries < 3) {
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret >= 0) {
ret = 1;
break;
}
retries++;
}
if (retries >= 3)
pr_err("%s error...retyies = %d\n", __func__, retries);
return ret;
}
static void leds_aw9523b_work_func(struct work_struct *work)
{
int i, j;
if (cycles_val == 0) {
for (j = 0; j < instruct_val; j++) {
aw9523_i2c_writes(aw9523->i2c, REG_DIM00, 16, tmp_w[j]);
usleep_range(1000 * (time_val - 10), 1000 * (time_val + 10));
if (state_mode == 0)
break;
}
if (state_mode != 0)
queue_work(aw9523->leds_wq, &aw9523->leds_work);
} else {
for (i = 1; i <= cycles_val; i++) {
for (j = 0; j < instruct_val; j++) {
aw9523_i2c_writes(aw9523->i2c, REG_DIM00, 16, tmp_w[j]);
usleep_range(1000 * (time_val - 10), 1000 * (time_val + 10));
if (state_mode == 0)
break;
}
if (state_mode == 0)
break;
}
}
}
static void init_leds_mode(int leds_mode)
{
int offset, i, j;
if (leds_mode > MAX_LED_MODE - 1) {
dev_err(aw9523->dev, "%s erro,out of led mode range\n", __func__);
return;
}
time_val = *(all_data[leds_mode] + 0);
cycles_val = *((all_data[leds_mode]) + 1);
instruct_val = (length_data[leds_mode] - 2) / 16;
state_mode = 0;
usleep_range(80000, 100000);
for (i = 0; i < instruct_val; i++) {
for (j = 0; j < 16; j++) {
offset = 16 * i + j;
tmp_w[i][j] = *((all_data[leds_mode]) + offset + 2);
}
}
state_mode = leds_mode;
queue_work(aw9523->leds_wq, &aw9523->leds_work);
}
static void meson_aw9523_set_colors(unsigned int *colors, unsigned int counts)
{
struct meson_aw9523_colors *color;
struct meson_aw9523_io *io;
unsigned char color_data[AW9523_MAX_IO];
unsigned int i;
memset(color_data, 0, sizeof(color_data));
for (i = 0; i < counts; i++) {
io = &aw9523->io[i];
color = &aw9523->colors[i];
color->blue = colors[i] & 0xff;
color->green = (colors[i] >> 8) & 0xff;
color->red = (colors[i] >> 16) & 0xff;
color_data[io->b_io] = color->blue;
color_data[io->g_io] = color->green;
color_data[io->r_io] = color->red;
}
aw9523_i2c_writes(aw9523->i2c, REG_DIM00, AW9523_MAX_IO, color_data);
}
static void meson_aw9523_set_colors_again(struct meson_aw9523 *aw9523)
{
struct meson_aw9523_colors *color;
struct meson_aw9523_io *io;
unsigned char color_data[AW9523_MAX_IO];
unsigned int i;
for (i = 0; i < aw9523->led_counts; i++) {
io = &aw9523->io[i];
color = &aw9523->colors[i];
color_data[io->b_io] = color->blue;
color_data[io->g_io] = color->green;
color_data[io->r_io] = color->red;
}
aw9523_i2c_writes(aw9523->i2c, REG_DIM00, AW9523_MAX_IO, color_data);
}
static void init_leds_config(void)
{
aw9523_reset(aw9523);
//aw9523_i2c_write(aw9523, 0x7F, 0x00);//0x7F SW_RSTN
/*0x12 P0 mode, <0x00: all PWM mode> */
aw9523_i2c_write(aw9523, 0x12, 0x00);
/*0x13 P1 mode, <0x00: all PWM mode, 0xF0: P1_7~P1_4 IO mode>*/
aw9523_i2c_write(aw9523, 0x13, 0x00);
/*
* GPOMD<0x00: Open-Drain,
* 0x01: Push-Pull> + ISEL <0x00:Imax,
* 0x11: 1/4-Imax,0x10: 2/4-Imax, 0x01: 3/4-Imax>
*/
aw9523_i2c_write(aw9523, 0x11, 0x11);
}
static void init_leds_data(void)
{
all_data[0] = (int *)&data_mode0;
all_data[1] = (int *)&data_mode1;
all_data[2] = (int *)&data_mode2;
all_data[3] = (int *)&data_mode3;
all_data[4] = (int *)&data_mode4;
all_data[5] = (int *)&data_mode5;
all_data[6] = (int *)&data_mode6;
all_data[7] = (int *)&data_mode7;
all_data[8] = (int *)&data_mode8;
all_data[9] = (int *)&data_mode9;
all_data[10] = (int *)&data_mode10;
all_data[11] = (int *)&data_mode11;
length_data[0] = ARRAY_SIZE(data_mode0);
length_data[1] = ARRAY_SIZE(data_mode1);
length_data[2] = ARRAY_SIZE(data_mode2);
length_data[3] = ARRAY_SIZE(data_mode3);
length_data[4] = ARRAY_SIZE(data_mode4);
length_data[5] = ARRAY_SIZE(data_mode5);
length_data[6] = ARRAY_SIZE(data_mode6);
length_data[7] = ARRAY_SIZE(data_mode7);
length_data[8] = ARRAY_SIZE(data_mode8);
length_data[9] = ARRAY_SIZE(data_mode9);
length_data[10] = ARRAY_SIZE(data_mode10);
length_data[11] = ARRAY_SIZE(data_mode11);
/*add X3 SEI600TID */
all_data[12] = (int *)&data_mode12;
length_data[12] = ARRAY_SIZE(data_mode12);
/*add X4 SEI810CPR */
all_data[13] = (int *)&data_mode13;
length_data[13] = ARRAY_SIZE(data_mode13);
all_data[14] = (int *)&data_mode14;
length_data[14] = ARRAY_SIZE(data_mode14);
init_leds_config();
if (platform_id == 3) {
init_leds_mode(12);
aw9523_i2c_write(aw9523, REG_OUTPUT_P1, 0x00);
} else if (platform_id == 2) {
init_leds_mode(1);
} else if (platform_id == 4) {
init_leds_mode(1);
} else if (platform_id == 8) {
init_leds_mode(13);
}
}
static int aw9523_i2c_write(struct meson_aw9523 *aw9523,
unsigned char reg_addr, unsigned char reg_data)
{
int ret = -1;
unsigned char cnt = 0;
while (cnt < AW_I2C_RETRIES) {
ret = i2c_smbus_write_byte_data(aw9523->i2c, reg_addr, reg_data);
if (ret < 0) {
pr_err("%s: i2c_write cnt=%d error=%d\n", __func__, cnt, ret);
cnt++;
msleep(AW_I2C_RETRY_DELAY);
} else {
break;
}
}
return ret;
}
static int aw9523_i2c_read(struct meson_aw9523 *aw9523,
unsigned char reg_addr, unsigned int *reg_data)
{
int ret = -1;
unsigned char cnt = 0;
while (cnt < AW_I2C_RETRIES) {
ret = i2c_smbus_read_byte_data(aw9523->i2c, reg_addr);
if (ret < 0) {
pr_debug("%s: i2c_read cnt=%d error=%d\n", __func__, cnt, ret);
} else {
*reg_data = ret;
break;
}
cnt++;
msleep(AW_I2C_RETRY_DELAY);
}
return ret;
}
static void aw9523_set_brightness(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct meson_aw9523 *aw9523 = container_of(cdev, struct meson_aw9523, cdev);
aw9523->cdev.brightness = brightness;
}
static int aw9523_parse_dt(struct device *dev, struct meson_aw9523 *aw9523,
struct device_node *np)
{
int ret;
aw9523->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
if (gpio_is_valid(aw9523->reset_gpio)) {
ret = devm_gpio_request(dev, aw9523->reset_gpio, "reset-gpio");
if (ret) {
dev_err(dev, "failed to request gpio\n");
return ret;
}
gpio_direction_output(aw9523->reset_gpio, 1);
}
if (of_property_read_bool(np, "ignore-led-suspend"))
aw9523->ignore_led_suspend = 1;
else
aw9523->ignore_led_suspend = 0;
return 0;
}
static int aw9523_reset(struct meson_aw9523 *aw9523)
{
if (gpio_is_valid(aw9523->reset_gpio)) {
gpio_set_value_cansleep(aw9523->reset_gpio, 0);
msleep(20);
gpio_set_value_cansleep(aw9523->reset_gpio, 1);
msleep(20);
} else {
aw9523_i2c_write(aw9523, 0x7F, 0x00); //0x7F SW_RSTN
// dev_err(aw9523->dev, "%s: failed\n", __func__);
}
return 0;
}
static int aw9523_off(struct meson_aw9523 *aw9523)
{
pr_debug("Tiger]%s enter,Line:%d\n", __func__, __LINE__);
if (gpio_is_valid(aw9523->reset_gpio)) {
gpio_set_value_cansleep(aw9523->reset_gpio, 0);
msleep(20);
} else {
aw9523_i2c_write(aw9523, 0x7F, 0x00); //0x7F SW_RSTN
// dev_err(aw9523->dev, "%s: failed\n", __func__);
}
return 0;
}
static int aw9523_read_chipid(struct meson_aw9523 *aw9523)
{
int ret = -1;
unsigned char cnt = 0;
unsigned int reg_val = 0;
while (cnt < AW_READ_CHIPID_RETRIES) {
ret = aw9523_i2c_read(aw9523, REG_ID, &reg_val);
if (ret < 0)
dev_dbg(aw9523->dev,
"%s: failed to read register aw9523_REG_ID: %d retry: %d\n",
__func__, ret, cnt);
switch (reg_val) {
case AW9523_ID:
pr_debug("Tiger]%s aw9523 detected\n", __func__);
aw9523->chipid = AW9523_ID;
return 0;
default:
break;
}
cnt++;
}
return -EINVAL;
}
static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned int state_mode;
int ret;
cancel_work_sync(&aw9523->leds_work);
flush_workqueue(aw9523->leds_wq);
ret = kstrtouint(buf, 10, &state_mode);
if (ret)
dev_err(dev, "%s error\n", __func__);
pr_debug("Tiger]%s enter,Line:%d...state_mode=%d\n", __func__, __LINE__, state_mode);
init_leds_mode(state_mode);
return count;
}
static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ssize_t len = 0;
len += snprintf(buf + len, PAGE_SIZE - len, "current mode:%d\n", state_mode);
return len;
}
static ssize_t reg_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev);
unsigned int databuf[2] = {0, 0};
if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2)
aw9523_i2c_write(aw9523, (unsigned char)databuf[0], (unsigned char)databuf[1]);
return count;
}
static ssize_t reg_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev);
ssize_t len = 0;
unsigned char i = 0;
unsigned int reg_val = 0;
for (i = 0; i < AW9523_REG_MAX; i++) {
if (!(aw9523_reg_access[i] & REG_RD_ACCESS))
continue;
aw9523_i2c_read(aw9523, i, &reg_val);
len += snprintf(buf + len, PAGE_SIZE - len, "reg:0x%02x=0x%02x\n", i, reg_val);
}
return len;
}
static ssize_t hwen_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev);
unsigned int databuf[1] = {0};
if (kstrtoint(buf, 16, &databuf[0]) == 0) {
if (databuf[0] == 1)
aw9523_reset(aw9523);
else if (databuf[0] == 2)//sw-reset
aw9523_i2c_write(aw9523, 0x7F, 0x00);//0x7F SW_RSTN
else if (databuf[0] == 3)//sw-init
init_leds_data();
else
aw9523_off(aw9523);
hwen_id = databuf[0];
}
return count;
}
static ssize_t hwen_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ssize_t len = 0;
len += snprintf(buf + len, PAGE_SIZE - len, "hwen=%d\n", hwen_id);
return len;
}
static ssize_t isel_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev);
unsigned int databuf[1] = {0};
if (kstrtoint(buf, 16, &databuf[0]) == 0) {
isel_id = databuf[0];
aw9523_i2c_write(aw9523, 0x11, databuf[0]);
}
return count;
}
static ssize_t isel_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ssize_t len = 0;
len += snprintf(buf + len, PAGE_SIZE - len, "isel=%d\n", isel_id);
return len;
}
static ssize_t colors_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct meson_aw9523 *aw9523 = container_of(led_cdev,
struct meson_aw9523, cdev);
unsigned int colors[4];
int ret;
ret = sscanf(buf, "%x %x %x %x", &colors[0], &colors[1], &colors[2], &colors[3]);
if (ret != 4)
dev_err(dev, " enter,Line:...set led colors fail ret: %d\n", ret);
if (ret == 4) {
meson_aw9523_set_colors(colors, 4);
tmp_color[0] = colors[0];
tmp_color[1] = colors[1];
tmp_color[2] = colors[2];
tmp_color[3] = colors[3];
} else if (ret == 2) { //for android 11, param: index, color
if (colors[0] < aw9523->led_counts)
tmp_color[colors[0]] = colors[1];
meson_aw9523_set_colors(tmp_color, 4);
}
return count;
}
static ssize_t colors_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
ssize_t len = 0;
len += snprintf(buf + len, PAGE_SIZE - len, "current mode:%d\n", state_mode);
return len;
}
static ssize_t led1_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev);
unsigned int databuf[1] = {0};
if (kstrtoint(buf, 16, &databuf[0]) == 0) {
if (databuf[0] >= 1)
databuf[0] = 1;
if (platform_id == 3)
databuf[0] = !databuf[0];
aw9523_i2c_read(aw9523, REG_OUTPUT_P1, &led_value);
led_value = ((led_value & 0xE0) | (databuf[0] << 4));
dev_dbg(dev, "Tiger] databuf=%x, led_value: %d\n\n", databuf[0], led_value);
aw9523_i2c_write(aw9523, REG_OUTPUT_P1, led_value);
}
return count;
}
static ssize_t led1_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ssize_t len = 0;
aw9523_i2c_read(aw9523, REG_OUTPUT_P1, &led_value);
led_value = ((led_value & 0x10) >> 4);
if (platform_id == 3)
led_value = !led_value;
len += snprintf(buf + len, PAGE_SIZE - len, "led1_value=%d\n", led_value);
return len;
}
static ssize_t led2_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev);
unsigned int databuf[1] = {0};
if (kstrtoint(buf, 16, &databuf[0]) == 0) {
if (databuf[0] >= 1)
databuf[0] = 1;
if (platform_id == 3)
databuf[0] = !databuf[0];
aw9523_i2c_read(aw9523, REG_OUTPUT_P1, &led_value);
led_value = ((led_value & 0xD0) | (databuf[0] << 5));
dev_dbg(dev, "Tiger] databuf=%x, led_value: %d\n\n", databuf[0], led_value);
aw9523_i2c_write(aw9523, REG_OUTPUT_P1, led_value);
}
return count;
}
static ssize_t led2_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ssize_t len = 0;
aw9523_i2c_read(aw9523, REG_OUTPUT_P1, &led_value);
led_value = ((led_value & 0x20) >> 5);
if (platform_id == 3)
led_value = !led_value;
len += snprintf(buf + len, PAGE_SIZE - len, "led2_value=%d\n", led_value);
return len;
}
static ssize_t led3_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct meson_aw9523 *aw9523 = container_of(led_cdev, struct meson_aw9523, cdev);
unsigned int databuf[1] = {0};
if (kstrtoint(buf, 16, &databuf[0]) == 0) {
if (databuf[0] >= 1)
databuf[0] = 1;
if (platform_id == 3)
databuf[0] = !databuf[0];
aw9523_i2c_read(aw9523, REG_OUTPUT_P1, &led_value);
led_value = ((led_value & 0xB0) | (databuf[0] << 6));
dev_dbg(dev, "Tiger] databuf=%x, led_value: %d\n\n", databuf[0], led_value);
aw9523_i2c_write(aw9523, REG_OUTPUT_P1, led_value);
}
return count;
}
static ssize_t led3_show(struct device *dev, struct device_attribute *attr, char *buf)
{
ssize_t len = 0;
aw9523_i2c_read(aw9523, REG_OUTPUT_P1, &led_value);
led_value = ((led_value & 0x40) >> 6);
if (platform_id == 3)
led_value = !led_value;
len += snprintf(buf + len, PAGE_SIZE - len, "led3_value=%d\n", led_value);
return len;
}
static int meson_aw9523_set_singlecolors(u32 ledid, u32 led_color)
{
struct meson_aw9523_colors *color;
struct meson_aw9523_io *io;
if (ledid > 3) {
pr_debug(" Exceed the maximum number of leds!\n");
return 0;
}
io = &aw9523->io[ledid];
color = &aw9523->colors[ledid];
color->blue = led_color & 0xff;
color->green = (led_color >> 8) & 0xff;
color->red = (led_color >> 16) & 0xff;
aw9523_i2c_write(aw9523, io->r_io + REGISTER_OFFSET, color->red);
aw9523_i2c_write(aw9523, io->g_io + REGISTER_OFFSET, color->green);
aw9523_i2c_write(aw9523, io->b_io + REGISTER_OFFSET, color->blue);
return 0;
}
static ssize_t single_colors_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
u32 ledid, color;
int ret;
ret = sscanf(buf, "%d %x", &ledid, &color);
if (ret != 2) {
pr_debug(" enter,Line:...set led colors fail!\n");
return count;
}
meson_aw9523_set_singlecolors(ledid, color);
return count;
}
static DEVICE_ATTR_RW(reg);
static DEVICE_ATTR_RW(hwen);
static DEVICE_ATTR_RW(mode);
static DEVICE_ATTR_RW(isel);
static DEVICE_ATTR_RW(colors);
static DEVICE_ATTR_WO(single_colors);
static DEVICE_ATTR_RW(led1);
static DEVICE_ATTR_RW(led2);
static DEVICE_ATTR_RW(led3);
static struct attribute *aw9523_attributes[] = {
&dev_attr_reg.attr,
&dev_attr_hwen.attr,
&dev_attr_mode.attr,
&dev_attr_isel.attr,
&dev_attr_colors.attr,
&dev_attr_led1.attr,
&dev_attr_led2.attr,
&dev_attr_led3.attr,
&dev_attr_single_colors.attr,
NULL
};
static struct attribute_group aw9523_attribute_group = {
.attrs = aw9523_attributes
};
static int meson_aw9523_suspend(struct device *dev)
{
unsigned char color_data[AW9523_MAX_IO] = { 0 };
if (aw9523->ignore_led_suspend)
return 0;
aw9523_i2c_writes(aw9523->i2c, REG_DIM00, AW9523_MAX_IO, color_data);
pr_debug("Tiger]aw9523b suspend\n");
return 0;
}
static int meson_aw9523_resume(struct device *dev)
{
if (aw9523->ignore_led_suspend)
return 0;
meson_aw9523_set_colors_again(aw9523);
pr_debug("Tiger]aw9523b resume\n");
return 0;
}
static int aw9523_parse_led_cdev(struct meson_aw9523 *aw9523,
struct device_node *np)
{
struct device_node *temp;
unsigned int colors[AW9523_COLORS_COUNT];
int ret = -1;
int i = 0;
for_each_child_of_node(np, temp) {
ret = of_property_read_u32_array(temp, "default_colors",
colors, AW9523_COLORS_COUNT);
if (ret < 0) {
dev_err(aw9523->dev,
"Failure reading default colors ret = %d\n", ret);
goto free_pdata;
}
ret = of_property_read_u32(temp, "r_io_number", &aw9523->io[i].r_io);
if (ret < 0) {
dev_err(aw9523->dev,
"Failure reading imax ret = %d\n", ret);
goto free_pdata;
}
ret = of_property_read_u32(temp, "g_io_number", &aw9523->io[i].g_io);
if (ret < 0) {
dev_err(aw9523->dev,
"Failure reading brightness ret = %d\n", ret);
goto free_pdata;
}
ret = of_property_read_u32(temp, "b_io_number", &aw9523->io[i].b_io);
if (ret < 0) {
dev_err(aw9523->dev,
"Failure reading max brightness ret = %d\n",
ret);
goto free_pdata;
}
aw9523->colors[i].red = colors[0];
aw9523->colors[i].green = colors[1];
aw9523->colors[i].blue = colors[2];
i++;
}
ret = of_property_read_u32(np, "platform", &platform_id);
if (ret < 0)
dev_err(aw9523->dev, "Tiger] %s:%d default platform_id=%d\n", __func__,
__LINE__, platform_id);
dev_dbg(aw9523->dev, "Tiger] %s:%d set platform_id=%d\n", __func__, __LINE__, platform_id);
aw9523->cdev.name = MESON_LEDS_CDEV_NAME;
aw9523->cdev.brightness = 0;
aw9523->cdev.max_brightness = 255;
aw9523->cdev.brightness_set = aw9523_set_brightness;
ret = led_classdev_register(aw9523->dev, &aw9523->cdev);
if (ret) {
dev_err(aw9523->dev,
"unable to register led ret=%d\n", ret);
goto free_pdata;
}
ret = sysfs_create_group(&aw9523->cdev.dev->kobj,
&aw9523_attribute_group);
if (ret) {
dev_err(aw9523->dev, "led sysfs ret: %d\n", ret);
goto free_class;
}
return 0;
free_class:
led_classdev_unregister(&aw9523->cdev);
free_pdata:
return ret;
}
static int aw9523_i2c_probe(struct i2c_client *i2c)
{
struct device_node *np = i2c->dev.of_node;
int ret;
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL)) {
dev_err(&i2c->dev, "check_functionality failed\n");
return -EIO;
}
aw9523 = devm_kzalloc(&i2c->dev, sizeof(struct meson_aw9523), GFP_KERNEL);
if (!aw9523)
return -ENOMEM;
aw9523_parse_dt(&i2c->dev, aw9523, np);
aw9523->dev = &i2c->dev;
aw9523->i2c = i2c;
aw9523->led_counts = device_get_child_node_count(&i2c->dev);
aw9523->io = devm_kcalloc(&i2c->dev, aw9523->led_counts, sizeof(struct meson_aw9523_io),
GFP_KERNEL);
if (!aw9523->io)
return -ENOMEM;
aw9523->colors = devm_kcalloc(&i2c->dev, aw9523->led_counts,
sizeof(struct meson_aw9523_colors), GFP_KERNEL);
if (!aw9523->colors)
return -ENOMEM;
i2c_set_clientdata(i2c, aw9523);
/* aw9523 chip id */
ret = aw9523_read_chipid(aw9523);
if (ret < 0) {
dev_info(&i2c->dev, "there is no aw9523. Exit probe process\n");
return 0;
}
dev_set_drvdata(&i2c->dev, aw9523);
ret = aw9523_parse_led_cdev(aw9523, np);
if (ret < 0) {
dev_err(&i2c->dev, "%s error creating led class dev\n", __func__);
goto err_sysfs;
}
INIT_WORK(&aw9523->leds_work, leds_aw9523b_work_func);
aw9523->leds_wq = create_singlethread_workqueue("leds_wq");
if (!aw9523->leds_wq) {
dev_err(&i2c->dev, "create leds_work->input_wq fail!\n");
return -ENOMEM;
}
queue_work(aw9523->leds_wq, &aw9523->leds_work);
init_leds_data();
return 0;
err_sysfs:
return ret;
}
static void aw9523_i2c_remove(struct i2c_client *i2c)
{
struct meson_aw9523 *aw9523 = i2c_get_clientdata(i2c);
sysfs_remove_group(&aw9523->cdev.dev->kobj,
&aw9523_attribute_group);
led_classdev_unregister(&aw9523->cdev);
aw9523_reset(aw9523);
cancel_work_sync(&aw9523->leds_work);
destroy_workqueue(aw9523->leds_wq);
}
static const struct i2c_device_id aw9523_i2c_id[] = {
{ AW9523_I2C_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw9523_i2c_id);
static const struct of_device_id aw9523_dt_match[] = {
{ .compatible = "amlogic,aw9523b_led" },
{ },
};
static SIMPLE_DEV_PM_OPS(meson_aw9523_pm, meson_aw9523_suspend, meson_aw9523_resume);
static struct i2c_driver meson_aw9523_driver = {
.driver = {
.name = AW9523_I2C_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(aw9523_dt_match),
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &meson_aw9523_pm,
},
.probe = aw9523_i2c_probe,
.remove = aw9523_i2c_remove,
.id_table = aw9523_i2c_id,
};
int led_aw9523_init(void)
{
return i2c_add_driver(&meson_aw9523_driver);
}
void __exit led_aw9523_exit(void)
{
return i2c_del_driver(&meson_aw9523_driver);
}