公众号
欢迎扫码关注本人微信公众号:公众号上分享更多嵌入式知识和资料,分享个人学习嵌入式的心得体会。欢迎大家一起来玩呀。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/spi/spi.h>
#include <linux/timer.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/platform_data/spi-rockchip.h>
#include <linux/delay.h>
#include <dt-bindings/gpio/gpio.h>
#ifdef CONFIG_OF
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#endif
#define pga2311_spi_speed 5000000
struct pga2311_data_t{
struct spi_device *spi;
int gpio_mute;
int gpio_mute_is_valid;
int gpio_mute_output;
int gpio_zce; //zero crossing enable pin
int gpio_zce_is_valid;
int gpio_zce_output;
uint8_t right_volume;
uint8_t left_volume;
};
static DEFINE_MUTEX(pga2311_global_lock);
static struct pga2311_data_t *p_pga2311_data = NULL;
/*
* This function write wk2xxx of Global register:
*/
static int pga2311_write_global_reg(struct spi_device *spi,uint8_t right_volume,uint8_t left_volume) {
struct spi_message msg;
uint8_t volume[2];
int status;
struct spi_transfer index_xfer = {
.len = 2,
.speed_hz = pga2311_spi_speed,
};
mutex_lock(&pga2311_global_lock);
spi_message_init(&msg);
/* register index */
volume[0] = right_volume;
volume[1] = left_volume;
index_xfer.tx_buf = volume;
spi_message_add_tail(&index_xfer, &msg);
status = spi_sync(spi, &msg);
mutex_unlock(&pga2311_global_lock);
if (status) {
printk(KERN_ERR "%s!!status=%d!---\n", __func__,status);
}
return status;
}
static int pga2311_read_global_reg(struct spi_device *spi,uint8_t *right_volume,uint8_t *left_volume) {
struct spi_message msg;
uint8_t buf_rdat[2];
int status;
struct spi_transfer index_xfer = {
.len = 2,
.speed_hz = pga2311_spi_speed,
};
mutex_lock(&pga2311_global_lock);
status =0;
spi_message_init(&msg);
index_xfer.rx_buf =(void *) buf_rdat;
spi_message_add_tail(&index_xfer, &msg);
status = spi_sync(spi, &msg);
mutex_unlock(&pga2311_global_lock);
if (status) {
printk(KERN_ERR "%s!!status=%d!---\n", __func__,status);
return status;
}
*right_volume = buf_rdat[0];
*left_volume = buf_rdat[1];
return 0;
}
static ssize_t pga2311_mute_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) {
struct pga2311_data_t *pdata = p_pga2311_data;
printk(KERN_ERR "pga2311_mute_store %s \n",buf);
if (buf[0] == '0') {
pdata->gpio_mute_output = 0;
gpio_direction_output(pdata->gpio_mute, pdata->gpio_mute_output);
}else if (buf[0] == '1') {
pdata->gpio_mute_output = 1;
gpio_direction_output(pdata->gpio_mute, pdata->gpio_mute_output);
}
printk(KERN_ERR "gpio_mute_output %d \n",pdata->gpio_mute_output);
return count;
}
static ssize_t pga2311_zce_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) {
struct pga2311_data_t *pdata = p_pga2311_data;
printk(KERN_ERR "pga2311_zce_store %s \n",buf);
if (buf[0] == '0') {
pdata->gpio_zce_output = 0;
gpio_direction_output(pdata->gpio_zce, pdata->gpio_zce_output);
}else if (buf[0] == '1') {
pdata->gpio_zce_output = 1;
gpio_direction_output(pdata->gpio_zce, pdata->gpio_zce_output);
}
printk(KERN_ERR "gpio_zce_output %d \n",pdata->gpio_zce_output);
return count;
}
static ssize_t pga2311_set_right_volume_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) {
struct pga2311_data_t *pdata = p_pga2311_data;
int num;
printk(KERN_ERR "pga2311_set_right_volume_store %s \n",buf);
num = simple_strtol(buf, NULL, 0);
if ( !(num >= 0 && num <= 255))
return count;
pdata->right_volume = num;
pga2311_write_global_reg(pdata->spi,pdata->right_volume,pdata->left_volume);
return count;
}
static ssize_t pga2311_set_left_volume_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) {
struct pga2311_data_t *pdata = p_pga2311_data;
int num;
printk(KERN_ERR "pga2311_set_left_volume_store %s \n",buf);
num = simple_strtol(buf, NULL, 0);
if ( !(num >= 0 && num <= 255))
return count;
pdata->left_volume = num;
pga2311_write_global_reg(pdata->spi,pdata->right_volume,pdata->left_volume);
return count;
}
static ssize_t pga2311_set_right_volume_show(struct device *dev, struct device_attribute *attr,
char *buf) {
uint8_t right_volume = 0;
uint8_t left_volume = 0;
struct pga2311_data_t *pdata = p_pga2311_data;
pga2311_read_global_reg(pdata->spi,&right_volume,&left_volume);
pga2311_write_global_reg(pdata->spi,pdata->right_volume,pdata->left_volume);
return snprintf(buf, PAGE_SIZE, "%d\n",
right_volume);
}
static ssize_t pga2311_set_left_volume_show(struct device *dev, struct device_attribute *attr,
char *buf) {
uint8_t right_volume = 0;
uint8_t left_volume = 0;
struct pga2311_data_t *pdata = p_pga2311_data;
pga2311_read_global_reg(pdata->spi,&right_volume,&left_volume);
pga2311_write_global_reg(pdata->spi,pdata->right_volume,pdata->left_volume);
return snprintf(buf, PAGE_SIZE, "%d\n",
left_volume);
}
static struct device_attribute pga2311_attr[] = {
__ATTR(set_mute_gpio_direction, 0664, NULL, pga2311_mute_store),
__ATTR(set_zce_gpio_direction, 0664, NULL, pga2311_zce_store),
__ATTR(right_volume, 0664, pga2311_set_right_volume_show, pga2311_set_right_volume_store),
__ATTR(left_volume, 0664, pga2311_set_left_volume_show, pga2311_set_left_volume_store),
};
static void pga2311_init_sysfs(struct device *dev)
{
int i, ret;
for (i = 0; i < ARRAY_SIZE(pga2311_attr); i++) {
ret = sysfs_create_file(&dev->kobj,
&pga2311_attr[i].attr);
if (ret)
dev_err(dev, "create charger node(%s) error\n",
pga2311_attr[i].attr.name);
}
}
static int pga2311_parse_dt(struct pga2311_data_t *pdata)
{
int gpio;
enum of_gpio_flags flags;
struct device *dev = &pdata->spi->dev;
int ret;
struct device_node *node = dev->of_node;
if (!node)
return -ENODEV;
gpio = of_get_named_gpio_flags(node, "gpio_mute", 0, &flags);
if (gpio_is_valid(gpio))
{
pdata->gpio_mute = gpio;
pdata->gpio_mute_output = (flags == GPIO_ACTIVE_HIGH)? 1:0;
printk(KERN_ERR"gpio_mute: %d\n", pdata->gpio_mute);
ret = devm_gpio_request(dev,gpio, "gpio_mute");
if (ret) {
printk("Failed to get gpio_mute gpio.\n");
}
pdata->gpio_mute_is_valid = 1;
}
gpio = of_get_named_gpio_flags(node, "gpio_zero_crossing_enable", 0,&flags);
if (gpio_is_valid(gpio))
{
pdata->gpio_zce = gpio;
pdata->gpio_zce_output = (flags == GPIO_ACTIVE_HIGH)? 1:0;
printk(KERN_ERR"gpio_zero_crossing_enable: %d\n", pdata->gpio_zce);
ret = devm_gpio_request(dev,gpio, "gpio_zero_crossing_enable");
if (ret) {
printk("Failed to get gpio_zero_crossing_enable gpio.\n");
}
pdata->gpio_zce_is_valid = 1;
}
if (pdata->gpio_mute_is_valid == 1) { //set mute to default
gpio_direction_output(pdata->gpio_mute, pdata->gpio_mute_output);
}
if (pdata->gpio_zce_is_valid == 1) { //set zero crossing enable to default
gpio_direction_output(pdata->gpio_zce, pdata->gpio_zce_output);
}
return 0;
}
static int pga2311_probe(struct spi_device *spi)
{
int ret = 0;
if (!spi)
return -ENOMEM;
dev_err(&spi->dev, "pga2311_probe: setup mode %d, %s%s%s%s%u bits/w, %u Hz max\n",
(int) (spi->mode & (SPI_CPOL | SPI_CPHA)),
(spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",
(spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",
(spi->mode & SPI_3WIRE) ? "3wire, " : "",
(spi->mode & SPI_LOOP) ? "loopback, " : "",
spi->bits_per_word, spi->max_speed_hz);
ret = spi_setup(spi);
if (ret < 0) {
dev_err(&spi->dev,"ERR: fail to setup spi\n");
return -1;
}
p_pga2311_data = kmalloc(sizeof(struct pga2311_data_t), GFP_KERNEL);
if (p_pga2311_data == NULL) {
printk(KERN_ERR"kmalloc pga2311_data_t err \n");
return -ENOMEM;
}
memset(p_pga2311_data, 0, sizeof(struct pga2311_data_t));
p_pga2311_data->spi = spi;
if (pga2311_parse_dt(p_pga2311_data)) {
printk(KERN_ERR"pga2311_parse_dt err \n");
goto err;
}
pga2311_init_sysfs(&p_pga2311_data->spi->dev);
return 0;
err:
kfree(p_pga2311_data);
return -1;
}
static const struct of_device_id pga2311_dt_match[] = {
{ .compatible = "ti,pga2311", },
{ },
};
MODULE_DEVICE_TABLE(of, pga2311_dt_match);
static struct spi_driver pga2311_driver = {
.driver = {
.name = "pga2311",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(pga2311_dt_match),
},
.probe = pga2311_probe,
};
static int __init pga2311_init(void)
{
int retval;
retval = spi_register_driver(&pga2311_driver);
printk(KERN_ALERT "%s,register spi return v = :%d\n",__func__,retval);
return retval;
}
static void __exit pga2311_exit(void)
{
printk(KERN_ALERT "%s!!--in--\n", __func__);
return spi_unregister_driver(&pga2311_driver);
}
module_init(pga2311_init);
module_exit(pga2311_exit);
MODULE_AUTHOR("System Application Group");
MODULE_DESCRIPTION("pga2311 driver");
MODULE_LICENSE("GPL");
···