linux 驱动 --- 不使用设备树注册I2C设备

不使用设备树注册I2C设备,主要使用以下API:

i2c_get_adapter() 获取指定I2C适配器
i2c_new_device() 在指定 I2C 适配器下创建一个I2C设备,通过 i2c_board_info 结构体提供设备的静态信息(如设备地址、名称、平台数据等),并基于这些信息创建 i2c_client 实例。在支持设备树的系统中,通常优先使用设备树描述设备,内核会自动解析并调用 i2c_new_device 完成注册。
注:这种方式还是需要设备树描述适配器,完成适配器的初始化。
 
实例:
#define  pr_fmt(fmt) "[Et6312 driver]" fmt

#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/timer.h>
#include <linux/ioctl.h>
#include <linux/gpio.h>

#include "et6312.h"

// data struct for userspace ===========================================

#if defined(CONFIG_REMO_PDT_TP_TINY2)
#define REMO_RGB_LED_NUM    3
#else
#define REMO_RGB_LED_NUM    4
#endif

#define RGB_VALUE_R(__rgb)              ((__rgb) & 0xFF)
#define RGB_VALUE_G(__rgb)              ((__rgb >> 8) & 0xFF)
#define RGB_VALUE_B(__rgb)              ((__rgb >> 16) & 0xFF)
#define MAKE_RGB_VALUE(__r, __g, __b)   ((__r & 0xFF) | ((__g & 0xFF) << 8) | ((__b & 0xFF) << 16))

#if REMO_RGB_LED_NUM == 3
#define LED_COLORSET_SAME_COLORS(__rgb) {__rgb, __rgb, __rgb}
#elif REMO_RGB_LED_NUM == 4
#define LED_COLORSET_SAME_COLORS(__rgb) {__rgb, __rgb, __rgb, __rgb}
#endif

#pragma pack(1)
struct remo_led_color_set
{
    u32 rgb_colors[REMO_RGB_LED_NUM];
};

struct remo_led_marquee_set {
    u32 rgb_colors[REMO_RGB_LED_NUM];
    u16 delay_ms; // delay time for each light
};

struct remo_led_breathing_set {
    u32 rgb_colors[REMO_RGB_LED_NUM];
    u16 period_ms;
};

struct remo_led_blink_set {
    u32 rgb_colors[REMO_RGB_LED_NUM];
    u16 interval_ms;
};

struct remo_led_queue_set {
    u32 rgb_colors[REMO_RGB_LED_NUM];
    u16 delay_ms;
};

#pragma pack()

#define REMO_LED_SET_COLOR              _IOW(0x81, 0x14, struct remo_led_color_set)
#define REMO_LED_SET_MARQUEE            _IOW(0x81, 0x15, struct remo_led_marquee_set)
#define REMO_LED_SET_BREATHING          _IOW(0x81, 0x16, struct remo_led_breathing_set)
#define REMO_LED_SET_BLINK              _IOW(0x81, 0x17, struct remo_led_blink_set)
#define REMO_LED_SET_QUEUE              _IOW(0x81, 0x18, struct remo_led_queue_set)

#define REMO_LED_SHUT_DOWN              _IO(0x81, 0x30)
#define REMO_LED_RESET                  _IO(0x81, 0x31)
#define REMO_LED_GET_STATUS             _IOR(0x81, 0x32, int)

// =====================================================================

#define ET6312_I2C_ADDRESS              0x48
#define ET6312_I2C_BUS_ID               5
// et6312 has two current range, 24mA or 48mA, we choose 24mA
#define ET6312_CHAN_MAX_CURRENT         0xFF // 24mA
#define ET6312_CHAN_DEFAULT_CURRENT     0x70 // 14mA

#define ET6312_USED_CHANNEL_NUM         (3 * REMO_RGB_LED_NUM)

#define ET6312_RISE_FALL_SCALE          2

enum et6312_led_workmode
{
    LED_WORK_MODE_ALWAYS_OFF,
    LED_WORK_MODE_ALWAYS_ON,
    LED_WORK_MODE_THREAD_1,
    LED_WORK_MODE_THREAD_2,
    LED_WORK_MODE_THREAD_3,
};

enum et6312_clk_io_mode
{
    CLK_IO_MODE_INPUT,
    CLK_IO_MODE_OUTPUT_0,
    CLK_IO_MODE_OUTPUT_INNER_CLOCK,
};

enum et6312_max_current_mode
{
    MAX_CURRENT_MODE_24MA,
    MAX_CURRENT_MODE_48MA
};

enum et6312_timer_mode
{
    TIMER_MODE_1,
    TIMER_MODE_2,
    TIMER_MODE_3,
    TIMER_MODE_4,
};

enum et6312_rise_fall_scale_type {
    RISE_FALL_SCALE_1X_NORMAL,
    RISE_FALL_SCALE_2X_SLOWER,
    RISE_FALL_SCALE_4X_SLOWER,
    RISE_FALL_SCALE_8X_FASTER
};

struct et6312_config_ops;
#pragma pack(1)
struct et6312_device
{
    bool is_connected;
    u8 max_channel;
    struct i2c_client *client;
    struct et6312_config_ops *cfg_ops;
    struct mutex lock;
};

struct et6312_devices_data
{
    struct i2c_adapter *bus_adapter;
    struct et6312_device device;
    struct miscdevice miscdev;
};

// flash/blink config
struct et6312_flash_config
{
    u8 period;
    u8 timer1_on;
    u8 timer2_on;
    u8 trise : 4;
    u8 tfall : 4;
    u8 start_delay;
};
#pragma pack()

struct et6312_config_ops {
    int (*init_device) (struct et6312_device *device);
    int (*get_status) (struct et6312_device *device);
    int (*set_auto_flash_enable) (struct et6312_device *device, bool is_enable);
    int (*set_clk_io_mode) (struct et6312_device *device, enum et6312_clk_io_mode mode);
    int (*set_shutdown_enable) (struct et6312_device *device, bool is_enable);
    int (*set_scl_shutdown_enable) (struct et6312_device *device, bool is_enable);
    int (*set_max_current_mode) (struct et6312_device *device, enum et6312_max_current_mode mode);
    int (*set_timer_mode) (struct et6312_device *device, enum et6312_timer_mode);
    int (*set_flash_config) (struct et6312_device *device, u8 rgb_n, struct et6312_flash_config *flash_config);
    int (*set_rf_scale) (struct et6312_device *device, u8 rgb_n, enum et6312_rise_fall_scale_type scale_type);

    int (*set_led_work_mode) (struct et6312_device *device, u8 channel, enum et6312_led_workmode mode);
    int (*set_led_current) (struct et6312_device *device, u8 channel, u8 val);
};

static int skip_led = 1;
static int auto_deinit = 1;
module_param(skip_led, int, 0644);
module_param(auto_deinit, int, 0644);

static struct i2c_board_info dev_info [] =
{
    {
        // I2C_BOARD_INFO("ET6312_M", (BOARD_MASTER_I2C_ADDER)),
        I2C_BOARD_INFO("ET6312", ET6312_I2C_ADDRESS),
    },
};

static int et6312_i2c_write(const struct i2c_client *client, u8 reg, u8 val)
{
    return i2c_smbus_write_byte_data(client, reg, val);
}

static int et6312_i2c_read(const struct i2c_client *client, u8 reg, u8 *buf, u8 len)
{
    int ret, i;
    struct i2c_msg msg[2];
    u8 tranfer_buf[0xFF];

    if ((len == 0) || (buf == NULL)){
        return -1;
    }

    tranfer_buf[0] = reg;

    msg[0].addr = client->addr;
    msg[0].flags = client->flags & I2C_M_TEN;
    msg[0].len = 1;
    msg[0].buf = tranfer_buf;
    msg[1].addr = client->addr;
    msg[1].flags = client->flags & I2C_M_TEN;
    msg[1].flags |= I2C_M_RD;
    msg[1].len = len;
    msg[1].buf = tranfer_buf;
    ret = i2c_transfer(client->adapter, msg, 2);
    if(ret == 2)
    {
        for(i = 0; i < len; i++){
            buf[i] = tranfer_buf[i];
        }
        return 0;
    }
    return -1;
}

static int et6312_i2c_write_bytes(const struct i2c_client *client, u8 reg, u8 *buf, u8 len)
{
    u8 tranfer_buf[0xFF];
    int ret, i;

    if ((len == 0) || (buf == NULL)){
        return -EINVAL;
    }

    tranfer_buf[0] = reg;
    // printk("write to 0x%02x data: ", reg);
    for(i = 0; i < len; i ++){
        tranfer_buf[i + 1] = buf[i];
        // printk("%02x ", buf[i]);
    }

    // printk("\n");

    ret = i2c_master_send(client, tranfer_buf, len + 1);
    if (ret != (len + 1)) {
        return -1;
    }

    return 0;
}

static int et6312_i2c_init(struct et6312_devices_data *data)
{
    int i;

    data->bus_adapter = i2c_get_adapter(ET6312_I2C_BUS_ID);
    if (NULL == data->bus_adapter) {
        pr_info("get i2c new adapter error.\n");
        return -1;
    }
    data->device.client = i2c_new_device(data->bus_adapter, &dev_info[0]);
//    data->device.client = i2c_new_device(data->bus_adapter, &et6312_i2c_dev_info);
    if (NULL == data->device.client) {
        data->device.is_connected = false;
        pr_info("get i2c new device for %d chip error.\n", i);
    }
    else {
        data->device.is_connected = true;
    }

    return 0;
}


static void et6312_i2c_release(struct et6312_devices_data *data)
{
    if (data->device.client != NULL) {
        i2c_unregister_device(data->device.client);
    }
}

static int et6312_init_device (struct et6312_device *device)
{
    int ret;
    u8 i = 0;

    mutex_init(&(device->lock));

//    val |= CLKDIR;
//    val |= TIMER_MODE_1;

//    ret = et6312_i2c_write(device->client, REG_CHIPCTR, val);
//    if(ret){
//        chip->is_connected = false;
//        return -EIO;
//    }

    // == uboot init chip, don't let channel down
    if (0) {
        for (i = 0; i < device->max_channel; ++i) {
            ret = device->cfg_ops->set_led_work_mode(device, i, LED_WORK_MODE_ALWAYS_OFF);
            if (ret) {
                return -EIO;
            }
        }
    }

    ret = device->cfg_ops->set_shutdown_enable(device, false);
    if (ret) {
        return -EIO;
    }

    ret = device->cfg_ops->set_scl_shutdown_enable(device, false);
    if (ret) {
        return -EIO;
    }

    return ret;
}

static int et6312_set_auto_flash_enable (struct et6312_device *device, bool is_enable)
{
    int ret = 0;
    u8 val = 0;
    ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1);
    if (ret) {
        return -EIO;
    }

    if (is_enable) {
        val |= AUTO_BRECON;
    }
    else {
        val &= ~AUTO_BRECON;
    }

    return et6312_i2c_write(device->client, REG_CHIPCTR, val);
}

static int et6312_set_clk_io_mode (struct et6312_device *device, enum et6312_clk_io_mode mode)
{
    int ret = 0;
    u8 val = 0;
    u8 clk_io_mode_val_table[3] = {0x01, 0x00, 0x02};

    if (mode > CLK_IO_MODE_OUTPUT_INNER_CLOCK) {
        return -EINVAL;
    }

    ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1);
    if (ret) {
        return -EIO;
    }

    val &= ~(0x03 << 5); // clear bit5:6
    val |= clk_io_mode_val_table[mode];

    return et6312_i2c_write(device->client, REG_CHIPCTR, val);
}

static int et6312_get_status (struct et6312_device *device)
{
    int ret = 0;
    u8 val = 0;

    ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1);
    if (ret) {
        return 0;
    }

    if (0 == (val & SOFTDN_EN)) {
        return 0;
    }

    return 1;
}

// if REG_CHIPCTR bit 4 is 1, chip go to work,  else chip in standby mode
static int et6312_set_shutdown_enable (struct et6312_device *device, bool is_enable)
{
    int ret = 0;
    u8 val = 0;
    ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1);
    if (ret) {
        return -EIO;
    }

    if (is_enable) {
        val &= ~SOFTDN_EN;
    }
    else {
        val |= SOFTDN_EN;
    }

    return et6312_i2c_write(device->client, REG_CHIPCTR, val);
}

static int et6312_set_scl_shutdown_enable (struct et6312_device *device, bool is_enable)
{
    int ret = 0;
    u8 val = 0;
    ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1);
    if (ret) {
        return -EIO;
    }

    if (is_enable) {
        val |= PDEN;
    }
    else {
        val &= ~PDEN;
    }

    return et6312_i2c_write(device->client, REG_CHIPCTR, val);
}

static int et6312_set_max_current_mode (struct et6312_device *device, enum et6312_max_current_mode mode)
{
    int ret = 0;
    u8 val = 0;
    ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1);
    if (ret) {
        return -EIO;
    }

    if (mode == MAX_CURRENT_MODE_24MA) {
        val &= ~IMAX_SEL;
    }
    else {
        val |= IMAX_SEL;
    }

    return et6312_i2c_write(device->client, REG_CHIPCTR, val);
}

static int et6312_set_timer_mode (struct et6312_device *device, enum et6312_timer_mode mode)
{
    int ret;
    u8 val;

    if (mode > TIMER_MODE_4) {
        return -EINVAL;
    }

    ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1);
    if (ret) {
        return -EIO;
    }
    val &= ~TMD_SEL;
    val |= (mode & TMD_SEL);
    ret = et6312_i2c_write(device->client, REG_CHIPCTR, val);
    if (ret) {
        return -EIO;
    }

    return 0;
}

static int et6312_set_flash_config (struct et6312_device *device, u8 rgb_n, struct et6312_flash_config *flash_config)
{
    int ret;
    if (rgb_n >= REMO_RGB_LED_NUM) {
        return -EINVAL;
    }
    ret = et6312_i2c_write_bytes(device->client, REG_FLASH_PERIOD_RGB0 + (rgb_n * 5),
                                (u8 *)(flash_config), sizeof(struct et6312_flash_config));
    return ret;
}

static int et6312_set_rf_scale (struct et6312_device *device, u8 rgb_n, enum et6312_rise_fall_scale_type scale_type)
{
    int ret;
    u8 val = 0;
    if (rgb_n >= REMO_RGB_LED_NUM) {
        return -EINVAL;
    }
    ret = et6312_i2c_read(device->client, REG_RF_SCALE, &val, 1);
    if (ret) {
        return -EIO;
    }
    val &= ~(0x03 << (rgb_n * 2));
    val |= ((scale_type & 0x03) << (rgb_n * 2));

    ret = et6312_i2c_write(device->client, REG_RF_SCALE, val);
    return ret;
}


static u8 et6312_led_logical_to_physical(u8 logical_led)
{
#if defined(CONFIG_REMO_PDT_TP_TINY2)
    u8 led_group_map[REMO_RGB_LED_NUM] = {0, 1, 2};
#else
    u8 led_group_map[REMO_RGB_LED_NUM] = {3, 0, 1, 2};
#endif
    if (logical_led > REMO_RGB_LED_NUM) {
        return 0;
    }

    return led_group_map[logical_led];
}

static int et6312_set_led_work_mode (struct et6312_device *device, u8 channel, enum et6312_led_workmode mode)
{
    int ret = 0;
    u8 addr = (channel / 3) + REG_LEDXMD_RGB0;
    u8 bit = channel % 3;
    u8 val = 0;
    u8 mode_magic_val_table[3] = {0x03, 0x1C, 0xE0};
    u8 mode_magic_bit_table[3] ={0, 2, 5};

//    printk("channel %u write mode %u\n", channel, mode);

    if (channel >= device->max_channel
            || mode > LED_WORK_MODE_THREAD_3) {
        return -EINVAL;
    }

    ret = et6312_i2c_read(device->client, addr, &val, 1);
    if (ret) {
        return -EIO;
    }

    if (bit == 0 && mode == LED_WORK_MODE_THREAD_3) { // led 1/4/7/10 only can carry on thread1/2
        mode = LED_WORK_MODE_THREAD_1;
    }

    val &= ~(mode_magic_val_table[bit]);
    val |= ((mode << mode_magic_bit_table[bit]) & mode_magic_val_table[bit]);

//    printk("channel %u write mode addr 0x%02x with %u\n", channel, addr, val);
    ret = et6312_i2c_write(device->client, addr, val);
    if (ret) {
        return -EIO;
    }

    return 0;
}

static int et6312_set_led_current (struct et6312_device *device, u8 channel, u8 val)
{
    u8 addr;

    if (channel >= device->max_channel) {
        return -EINVAL;
    }

    addr = channel + REG_LED1_CURT;
//    printk("set current to addr 0x%02x with %d\n", addr, val);

    return et6312_i2c_write(device->client, addr, val);
}

static int et6312_set_led_color(struct et6312_device *device, struct remo_led_color_set *color_set)
{
//    int i, ret = 0, j = 0;
//    int channel;
    int ret = 0;
    u8 i, j, channel;
    u8 rgb_val[3];

    device->cfg_ops->set_auto_flash_enable(device, false);

    for (i = 0; i < REMO_RGB_LED_NUM; ++i) {
        rgb_val[0] = RGB_VALUE_R(color_set->rgb_colors[i]);
        rgb_val[1] = RGB_VALUE_G(color_set->rgb_colors[i]);
        rgb_val[2] = RGB_VALUE_B(color_set->rgb_colors[i]);
        for (j = 0; j < 3; ++j) {
            channel = i * 3 + j;
            if (rgb_val[j] > 0) {
                device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_ON);
                ret |= device->cfg_ops->set_led_current(device, channel, rgb_val[j]);
            }
            else {
                device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF);
            }
        }
    }
    return ret;
}

static int et6312_set_led_color_ioctl(struct et6312_devices_data *pdata, unsigned long arg)
{
//    int i, ret = 0, j = 0;
//    int channel;
    struct et6312_device *device;
    struct remo_led_color_set color_set;

    if(copy_from_user(&color_set, (struct remo_led_color_set __user *)arg, sizeof(color_set))){
        return -EFAULT;
    }

    device = &pdata->device;

    return et6312_set_led_color(device, &color_set);
}

//
static int et6312_set_led_marquee(struct et6312_device *device, struct remo_led_marquee_set *marquee_set)
{
    int ret = 0;
    u8 i, j, channel;
    u8 rgb_val[3];

    struct et6312_flash_config blink_config = {0};

    device->cfg_ops->set_timer_mode(device, TIMER_MODE_1);

    ret |= device->cfg_ops->set_auto_flash_enable(device, false);

    blink_config.period = (REMO_RGB_LED_NUM - 1) * marquee_set->delay_ms / 128;
    blink_config.timer1_on = 256 / 2;

    for (i = 0; i < REMO_RGB_LED_NUM; ++i) {
        rgb_val[0] = RGB_VALUE_R(marquee_set->rgb_colors[i]);
        rgb_val[1] = RGB_VALUE_G(marquee_set->rgb_colors[i]);
        rgb_val[2] = RGB_VALUE_B(marquee_set->rgb_colors[i]);
        for (j = 0; j < 3; ++j) {
            channel = i * 3 + j;
            if (rgb_val[j] > 0) {
                device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_THREAD_1);
                ret |= device->cfg_ops->set_led_current(device, channel, rgb_val[j]);
            }
            else {
                device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF);
            }
        }
        blink_config.start_delay = i * marquee_set->delay_ms / (128 * ET6312_RISE_FALL_SCALE);
        ret |= device->cfg_ops->set_rf_scale(device, et6312_led_logical_to_physical(i), RISE_FALL_SCALE_2X_SLOWER);
        ret |= device->cfg_ops->set_flash_config(device, et6312_led_logical_to_physical(i), &blink_config);
    }

    ret |= device->cfg_ops->set_auto_flash_enable(device, true);

    return ret;
}

static int et6312_set_led_marquee_ioctl(struct et6312_devices_data *pdata, unsigned long arg)
{
    struct et6312_device *device;
    struct remo_led_marquee_set marquee_set;

    if (copy_from_user(&marquee_set, (struct remo_led_marquee_set __user *)arg, sizeof(marquee_set))) {
        return -EFAULT;
    }

    device = &pdata->device;

    return et6312_set_led_marquee(device, &marquee_set);
}

static int et6312_set_led_breathing(struct et6312_device *device, struct remo_led_breathing_set *breathing_set)
{
    int ret = 0;
    u8 i, j, channel;
    u8 rgb_val[3];

    struct et6312_flash_config blink_config = {0};

    device->cfg_ops->set_timer_mode(device, TIMER_MODE_1);

    ret |= device->cfg_ops->set_auto_flash_enable(device, false);

    blink_config.period = breathing_set->period_ms / 128;
    blink_config.timer1_on = 125;
    blink_config.trise = breathing_set->period_ms / (128 * ET6312_RISE_FALL_SCALE);
    blink_config.tfall = breathing_set->period_ms / (128 * ET6312_RISE_FALL_SCALE);
//    blink_config.tfall = 15;

    for (i = 0; i < REMO_RGB_LED_NUM; ++i) {
        rgb_val[0] = RGB_VALUE_R(breathing_set->rgb_colors[i]);
        rgb_val[1] = RGB_VALUE_G(breathing_set->rgb_colors[i]);
        rgb_val[2] = RGB_VALUE_B(breathing_set->rgb_colors[i]);
        for (j = 0; j < 3; ++j) {
            channel = i * 3 + j;
            if (rgb_val[j] > 0) {
                device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_THREAD_1);
                ret |= device->cfg_ops->set_led_current(device, channel, rgb_val[j]);
            }
            else {
                device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF);
            }
        }
        blink_config.start_delay = 0;

        ret |= device->cfg_ops->set_rf_scale(device, et6312_led_logical_to_physical(i), RISE_FALL_SCALE_2X_SLOWER);
        ret |= device->cfg_ops->set_flash_config(device, et6312_led_logical_to_physical(i), &blink_config);
    }

    ret |= device->cfg_ops->set_auto_flash_enable(device, true);
    return ret;
}

static int et6312_set_led_breathing_ioctl(struct et6312_devices_data *pdata, unsigned long arg)
{
    struct et6312_device *device;
    struct remo_led_breathing_set breathing_set;

    if (copy_from_user(&breathing_set, (struct remo_led_breathing_set __user *)arg, sizeof(breathing_set))) {
        return -EFAULT;
    }

    device = &pdata->device;
    return et6312_set_led_breathing(device, &breathing_set);
}

static int et6312_set_led_blink(struct et6312_device *device, struct remo_led_blink_set *blink_set)
{
    int ret = 0;
    u8 i, j, channel;
    u8 rgb_val[3];

    struct et6312_flash_config blink_config = {0};

    device->cfg_ops->set_timer_mode(device, TIMER_MODE_1);

    ret |= device->cfg_ops->set_auto_flash_enable(device, false);

    blink_config.period = blink_set->interval_ms / 128;
    blink_config.timer1_on = 125;
//    blink_config.tfall = 15;

    for (i = 0; i < REMO_RGB_LED_NUM; ++i) {
        rgb_val[0] = RGB_VALUE_R(blink_set->rgb_colors[i]);
        rgb_val[1] = RGB_VALUE_G(blink_set->rgb_colors[i]);
        rgb_val[2] = RGB_VALUE_B(blink_set->rgb_colors[i]);
        for (j = 0; j < 3; ++j) {
            channel = i * 3 + j;
            if (rgb_val[j] > 0) {
                device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_THREAD_1);
                ret |= device->cfg_ops->set_led_current(device, channel, rgb_val[j]);
            }
            else {
                device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF);
            }
        }
        blink_config.start_delay = 0;

        ret |= device->cfg_ops->set_rf_scale(device, et6312_led_logical_to_physical(i), RISE_FALL_SCALE_2X_SLOWER);
        ret |= device->cfg_ops->set_flash_config(device, et6312_led_logical_to_physical(i), &blink_config);
    }

    ret |= device->cfg_ops->set_auto_flash_enable(device, true);
    return ret;
}

static int et6312_set_led_blink_ioctl(struct et6312_devices_data *pdata, unsigned long arg)
{
    struct et6312_device *device;
    struct remo_led_blink_set blink_set;

    if (copy_from_user(&blink_set, (struct remo_led_blink_set __user *)arg, sizeof(blink_set))) {
        return -EFAULT;
    }

    device = &pdata->device;
    return et6312_set_led_blink(device, &blink_set);
}

static int et6312_set_led_queue(struct et6312_device *device, struct remo_led_queue_set *queue_set)
{
    int ret = 0;
    u8 i, j, channel;
    u8 rgb_val[3];

    struct et6312_flash_config blink_config = {0};

    device->cfg_ops->set_timer_mode(device, TIMER_MODE_1);

    ret |= device->cfg_ops->set_auto_flash_enable(device, false);

    blink_config.period = 5 * queue_set->delay_ms / (128);

    for (i = 0; i < REMO_RGB_LED_NUM; ++i) {
        rgb_val[0] = RGB_VALUE_R(queue_set->rgb_colors[i]);
        rgb_val[1] = RGB_VALUE_G(queue_set->rgb_colors[i]);
        rgb_val[2] = RGB_VALUE_B(queue_set->rgb_colors[i]);
        for (j = 0; j < 3; ++j) {
            channel = i * 3 + j;
            if (rgb_val[j] > 0) {
                device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_THREAD_1);
                ret |= device->cfg_ops->set_led_current(device, channel, rgb_val[j]);
            }
            else {
                device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF);
            }
        }
        blink_config.timer1_on = (REMO_RGB_LED_NUM - i) * 256 / 5;
        blink_config.start_delay = (i + 1) * queue_set->delay_ms / (128);
        ret |= device->cfg_ops->set_rf_scale(device, et6312_led_logical_to_physical(i), RISE_FALL_SCALE_2X_SLOWER);
        ret |= device->cfg_ops->set_flash_config(device, et6312_led_logical_to_physical(i), &blink_config);
    }

    ret |= device->cfg_ops->set_auto_flash_enable(device, true);

    return ret;
}

static int et6312_set_led_queue_ioctl(struct et6312_devices_data *pdata, unsigned long arg)
{
    struct et6312_device *device;
    struct remo_led_queue_set queue_set;

    if (copy_from_user(&queue_set, (struct remo_led_queue_set __user *)arg, sizeof(queue_set))) {
        return -EFAULT;
    }

    device = &pdata->device;
    return et6312_set_led_queue(device, &queue_set);
}

static int et6312_shutdown_ioctl(struct et6312_devices_data *pdata)
{
    struct et6312_device *device;
    device = &pdata->device;

    return device->cfg_ops->set_shutdown_enable(device, true);
}

static int et6312_reset_ioctl(struct et6312_devices_data *pdata)
{
    struct et6312_device *device;
    device = &pdata->device;

    return device->cfg_ops->set_shutdown_enable(device, false);
}

static int et6312_get_status_ioctl(struct et6312_devices_data *pdata, unsigned long arg)
{
    int status = 0;
    struct et6312_device *device;
    device = &pdata->device;

    status = device->cfg_ops->get_status(device);
    if (copy_to_user((int __user *)arg, &status, sizeof(int))) {
        return -EFAULT;
    }

    return 0;
}

static int et6312_set_default_blue_marquee(struct et6312_device *device)
{
    struct remo_led_marquee_set marquee_set = {
#if defined(CONFIG_REMO_PDT_TP_TINY2)
        .rgb_colors = LED_COLORSET_SAME_COLORS(MAKE_RGB_VALUE(0, 5, 50)),
#else
        .rgb_colors = LED_COLORSET_SAME_COLORS(MAKE_RGB_VALUE(0, 30, 180)),
#endif
        .delay_ms = 800,
    };

    return et6312_set_led_marquee(device, &marquee_set);
}

// green and blue blink 500ms
static int et6312_set_upgrading_mode(struct et6312_device *device)
{
    int ret = 0;
    u8 i, j, channel;

    struct et6312_flash_config blink_config = {0};

    device->cfg_ops->set_timer_mode(device, TIMER_MODE_1);

    ret |= device->cfg_ops->set_auto_flash_enable(device, false);

    blink_config.period = 2000 / 256;
    blink_config.timer1_on = 255 / REMO_RGB_LED_NUM;
    blink_config.timer2_on = 255 / REMO_RGB_LED_NUM;

/*
#define LED_COLORSET_GREEN_ALL      LED_COLORSET_SAME_COLORS(MAKE_RGB_VALUE(0, 0x25, 0))
#define LED_COLORSET_BLUE_ALL       LED_COLORSET_SAME_COLORS(MAKE_RGB_VALUE(0, 0, 0x25))
*/

    for (i = 0; i < REMO_RGB_LED_NUM; ++i) {
        for (j = 0; j < 3; ++j) {
            channel = i * 3 + j;
            if (j == 0) {  //r
                device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF);
            }
            else if (j == 1) { //g
                device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_THREAD_1);
                ret |= device->cfg_ops->set_led_current(device, channel, 0x25);
                // device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF);
            }
            else { //b
                device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_THREAD_3);
                ret |= device->cfg_ops->set_led_current(device, channel, 0x25);
            }
        }
        blink_config.start_delay = 0;

        ret |= device->cfg_ops->set_rf_scale(device, et6312_led_logical_to_physical(i), RISE_FALL_SCALE_2X_SLOWER);
        ret |= device->cfg_ops->set_flash_config(device, et6312_led_logical_to_physical(i), &blink_config);
    }

    ret |= device->cfg_ops->set_auto_flash_enable(device, true);
    return ret;
}

static long et6312_ioctl_ops(struct file *file, unsigned int cmd, unsigned long arg)
{
    int ret;
    struct et6312_devices_data *pdata = container_of(file->private_data,
                      struct et6312_devices_data, miscdev);

    switch(cmd)
    {
    case REMO_LED_SET_COLOR:
        ret = et6312_set_led_color_ioctl(pdata, arg);
        break;
    case REMO_LED_SET_MARQUEE:
        ret = et6312_set_led_marquee_ioctl(pdata, arg);
        break;
    case REMO_LED_SET_BREATHING:
        ret = et6312_set_led_breathing_ioctl(pdata, arg);
        break;
    case REMO_LED_SET_BLINK:
        ret = et6312_set_led_blink_ioctl(pdata, arg);
        break;
    case REMO_LED_SET_QUEUE:
        ret = et6312_set_led_queue_ioctl(pdata, arg);
        break;
    case REMO_LED_SHUT_DOWN:
        ret = et6312_shutdown_ioctl(pdata);
        break;
    case REMO_LED_RESET:
        ret = et6312_reset_ioctl(pdata);
        break;
    case REMO_LED_GET_STATUS:
        ret = et6312_get_status_ioctl(pdata, arg);
        break;
    default:
        return ENOTTY;
    }

    return ret;
}

static int et6312_open_ops(struct inode *inode, struct file *filp)
{
    struct et6312_devices_data *pdata = container_of(filp->private_data,
                          struct et6312_devices_data, miscdev);

    if (!pdata->device.is_connected) {
        return -EIO;
    }

    return 0;
}

static int et6312_release_ops(struct inode *inode, struct file *filp)
{
    return 0;
}

static struct et6312_config_ops et6312_cfg_ops = {
    .init_device = et6312_init_device,
    .get_status = et6312_get_status,
    .set_auto_flash_enable = et6312_set_auto_flash_enable,
    .set_clk_io_mode = et6312_set_clk_io_mode,
    .set_shutdown_enable = et6312_set_shutdown_enable,
    .set_scl_shutdown_enable = et6312_set_scl_shutdown_enable,
    .set_max_current_mode = et6312_set_max_current_mode,
    .set_timer_mode = et6312_set_timer_mode,
    .set_flash_config = et6312_set_flash_config,
    .set_rf_scale = et6312_set_rf_scale,

    .set_led_work_mode = et6312_set_led_work_mode,
    .set_led_current = et6312_set_led_current,
};

static struct file_operations et6312_fops =
{
    .owner = THIS_MODULE,
    .unlocked_ioctl = et6312_ioctl_ops,
    .open = et6312_open_ops,
    .release = et6312_release_ops,
};

static struct et6312_devices_data et6312_data;
static const char *device_name = "remo_led";

int et6312_module_init(void)
{
    int ret = 0;
    et6312_data.device.max_channel = ET6312_USED_CHANNEL_NUM;

    et6312_data.bus_adapter = NULL;
    et6312_data.miscdev.minor = MISC_DYNAMIC_MINOR;
    et6312_data.miscdev.name = device_name;
    et6312_data.miscdev.fops = &et6312_fops;

    ret = et6312_i2c_init(&et6312_data);
    if (ret) {
        pr_info("Init et6312 i2c client fail %d\n", ret);
        goto i2c_error;
    }

    et6312_data.device.cfg_ops = &et6312_cfg_ops;

    ret = et6312_data.device.cfg_ops->init_device(&et6312_data.device);

    if (ret) {
        pr_info("init_device faild with %d\n", ret);
        et6312_data.device.is_connected = false;
        goto i2c_error;
    }
    else {
        et6312_data.device.is_connected = true;
    }

    // == init this state in uboot
    if (!skip_led) {
        et6312_set_default_blue_marquee(&(et6312_data.device));
    }

    // == init this state in uboot
    if (0) {
        et6312_set_upgrading_mode(&(et6312_data.device));
    }

    ret = misc_register(&(et6312_data.miscdev));
    if (ret) {
        pr_info("register et6312 misc device fail.%d\n", ret);
        goto register_error;
    }

//    device_create_file(et6312_data.miscdev.this_device, &dev_attr_led_enable);
//    device_create_file(et6312_data.miscdev.this_device, &dev_attr_led_duty);

    return 0;

register_error:
    misc_deregister(&et6312_data.miscdev);
i2c_error:
    et6312_i2c_release(&et6312_data);

    return -1;
}

void et6312_module_exit(void)
{
    pr_info("ET6312 module exit.\n");

    // == don't disable it by software, just let it power off
    if (auto_deinit) {
        et6312_data.device.cfg_ops->set_shutdown_enable(&et6312_data.device, true);
    }

    et6312_i2c_release(&et6312_data);

    misc_deregister(&et6312_data.miscdev);
}

module_init(et6312_module_init);
module_exit(et6312_module_exit);

MODULE_AUTHOR("remo");
MODULE_DESCRIPTION("et6312 driver");
MODULE_LICENSE("GPL");

 

posted @ 2025-06-12 16:40  流水灯  阅读(35)  评论(0)    收藏  举报