diff --git a/MAINTAINERS b/MAINTAINERS index ff6b074935c3..d029cf7f29c7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14163,4 +14163,8 @@ F: arch/arm64/boot/dts/amlogic/gxl_p241_v2_1g_buildroot.dts AMLOGIC MKIMAGE SCRIPT M: Bo Yang F: scripts/amlogic/mkimage_32.sh -F: scripts/amlogic/mkimage_64.sh \ No newline at end of file +F: scripts/amlogic/mkimage_64.sh + +AMLOGIC ledring driver +M: Renjun Xu +F: drivers/amlogic/ledring/ledring.c diff --git a/arch/arm64/boot/dts/amlogic/axg_s400.dts b/arch/arm64/boot/dts/amlogic/axg_s400.dts index ed518368698b..0399d94cc8d1 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s400.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s400.dts @@ -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"; diff --git a/arch/arm64/boot/dts/amlogic/axg_s400_v03.dts b/arch/arm64/boot/dts/amlogic/axg_s400_v03.dts index 18e9c8084e8b..c59c1504ef74 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s400_v03.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s400_v03.dts @@ -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>; diff --git a/arch/arm64/configs/meson64_smarthome_defconfig b/arch/arm64/configs/meson64_smarthome_defconfig index 51b620659447..87621647e65f 100644 --- a/arch/arm64/configs/meson64_smarthome_defconfig +++ b/arch/arm64/configs/meson64_smarthome_defconfig @@ -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 diff --git a/drivers/amlogic/Kconfig b/drivers/amlogic/Kconfig index 35cd9a3f1b2e..6fbeb543a418 100644 --- a/drivers/amlogic/Kconfig +++ b/drivers/amlogic/Kconfig @@ -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 diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile index 98bbf90e8be8..f704db7222fd 100644 --- a/drivers/amlogic/Makefile +++ b/drivers/amlogic/Makefile @@ -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/ diff --git a/drivers/amlogic/ledring/Kconfig b/drivers/amlogic/ledring/Kconfig new file mode 100644 index 000000000000..2cd6eaf807d0 --- /dev/null +++ b/drivers/amlogic/ledring/Kconfig @@ -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. diff --git a/drivers/amlogic/ledring/Makefile b/drivers/amlogic/ledring/Makefile new file mode 100644 index 000000000000..d8bcdaf8be34 --- /dev/null +++ b/drivers/amlogic/ledring/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for LEDRING +# + +obj-$(CONFIG_AMLOGIC_LEDRING) += ledring.o diff --git a/drivers/amlogic/ledring/ledring.c b/drivers/amlogic/ledring/ledring.c new file mode 100644 index 000000000000..dff3779c8495 --- /dev/null +++ b/drivers/amlogic/ledring/ledring.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("i2c driver for ledring"); +MODULE_LICENSE("GPL");