mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 04:10:18 +09:00
ledring: add ledring driver
PD#156869: add ledring driver Change-Id: I162133eb7eedbf8705ec7c7cdd6ec401be9b92ea Signed-off-by: Renjun Xu <renjun.xu@amlogic.com>
This commit is contained in:
@@ -14163,4 +14163,8 @@ F: arch/arm64/boot/dts/amlogic/gxl_p241_v2_1g_buildroot.dts
|
||||
AMLOGIC MKIMAGE SCRIPT
|
||||
M: Bo Yang <bo.yang@amlogic.com>
|
||||
F: scripts/amlogic/mkimage_32.sh
|
||||
F: scripts/amlogic/mkimage_64.sh
|
||||
F: scripts/amlogic/mkimage_64.sh
|
||||
|
||||
AMLOGIC ledring driver
|
||||
M: Renjun Xu <renjun.xu@amlogic.com>
|
||||
F: drivers/amlogic/ledring/ledring.c
|
||||
|
||||
@@ -911,10 +911,10 @@
|
||||
pinctrl-names="default";
|
||||
pinctrl-0=<&ao_i2c_master_pin2>;
|
||||
|
||||
pca9557: pca9557@0x1f {
|
||||
compatible = "nxp,pca9557";
|
||||
reg = <0x1f>;
|
||||
status = "okay";
|
||||
aml_pca9557: aml_pca9557@0x1f {
|
||||
compatible = "aml, ledring";
|
||||
reg = <0x1f>;
|
||||
status = "okay";
|
||||
};
|
||||
tlv320adc3101_30: tlv320adc3101_30@30 {
|
||||
compatible = "ti,tlv320adc3101";
|
||||
|
||||
@@ -912,11 +912,12 @@
|
||||
pinctrl-names="default";
|
||||
pinctrl-0=<&ao_i2c_master_pin2>;
|
||||
|
||||
pca9557: pca9557@0x1f {
|
||||
compatible = "nxp,pca9557";
|
||||
reg = <0x1f>;
|
||||
status = "okay";
|
||||
aml_pca9557: aml_pca9557@0x1f {
|
||||
compatible = "aml, ledring";
|
||||
reg = <0x1f>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
tlv320adc3101_30: tlv320adc3101_30@30 {
|
||||
compatible = "ti,tlv320adc3101";
|
||||
#sound-dai-cells = <0>;
|
||||
|
||||
@@ -288,6 +288,7 @@ CONFIG_AMLOGIC_SUSPEND=y
|
||||
CONFIG_AMLOGIC_GX_SUSPEND=y
|
||||
CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND=y
|
||||
CONFIG_AMLOGIC_LED=y
|
||||
CONFIG_AMLOGIC_LEDRING=y
|
||||
CONFIG_AMLOGIC_LED_SYS=y
|
||||
CONFIG_AMLOGIC_JTAG=y
|
||||
CONFIG_AMLOGIC_JTAG_MESON=y
|
||||
|
||||
@@ -116,6 +116,8 @@ source "drivers/amlogic/secure_monitor/Kconfig"
|
||||
|
||||
source "drivers/amlogic/tee/Kconfig"
|
||||
|
||||
source "drivers/amlogic/ledring/Kconfig"
|
||||
|
||||
source "drivers/amlogic/memory_ext/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -108,3 +108,5 @@ obj-$(CONFIG_AMLOGIC_M8B_SM) += secure_monitor/
|
||||
obj-$(CONFIG_AMLOGIC_TEE) += tee/
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_MEMORY_EXTEND) += memory_ext/
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_LEDRING) += ledring/
|
||||
|
||||
8
drivers/amlogic/ledring/Kconfig
Normal file
8
drivers/amlogic/ledring/Kconfig
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# LEDRING drivers configuration
|
||||
#
|
||||
config AMLOGIC_LEDRING
|
||||
bool "System ledring Support"
|
||||
default n
|
||||
help
|
||||
This option enables support for system ledring drivers.
|
||||
5
drivers/amlogic/ledring/Makefile
Normal file
5
drivers/amlogic/ledring/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# Makefile for LEDRING
|
||||
#
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_LEDRING) += ledring.o
|
||||
335
drivers/amlogic/ledring/ledring.c
Normal file
335
drivers/amlogic/ledring/ledring.c
Normal file
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* drivers/amlogic/ledring/ledring.c
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#define addr_led_reg0 0 //input data
|
||||
#define addr_led_reg1 1 //output data
|
||||
#define addr_led_reg2 2 //inversion
|
||||
#define addr_led_reg3 3 //direction
|
||||
|
||||
#define DEFAULT_SPEED 230 //default speed 230ms
|
||||
|
||||
#define DEVICE_NAME "ledring"
|
||||
#define CHAR_DEV_NAME "aml_ledring"
|
||||
|
||||
#define CMD_LEDRING_ARG 0x100001
|
||||
|
||||
static int major;
|
||||
static struct class *cls;
|
||||
static struct timer_list mtimer;
|
||||
static struct work_struct ledring_work;
|
||||
static int ledring_speed;
|
||||
static struct kobject *ledring_kobj;
|
||||
static struct i2c_client *g_client;
|
||||
static int m_index;
|
||||
static int tmp1_rgb_style, tmp2_rgb_style, tmp3_rgb_style;
|
||||
static int tmp4_rgb_style, tmp5_rgb_style, tmp6_rgb_style;
|
||||
static int timeout_flag;
|
||||
static int timeout;
|
||||
|
||||
static struct _style {
|
||||
int num;
|
||||
int speed;
|
||||
int timeout;
|
||||
int style[6][6];
|
||||
} styleData = {
|
||||
6, DEFAULT_SPEED, 0,
|
||||
{
|
||||
/*rgb1 rgb2 rgb3 rgb4 rgb5 rgb6*/
|
||||
{0x80, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||
{0x40, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||
{0x20, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||
{0x10, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||
{0x08, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||
{0x04, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||
},
|
||||
};
|
||||
|
||||
static int leds_light(void);
|
||||
static int leds_init(void);
|
||||
|
||||
static const struct i2c_device_id ledring_id[] = {
|
||||
{"aml_pca9557"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ledring_id);
|
||||
|
||||
static const struct of_device_id ledring_dt_ids[] = {
|
||||
{
|
||||
.compatible = "aml, ledring",
|
||||
.data = (void *)NULL
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ledring_dt_ids);
|
||||
|
||||
static ssize_t speed_level_write(
|
||||
struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
int m_speed;
|
||||
|
||||
ret = kstrtouint(buf, 10, &m_speed);
|
||||
if (ret == 0) {
|
||||
pr_info("set speed level: %d ms\n", m_speed);
|
||||
} else {
|
||||
pr_info("set speed level fail, use default speed!!\n");
|
||||
m_speed = DEFAULT_SPEED;
|
||||
}
|
||||
ledring_speed = m_speed;
|
||||
schedule_work(&ledring_work);
|
||||
mod_timer(&mtimer, jiffies+ledring_speed*HZ/1000);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t speed_level_read(
|
||||
struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
pr_info("current speed level: %d ms\n", ledring_speed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kobj_attribute
|
||||
speed_attribute =
|
||||
__ATTR(speed, 0664,
|
||||
speed_level_read,
|
||||
speed_level_write);
|
||||
static struct attribute
|
||||
*attrs[] = {
|
||||
&speed_attribute.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group
|
||||
attr_group = {
|
||||
.attrs = attrs,
|
||||
};
|
||||
|
||||
static void mtimer_function(
|
||||
unsigned long data)
|
||||
{
|
||||
schedule_work(&ledring_work);
|
||||
mod_timer(&mtimer, jiffies+ledring_speed*HZ/1000);
|
||||
}
|
||||
|
||||
static void ledring_work_func(
|
||||
struct work_struct *work)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (timeout_flag == 0) {
|
||||
leds_light();
|
||||
} else {
|
||||
timeout--;
|
||||
if (timeout <= 0) {
|
||||
timeout = 0;
|
||||
del_timer(&mtimer);
|
||||
ret = i2c_smbus_write_byte_data(g_client,
|
||||
addr_led_reg1, 0);
|
||||
if (ret < 0)
|
||||
pr_err("led set reg1 fail!\n");
|
||||
} else
|
||||
leds_light();
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_timer_task(void)
|
||||
{
|
||||
setup_timer(&mtimer, mtimer_function, 0);
|
||||
mod_timer(&mtimer, jiffies+ledring_speed*HZ/1000);
|
||||
INIT_WORK(&ledring_work, ledring_work_func);
|
||||
}
|
||||
|
||||
static int leds_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(g_client,
|
||||
addr_led_reg3, 0);
|
||||
if (ret < 0) {
|
||||
pr_err("led init reg3 fail!\n");
|
||||
return -1;
|
||||
}
|
||||
ret = i2c_smbus_write_byte_data(g_client,
|
||||
addr_led_reg1, 0);
|
||||
if (ret < 0) {
|
||||
pr_err("led init reg1 fail!\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int leds_light(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((tmp1_rgb_style != styleData.style[m_index][0]) ||
|
||||
(tmp2_rgb_style != styleData.style[m_index][1]) ||
|
||||
(tmp3_rgb_style != styleData.style[m_index][2]) ||
|
||||
(tmp4_rgb_style != styleData.style[m_index][3]) ||
|
||||
(tmp5_rgb_style != styleData.style[m_index][4]) ||
|
||||
(tmp6_rgb_style != styleData.style[m_index][5])) {
|
||||
ret = i2c_smbus_write_byte_data(g_client,
|
||||
addr_led_reg1, styleData.style[m_index][0]);
|
||||
if (ret < 0)
|
||||
pr_err("led lit fail!\n");
|
||||
tmp1_rgb_style = styleData.style[m_index][0];
|
||||
tmp2_rgb_style = styleData.style[m_index][1];
|
||||
tmp3_rgb_style = styleData.style[m_index][2];
|
||||
tmp4_rgb_style = styleData.style[m_index][3];
|
||||
tmp5_rgb_style = styleData.style[m_index][4];
|
||||
tmp6_rgb_style = styleData.style[m_index][5];
|
||||
}
|
||||
|
||||
if (++m_index > 5)
|
||||
m_index = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long ledring_ioctl(struct file *file,
|
||||
unsigned int cmd,
|
||||
unsigned long args)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case CMD_LEDRING_ARG:
|
||||
m_index = 0;
|
||||
del_timer(&mtimer);
|
||||
ret = i2c_smbus_write_byte_data(g_client,
|
||||
addr_led_reg1, 0);
|
||||
if (ret < 0)
|
||||
pr_err("led set reg1 fail!\n");
|
||||
|
||||
ret = copy_from_user(&styleData,
|
||||
(int *)args, sizeof(styleData));
|
||||
if (styleData.speed < 0) {
|
||||
pr_info("set speed level fail,use default speed!!\n");
|
||||
ledring_speed = DEFAULT_SPEED;
|
||||
} else if (ledring_speed != styleData.speed)
|
||||
ledring_speed = styleData.speed;
|
||||
|
||||
if (styleData.timeout != 0) {
|
||||
timeout_flag = 1;
|
||||
timeout = styleData.timeout*1000/ledring_speed;
|
||||
} else
|
||||
timeout_flag = 0;
|
||||
schedule_work(&ledring_work);
|
||||
mod_timer(&mtimer, jiffies+ledring_speed*HZ/1000);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations ledring_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
//.unlocked_ioctl=ledring_ioctl,
|
||||
.compat_ioctl = ledring_ioctl,
|
||||
};
|
||||
|
||||
static int ledring_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *i2c_id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
ledring_kobj = kobject_create_and_add("ledring",
|
||||
kernel_kobj);
|
||||
if (!ledring_kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sysfs_create_group(ledring_kobj, &attr_group);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
g_client = client;
|
||||
|
||||
ledring_speed = DEFAULT_SPEED;
|
||||
|
||||
ret = leds_init();
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
setup_timer_task();
|
||||
|
||||
major = register_chrdev(major, CHAR_DEV_NAME,
|
||||
&ledring_fops);
|
||||
cls = class_create(THIS_MODULE, DEVICE_NAME);
|
||||
device_create(cls, NULL, MKDEV(major, 0), NULL,
|
||||
DEVICE_NAME);
|
||||
return 0;
|
||||
err:
|
||||
kobject_put(ledring_kobj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int ledring_remove(struct i2c_client *client)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
device_destroy(cls, MKDEV(major, 0));
|
||||
class_destroy(cls);
|
||||
unregister_chrdev(major, CHAR_DEV_NAME);
|
||||
|
||||
flush_work(&ledring_work);
|
||||
del_timer(&mtimer);
|
||||
kobject_put(ledring_kobj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver ledring_drv = {
|
||||
.driver = {
|
||||
.name = "aml_ledring",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ledring_dt_ids,
|
||||
},
|
||||
.probe = ledring_probe,
|
||||
.remove = ledring_remove,
|
||||
.id_table = ledring_id,
|
||||
};
|
||||
|
||||
static int __init ledring_init(void)
|
||||
{
|
||||
return i2c_add_driver(&ledring_drv);
|
||||
}
|
||||
|
||||
static void __exit ledring_exit(void)
|
||||
{
|
||||
i2c_del_driver(&ledring_drv);
|
||||
}
|
||||
|
||||
arch_initcall(ledring_init);
|
||||
module_exit(ledring_exit);
|
||||
MODULE_AUTHOR("renjun.xu <renjun.xu@amlogic.com>");
|
||||
MODULE_DESCRIPTION("i2c driver for ledring");
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user