| /* |
| * Base driver for mbtk |
| * |
| * Copyright (C) 2019 ASR. |
| * |
| * This file is subject to the terms and conditions of the GNU General |
| * Public License. See the file "COPYING" in the main directory of this |
| * archive for more details. |
| * |
| * 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/i2c.h> |
| #include <linux/irq.h> |
| #include <linux/mfd/core.h> |
| #include <linux/mfd/88pm80x.h> |
| #include <linux/slab.h> |
| #include <linux/delay.h> |
| #include <linux/of_device.h> |
| #include <linux/uaccess.h> |
| #include <linux/proc_fs.h> |
| #include <linux/fs.h> |
| #include <linux/string.h> |
| #include <sound/pcm.h> |
| #include <sound/soc.h> |
| #include <linux/gpio.h> |
| //#include <plat/mfp.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/pinctrl/consumer.h> |
| #include <linux/platform_device.h> |
| #include <linux/mutex.h> |
| #include <linux/of.h> |
| #include <linux/of_gpio.h> |
| #include <asm/io.h> |
| #include <linux/interrupt.h> |
| #include <linux/cputype.h> |
| #include <soc/asr/addr-map.h> |
| #include <linux/clk.h> |
| |
| #ifndef APB_VIRT_BASE |
| #define APB_VIRT_BASE IOMEM(0xfe000000) |
| #endif |
| |
| |
| #define tas5431_amp_DEBUG 1 |
| |
| static const struct i2c_device_id tas5431_amplifier_id_table[] = { |
| {"tas5431-amplifier", 0}, |
| {} /* NULL terminated */ |
| }; |
| MODULE_DEVICE_TABLE(i2c, tas5431_amplifier_id_table); |
| |
| static const struct of_device_id tas5431_amp_dt_ids[] = { |
| { .compatible = "tas5431-amplifier", }, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, tas5431_amp_dt_ids); |
| |
| static struct i2c_client *g_tas5431_amp_client = NULL; |
| |
| static struct device_node *g_tas5431_amp_node = NULL; |
| |
| int g_standy_gpio = -1; |
| #define tas5431_amp_reg_NUM 0xFF |
| /* base functions */ |
| |
| |
| |
| /****************************************************************************** |
| * Function : tas5431_amp_reg_read |
| ******************************************************************************* |
| * |
| * Description : |
| * |
| * Parameters : char RegAddr |
| * Parameters : unsigned short *value |
| * |
| * Output Param : None. |
| * |
| * Return value : |
| * |
| * Notes : |
| *******************************************************************************/ |
| static int tas5431_amp_reg_read(struct i2c_client *client, char reg) |
| { |
| char data[1] = {0}; |
| int value = 0x0; |
| |
| data[0] = reg & 0xFF; |
| |
| if(i2c_master_send(client, data, 1) == 1) { |
| i2c_master_recv(client, data, 1); |
| value = data[0]; |
| #ifdef tas5431_amp_DEBUG |
| printk(KERN_INFO"%s: tas5431_amp read reg:[0x%02x]=0x%02x\n", __FUNCTION__, reg, value); |
| #endif |
| return value; |
| } else { |
| printk(KERN_INFO"%s: tas5431_amp read failed.\n", __FUNCTION__); |
| return -EIO; |
| } |
| } |
| |
| |
| static int tas5431_amp_read(char reg, unsigned short *oValue) |
| { |
| int value; |
| |
| value = tas5431_amp_reg_read(g_tas5431_amp_client, reg); |
| |
| if (value < 0) |
| { |
| printk(KERN_INFO"%s: tas5431_amp_reg read failed.\n", __FUNCTION__); |
| |
| *oValue = 0x00; |
| return -EIO; |
| } |
| |
| *oValue = value & 0xFF; |
| |
| return 0; |
| } |
| /****************************************************************************** |
| * Function : tas5431_amp_write |
| ******************************************************************************* |
| * |
| * Description : |
| * |
| * Parameters : char RegAddr |
| * Parameters : unsigned short RegData |
| * |
| * Output Param : None. |
| * |
| * Return value : |
| * |
| * Notes : |
| *******************************************************************************/ |
| static int tas5431_amp_reg_write(struct i2c_client *client, char reg, unsigned short value) |
| { |
| char data[3] = {0}; |
| |
| data[0] = reg & 0xFF; |
| // data[1] = (char)((value >> 8) & 0xFF); |
| data[1] = (char)(value & 0xFF); |
| |
| if (i2c_master_send(client, data, 2) == 2) { |
| #ifdef tas5431_amp_DEBUG |
| printk(KERN_INFO"%s=> reg:0x%02x,value:0x%02x success\n", __FUNCTION__, reg, value); |
| #endif |
| return 0; |
| } else { |
| printk(KERN_INFO"%s=> reg:0x%02x,value:0x%02x error\n", __FUNCTION__, reg, value); |
| return -1; |
| } |
| } |
| |
| static int tas5431_amp_write(char reg, unsigned short value) |
| { |
| int ret; |
| |
| #ifdef tas5431_amp_DEBUG_CLOSE |
| unsigned short read_value; |
| #endif |
| |
| ret = tas5431_amp_reg_write(g_tas5431_amp_client, reg, value); |
| if (ret < 0) |
| { |
| printk(KERN_INFO"%s:L%d: tas5431_amp_write() error.\n", __FUNCTION__, __LINE__); |
| } |
| |
| #ifdef tas5431_amp_DEBUG_CLOSE |
| //for check whether write is OK or not. |
| ret = tas5431_amp_read(reg, &read_value); |
| if (ret < 0) |
| { |
| printk(KERN_INFO"%s:L%d: tas5431_amp_read() error.\n", __FUNCTION__, __LINE__); |
| } |
| |
| printk(KERN_INFO"%s:L%d: reg=0x%02x, value=0x%04x, read_value=0x%04x. Please check it:%s.\n", |
| __FUNCTION__, __LINE__, reg, value, read_value, (value == read_value)?"OK":"FAIL"); |
| #endif |
| return ret; |
| } |
| |
| |
| static void tas5431_amp_test(void) |
| { |
| |
| char data[1] = {0}; |
| int i,value = 0x0; |
| data[0] = 0x10 & 0xFF; |
| |
| printk("===>%s: begin\n", __FUNCTION__); |
| |
| for(i = 0 ;i < 1 ; i++) |
| { |
| tas5431_amp_reg_read(g_tas5431_amp_client, i); |
| } |
| |
| printk("===>%s: end\n", __FUNCTION__); |
| } |
| |
| |
| |
| static int level_value = 0; |
| |
| static ssize_t standy_ctrl_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| |
| { |
| level_value = gpio_get_value(g_standy_gpio); |
| |
| return sprintf(buf, "%d\n", level_value); |
| } |
| |
| |
| |
| static ssize_t standy_ctrl_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int ret; |
| |
| ret = sscanf(buf, "%d", &level_value); |
| //Judgment level value |
| if (level_value < 0 || level_value > 1) { |
| printk(KERN_DEBUG "Please enter the number range 0-1\n" |
| "0: Set the low level \n" |
| "1: Set the high level \n" |
| "After the level is set, the corresponding value can be obtained\n"); |
| return ret ? ret : size; |
| } |
| |
| if (level_value == 0) |
| gpio_direction_output(g_standy_gpio, 0); |
| else if(level_value == 1) |
| gpio_direction_output(g_standy_gpio, 1); |
| |
| return size; |
| |
| } |
| |
| |
| |
| /* |
| read register example: |
| echo 0x1 /tas5431_amp_reg |
| cat tas5431_amp_reg |
| >>>>>>>> |
| read all registers example: |
| echo + > tas5431_amp_reg |
| cat tas5431_amp_reg |
| >>>>>>>> |
| 运行 开机测试通信函数 |
| echo - > tas5431_amp_reg |
| cat tas5431_amp_reg |
| >>>>>>>> |
| write the register example: |
| echo 0x01 0x02 > tas5431_amp_reg |
| cat debug/tas5431_amp_reg |
| |
| */ |
| |
| static int reg_tas5431_amp = 0xffff; |
| struct dentry *tas5431_amp_dump_reg = NULL; |
| |
| static ssize_t tas5431_reg_show(struct device *dev, struct device_attribute *attr, char *_buf) |
| { |
| unsigned short reg_val = 0; |
| unsigned short out_val = 0; |
| int i; |
| int len = 0; |
| char str[100] = {0}; |
| |
| if (reg_tas5431_amp == 0xffff) { |
| len = snprintf(str, sizeof(str) - 1, "%s\n", |
| "tas5431_amp: register dump:"); |
| for (i = 0; i < tas5431_amp_reg_NUM; i++) { |
| |
| |
| reg_val = tas5431_amp_read(i, &out_val); |
| |
| pr_info("%s: [0x%02x]=0x%02x\n", __FUNCTION__, i, out_val); |
| |
| } |
| } |
| else if (reg_tas5431_amp == 0xff00) |
| tas5431_amp_test(); |
| |
| else { |
| |
| reg_val = tas5431_amp_read(reg_tas5431_amp, &out_val); |
| len = snprintf(str, sizeof(str), "reg_tas5431_amp=0x%02x, val=0x%04x\n", |
| reg_tas5431_amp, out_val); |
| printk(KERN_INFO"%s:%s\n", __FUNCTION__, str); |
| |
| } |
| |
| return 0; |
| } |
| |
| |
| |
| static ssize_t tas5431_reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
| { |
| int reg_val; |
| int i = 0; |
| int ret; |
| |
| char messages[20]; |
| memset(messages, '\0', 20); |
| memcpy(messages, buf, count); |
| |
| printk("--->%s/L%d, messages = %s\n", __FUNCTION__, __LINE__, messages); |
| if ('+' == messages[0]) |
| { |
| /* enable to get all the reg value */ |
| reg_tas5431_amp = 0xffff; |
| pr_info("%s: read all reg enabled!\n", __FUNCTION__); |
| } |
| else if ('-' == messages[0]) |
| { |
| /* enable to get all the reg value */ |
| reg_tas5431_amp = 0xff00; |
| pr_info("%s: read all reg enabled!\n", __FUNCTION__); |
| } |
| |
| else |
| { |
| if (messages[1] != 'x') |
| { |
| pr_err("Right format: 0x[addr]\n"); |
| return -EINVAL; |
| } |
| |
| if (strlen(messages) > 5) |
| { |
| while (messages[i] != ' ') |
| i++; |
| messages[i] = '\0'; |
| |
| if (kstrtouint(messages, 16, ®_tas5431_amp) < 0) |
| return -EINVAL; |
| i++; |
| if (kstrtouint(messages + i, 16, ®_val) < 0) |
| |
| /* config the registers */ |
| ret = tas5431_amp_write(reg_tas5431_amp, reg_val & 0xffff); |
| if (ret < 0) { |
| pr_err("write reg error!\n"); |
| return -EINVAL; |
| } |
| |
| printk(KERN_INFO"%s/L%d: addr=0x%02x, val=0x%04x.\n", __FUNCTION__, __LINE__, reg_tas5431_amp, reg_val); |
| |
| } |
| else |
| { |
| /* point out the register address for read. */ |
| if (kstrtouint(messages, 16, ®_tas5431_amp) < 0) |
| return -EINVAL; |
| } |
| } |
| |
| return count; |
| } |
| |
| static DEVICE_ATTR(standy_ctrl, 0644, standy_ctrl_show, standy_ctrl_store); |
| static DEVICE_ATTR(tas5431_reg_op, 0664, tas5431_reg_show, tas5431_reg_store); |
| |
| static const struct attribute *tas5431_amp_attrs[] = { |
| &dev_attr_tas5431_reg_op.attr, |
| &dev_attr_standy_ctrl.attr, |
| NULL, |
| }; |
| |
| static const struct attribute_group tas5431_amp_group = { |
| .attrs = (struct attribute **)tas5431_amp_attrs, |
| }; |
| |
| |
| |
| /* i2c driver */ |
| static int tas5431_amp_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| int ret = 0; |
| struct device_node *np = client->dev.of_node; |
| |
| printk("[===>%s: begin]/L%d.\n", __FUNCTION__, __LINE__); |
| if (NULL == client) { |
| printk("Please check tas5431_amp for client.\n"); |
| return 0; |
| } |
| |
| g_tas5431_amp_client = client; |
| g_tas5431_amp_node = client->dev.of_node; |
| |
| if (NULL == g_tas5431_amp_node) |
| { |
| printk("Please check g_tas5431_amp_node.\n"); |
| return 0; |
| } |
| |
| g_standy_gpio = of_get_named_gpio(np, "standy_gpio", 0); |
| if (unlikely(g_standy_gpio < 0)) { |
| printk("standy_gpio undefined\n"); |
| |
| } |
| else |
| { |
| printk("standy_gpio get success \n"); |
| gpio_request(g_standy_gpio, "standy_gpio"); |
| gpio_direction_output(g_standy_gpio, 1); |
| mdelay(50); |
| gpio_direction_output(g_standy_gpio, 0); |
| mdelay(50); |
| gpio_direction_output(g_standy_gpio, 1); |
| } |
| |
| tas5431_amp_test(); |
| sysfs_create_group(&client->dev.kobj, &tas5431_amp_group); |
| |
| return 0; |
| } |
| |
| static int tas5431_amp_remove(struct i2c_client *client) |
| { |
| #ifdef tas5431_amp_DEBUG |
| printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__); |
| #endif |
| |
| return 0; |
| } |
| |
| void tas5431_amp_shutdown(struct i2c_client *client) |
| { |
| #ifdef tas5431_amp_DEBUG |
| printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__); |
| #endif |
| |
| return; |
| } |
| |
| static struct i2c_driver tas5431_amp_driver = { |
| .driver = { |
| .name = "tas5431-amplifier", |
| .of_match_table = of_match_ptr(tas5431_amp_dt_ids), |
| }, |
| .probe = tas5431_amp_probe, |
| .remove = tas5431_amp_remove, |
| //.shutdown = tas5431_amp_shutdown, |
| .id_table = tas5431_amplifier_id_table, |
| }; |
| |
| static int tas5431_amp_init(void) |
| { |
| return i2c_add_driver(&tas5431_amp_driver); |
| } |
| module_init(tas5431_amp_init); |
| |
| static void tas5431_amp_exit(void) |
| { |
| i2c_del_driver(&tas5431_amp_driver); |
| } |
| module_exit(tas5431_amp_exit); |
| |
| MODULE_DESCRIPTION("Driver for tas5431_amp"); |
| MODULE_AUTHOR("wenchen@asrmicro.com"); |
| MODULE_LICENSE("GPL"); |