383 lines
12 KiB
C
Executable File
383 lines
12 KiB
C
Executable File
#include <asm/io.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sunxi-gpio.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/regulator/consumer.h>
|
|
// codec init param
|
|
#include "cx20810_config.h"
|
|
|
|
#define I2C_CX20810_DRIVER_NAME "i2c_cx20810"
|
|
#define MAX_CX20810_NUM (3)
|
|
void netease_enable_clk(void);
|
|
static int gpio_adc_rst = -1;
|
|
|
|
// g_client_cx20810[0] is on adapter 0 and its address is 0x35
|
|
// g_client_cx20810[1] is on adapter 1 and its address is 0x35
|
|
// g_client_cx20810[2] is on adapter 1 and its address is 0x3B
|
|
static struct i2c_client *g_client_cx20810[MAX_CX20810_NUM];
|
|
static const unsigned short i2c_cx20810_addr[] = {0x35, 0x3B, I2C_CLIENT_END};
|
|
static const struct i2c_device_id i2c_driver_cx20810_id[] = {
|
|
{I2C_CX20810_DRIVER_NAME, 0}, {}};
|
|
|
|
// function declaration
|
|
static int cx20810_hw_init(void);
|
|
static int i2c_driver_cx20810_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id);
|
|
static int i2c_driver_cx20810_remove(struct i2c_client *client);
|
|
static int i2c_driver_cx20810_detect(struct i2c_client *client,
|
|
struct i2c_board_info *info);
|
|
static int i2c_master_send_array_to_cx20810(const struct i2c_client *client,
|
|
const char *buf, int length);
|
|
static void cx20810_init(int index, int mode);
|
|
int cx20810_set_mode(int mode, int index);
|
|
|
|
// set cx20810 work mode
|
|
int cx20810_set_mode(int mode, int index) {
|
|
printk("Timothy:cx20810.c->cx20810_set_mode(), mode = %d, index = %d\n",
|
|
mode, index);
|
|
int i;
|
|
int ret;
|
|
char *param;
|
|
int length;
|
|
|
|
switch (mode) {
|
|
case CX20810_NORMAL_MODE:
|
|
param = codec_config_param_normal_mode;
|
|
length = sizeof(codec_config_param_normal_mode);
|
|
break;
|
|
|
|
case CX20810_NORMAL_MODE_SIMPLE:
|
|
param = codec_config_param_normal_mode_simple;
|
|
length = sizeof(codec_config_param_normal_mode_simple);
|
|
break;
|
|
|
|
case CX20810_48K_16BIT_MODE:
|
|
param = codec_config_param_48k_16bit_mode;
|
|
length = sizeof(codec_config_param_48k_16bit_mode);
|
|
break;
|
|
|
|
case CX20810_96K_16BIT_MODE:
|
|
param = codec_config_param_96k_16bit_mode;
|
|
length = sizeof(codec_config_param_96k_16bit_mode);
|
|
break;
|
|
|
|
case CX20810_NIRMAL_MODE_CODEC3:
|
|
param = codec3_config_param_normal_mode;
|
|
length = sizeof(codec3_config_param_normal_mode);
|
|
break;
|
|
|
|
case CX20810_NIRMAL_MODE_CODEC3_SIMPLE:
|
|
param = codec3_config_param_normal_mode_simple;
|
|
length = sizeof(codec3_config_param_normal_mode_simple);
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
break;
|
|
}
|
|
|
|
// if client is null, return
|
|
if (g_client_cx20810[index] == NULL) {
|
|
printk("Timothy:cx20810(%d) is not detected yet\n", index);
|
|
return -1;
|
|
}
|
|
|
|
ret = i2c_master_send_array_to_cx20810(g_client_cx20810[index], param,
|
|
length);
|
|
if (ret != 0) {
|
|
printk("Timothy:cx82011[%x] init error!\n",
|
|
g_client_cx20810[index]->addr);
|
|
return -1;
|
|
} else {
|
|
printk("Timothy:cx20810[%x] init ok\n", g_client_cx20810[index]->addr);
|
|
return 0;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(cx20810_set_mode);
|
|
|
|
int cx20810_write_reg(int index, int addr, int regval) {
|
|
char writedata[2] = {0};
|
|
if (index > 1) {
|
|
return -1;
|
|
}
|
|
writedata[0] = (char)addr;
|
|
writedata[1] = (char)regval;
|
|
|
|
return i2c_master_send(g_client_cx20810[index], writedata, 2);
|
|
}
|
|
EXPORT_SYMBOL(cx20810_write_reg);
|
|
|
|
int cx20810_read_reg(int index, int cmd, int *regval) {
|
|
int error = 0;
|
|
char data = 0;
|
|
if (index > 1) {
|
|
return -1;
|
|
}
|
|
|
|
error = i2c_master_send(g_client_cx20810[index], (const char *)&cmd, 1);
|
|
// write reg addr
|
|
if (1 != i2c_master_send(g_client_cx20810[index], (const char *)&cmd, 1)) {
|
|
printk(KERN_ERR " cx20810_read_reg fail1! \n");
|
|
return -1;
|
|
}
|
|
// wait
|
|
msleep(10);
|
|
// read
|
|
if (error >= 0) {
|
|
error = i2c_master_recv(g_client_cx20810[index], &data, 1);
|
|
if (error < 0) {
|
|
printk(KERN_ERR " cx20810_read_reg fail2! \n");
|
|
}
|
|
} else {
|
|
printk(KERN_ERR " cx20810_read_reg fail1! \n");
|
|
}
|
|
|
|
*regval = (int)data;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(cx20810_read_reg);
|
|
|
|
// send parameters to cx20810 as master
|
|
static int i2c_master_send_array_to_cx20810(const struct i2c_client *client,
|
|
const char *buf, int length) {
|
|
printk("Timothy:cx20810.c->i2c_master_send_array_to_cx20810()\n");
|
|
int i;
|
|
int nwrite;
|
|
for (i = 0; i < (length / 2); i++) {
|
|
nwrite = i2c_master_send(client, buf + i * 2, 2);
|
|
if (nwrite != 2) {
|
|
printk("Timothy:send to cx20810 error\n");
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// initial cx20810
|
|
static void cx20810_init(int index, int mode) {
|
|
printk("Timothy:cx20810.c->cx20810_init()\n");
|
|
if (cx20810_set_mode(mode, index) == 0) {
|
|
printk("Timothy:cx20810 init success\n");
|
|
} else {
|
|
printk("Timothy:cx20810 init fail\n");
|
|
}
|
|
}
|
|
|
|
static int i2c_driver_cx20810_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id) {
|
|
if (client->adapter->nr == 0 && client->addr == i2c_cx20810_addr[0]) {
|
|
g_client_cx20810[1] = client;
|
|
printk("Timothy:cx20810 (0x%x) init ok\n", client->addr);
|
|
} else if (client->adapter->nr == 0 &&
|
|
client->addr == i2c_cx20810_addr[1]) {
|
|
g_client_cx20810[0] = client;
|
|
printk("Timothy:cx20810 (0x%x) init ok\n", client->addr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int i2c_driver_cx20810_remove(struct i2c_client *client) {
|
|
printk("Timothy:cx20810.c->i2c_driver_cx20810_remove()\n");
|
|
return 0;
|
|
}
|
|
MODULE_DEVICE_TABLE(i2c, i2c_driver_cx20810_id);
|
|
|
|
static int i2c_driver_cx20810_detect(struct i2c_client *client,
|
|
struct i2c_board_info *info) {
|
|
printk("Timothy:cx20810.c->i2c_driver_cx20810_detect()...\n");
|
|
struct i2c_adapter *p_adapter;
|
|
const char *type_name = I2C_CX20810_DRIVER_NAME;
|
|
p_adapter = client->adapter;
|
|
printk("Timothy:adapter->nr = %d\n", p_adapter->nr);
|
|
if (0 == p_adapter->nr) {
|
|
if (info->addr == i2c_cx20810_addr[0]) {
|
|
printk("Timothy:detect cx20810 (%x) on i2c adapter (%d)\n",
|
|
info->addr, p_adapter->nr);
|
|
strlcpy(info->type, type_name, I2C_NAME_SIZE);
|
|
return 0;
|
|
} else if (info->addr == i2c_cx20810_addr[1]) {
|
|
printk("Timothy:detect cx20810 (%x) on i2c adapter (%d)\n",
|
|
info->addr, p_adapter->nr);
|
|
strlcpy(info->type, type_name, I2C_NAME_SIZE);
|
|
return 0;
|
|
}
|
|
}
|
|
return ENODEV;
|
|
}
|
|
|
|
static struct i2c_driver i2c_driver_cx20810 = {
|
|
.class = I2C_CLASS_HWMON,
|
|
.probe = i2c_driver_cx20810_probe,
|
|
.remove = i2c_driver_cx20810_remove,
|
|
.id_table = i2c_driver_cx20810_id,
|
|
.driver =
|
|
{
|
|
.name = I2C_CX20810_DRIVER_NAME,
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.detect = i2c_driver_cx20810_detect,
|
|
.address_list = i2c_cx20810_addr};
|
|
|
|
static int __init i2c_driver_cx20810_init(void) {
|
|
printk("Timothy:cx20810.c->i2c_driver_cx20810_init()\n");
|
|
msleep(300);
|
|
// cx20810_hw_init();
|
|
return i2c_add_driver(&i2c_driver_cx20810);
|
|
}
|
|
|
|
static void __exit i2c_driver_cx20810_exit(void) {
|
|
printk("Timothy:cx20810.c->i2c_driver_cx20810_exit()\n");
|
|
i2c_del_driver(&i2c_driver_cx20810);
|
|
}
|
|
|
|
static int __init cpld_r311_probe(struct platform_device *pdev) {
|
|
printk("Begin to set luyao asked gpios \n");
|
|
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct device *dev = &pdev->dev;
|
|
struct gpio_config cfg;
|
|
int gpionum;
|
|
const char *io_name = '\0';
|
|
struct regulator *reg = NULL;
|
|
|
|
if (of_property_read_string(np, "netease_io_regulator", &io_name)) {
|
|
printk("Wzj: can not get netease_io_regulator name!!\n");
|
|
} else {
|
|
printk("Wzj: get netease_io_regulator name:%s\n", io_name);
|
|
reg = regulator_get(dev, io_name);
|
|
if (reg) {
|
|
regulator_enable(reg);
|
|
printk("Get regulator ok, vol:%d\n", regulator_get_voltage(reg));
|
|
regulator_put(reg);
|
|
} else {
|
|
printk("Can not get regulator!!\n");
|
|
}
|
|
|
|
}
|
|
|
|
gpionum = -1;
|
|
gpionum = of_get_named_gpio_flags(np, "4v5_ldo_en", 0,
|
|
(enum of_gpio_flags *)&cfg);
|
|
if (gpio_is_valid(gpionum)) {
|
|
gpio_request(gpionum, "4v5_ldo_en");
|
|
gpio_direction_output(gpionum, 1);
|
|
printk("Set 4v5_ldo_en(%d) success\n", gpionum);
|
|
} else {
|
|
printk("Set 4v5_ldo_en fail\n");
|
|
}
|
|
|
|
gpionum = -1;
|
|
gpionum =
|
|
of_get_named_gpio_flags(np, "3v_ldo_en", 0, (enum of_gpio_flags *)&cfg);
|
|
if (gpio_is_valid(gpionum)) {
|
|
gpio_request(gpionum, "3v_ldo_en");
|
|
gpio_direction_output(gpionum, 1);
|
|
gpio_free(gpionum);
|
|
printk("Set 3v_ldo_en(%d) success\n", gpionum);
|
|
} else {
|
|
printk("Set 3v_ldo_en fail\n");
|
|
}
|
|
|
|
gpionum = -1;
|
|
gpionum = of_get_named_gpio_flags(np, "gp_adc_rst", 0,
|
|
(enum of_gpio_flags *)&cfg);
|
|
gpio_adc_rst = gpionum;
|
|
if (!gpio_is_valid(gpionum)) {
|
|
printk("get gp_adc_rst failed\n");
|
|
return -EINVAL;
|
|
} else {
|
|
printk("Get gp_adc_rst success,gpio:%d\n", gpionum);
|
|
}
|
|
|
|
devm_gpio_request(dev, gpionum, "gp_adc_rst");
|
|
gpio_direction_output(gpionum, 1);
|
|
printk("Set gp_adc_rst(%d) to 1!\n", gpionum);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void netease_cpld_reset(void) {
|
|
printk("Begin to reset cpld!\n");
|
|
if (gpio_is_valid(gpio_adc_rst)) {
|
|
gpio_set_value(gpio_adc_rst, 0);
|
|
msleep(100);
|
|
printk("Begin to start cpld!\n");
|
|
gpio_set_value(gpio_adc_rst, 1);
|
|
msleep(800);
|
|
printk("Finish reseting cpld!\n");
|
|
} else {
|
|
printk("Adc rst gpio is not init!\n");
|
|
}
|
|
}
|
|
|
|
static const struct of_device_id cpld_r311_ids[] = {
|
|
{.compatible = "allwinner,cpld-r311-pv1"}, {/* Sentinel */}};
|
|
|
|
static struct platform_driver cpld_r311_driver = {
|
|
.driver =
|
|
{
|
|
.owner = THIS_MODULE,
|
|
.name = "cpld",
|
|
.of_match_table = cpld_r311_ids,
|
|
},
|
|
};
|
|
|
|
static int __init cpld_r311_init(void) {
|
|
int ret = 0;
|
|
#if 0
|
|
type = script_get_item("cpld", "gp_ldo_4_5", &item);
|
|
if (SCIRPT_ITEM_VALUE_TYPE_PIO == type) {
|
|
printk("luyao:set gp_ldo_4_5\n");
|
|
printk("Gpio:%d\n", item.gpio.gpio);
|
|
|
|
gpio_request(item.gpio.gpio, NULL);
|
|
|
|
gpio_direction_output(item.gpio.gpio, 1);
|
|
gpio_set_value(item.gpio.gpio, 1);
|
|
|
|
gpio_free(item.gpio.gpio);
|
|
}
|
|
|
|
type = script_get_item("cpld", "gp_ldo_3", &item);
|
|
if (SCIRPT_ITEM_VALUE_TYPE_PIO == type) {
|
|
printk("luyao:set gp_ldo_3\n");
|
|
printk("Gpio:%d\n", item.gpio.gpio);
|
|
gpio_request(item.gpio.gpio, NULL);
|
|
gpio_direction_output(item.gpio.gpio, 1);
|
|
gpio_set_value(item.gpio.gpio, 1);
|
|
gpio_free(item.gpio.gpio);
|
|
}
|
|
#endif
|
|
printk("Begin to init cpld r311 driver!!\n");
|
|
ret = platform_driver_probe(&cpld_r311_driver, cpld_r311_probe);
|
|
printk("platform_driver_probe:%d!\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __exit cpld_r311_deinit(void) {}
|
|
|
|
fs_initcall_sync(cpld_r311_init);
|
|
module_exit(cpld_r311_deinit);
|
|
|
|
late_initcall_sync(i2c_driver_cx20810_init);
|
|
module_exit(i2c_driver_cx20810_exit);
|
|
|
|
MODULE_AUTHOR("Timothy");
|
|
MODULE_DESCRIPTION("I2C device cx20810 loader");
|
|
MODULE_LICENSE("GPL");
|