blob: c4bdb8ccff8ee9ade7daa3230312ab3fa6dc13c6 [file] [log] [blame]
/*
* 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, &reg_tas5431_amp) < 0)
return -EINVAL;
i++;
if (kstrtouint(messages + i, 16, &reg_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, &reg_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");