Squashed backup commit (no shallow-clone history issues) of xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream commit 9eece662e) plus all fixes applied to get everpal_defconfig building clean against a modern (GCC 15.2.0) toolchain -- see the previous commit message on the lineage-23.2 branch (also in this repo history is not available here since this is a squashed snapshot; full incremental history lives in the local DL380 checkout and the upstream GitHub remote). Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
2513 lines
65 KiB
C
2513 lines
65 KiB
C
/*
|
|
* Copyright (C) 2018, SI-IN, Yun Shi (yun.shi@si-in.com).
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
|
|
//#define DEBUG
|
|
#define LOG_FLAG "sia81xx_driver"
|
|
|
|
|
|
#include <linux/i2c.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/io.h>
|
|
#include <linux/init.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/gameport.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/mutex.h>
|
|
//#include <linux/wakelock.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/pinctrl/consumer.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/delay.h>
|
|
#include <sound/pcm.h>
|
|
#include <sound/pcm_params.h>
|
|
#include <sound/soc.h>
|
|
#include <sound/soc-dai.h>
|
|
#include <sound/soc-dapm.h>
|
|
#include <linux/time.h>
|
|
#include <linux/fs.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/cpufreq.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/module.h>
|
|
#include <linux/version.h>
|
|
|
|
|
|
#include "sia81xx_common.h"
|
|
#include "sia81xx_regmap.h"
|
|
#include "sia81xx_timer_task.h"
|
|
#include "sia81xx_tuning_if.h"
|
|
#include "sia81xx_set_vdd.h"
|
|
|
|
#ifdef SIA81XX_TUNING
|
|
#include "sia81xx_socket.h"
|
|
#endif
|
|
|
|
|
|
#define SIA81XX_NAME "sia81xx"
|
|
#define SIA81XX_I2C_NAME SIA81XX_NAME
|
|
|
|
#define DISTINGUISH_CHIP_TYPE
|
|
//#define ALGO_SWITCH_EN
|
|
|
|
#define SIA81XX_CMD_POWER_ON (1)
|
|
#define SIA81XX_CMD_POWER_OFF (2)
|
|
#define SIA81XX_CMD_GET_MODE (3)
|
|
#define SIA81XX_CMD_SET_MODE (4)
|
|
#define SIA81XX_CMD_GET_REG (5)
|
|
#define SIA81XX_CMD_SET_REG (6)
|
|
|
|
#define SIA81XX_CMD_WRITE_BYTE (100)
|
|
#define SIA81XX_CMD_SET_OWI_DELAY (101)
|
|
#define SIA81XX_CMD_CLR_ERROR (200)
|
|
#define SIA81XX_CMD_SOCKET_OPEN (400)
|
|
#define SIA81XX_CMD_SOCKET_CLOSE (401)
|
|
#define SIA81XX_CMD_VDD_SET_OPEN (402)
|
|
#define SIA81XX_CMD_VDD_SET_CLOSE (403)
|
|
#define SIA81XX_CMD_TIMER_TASK_OPEN (404)
|
|
#define SIA81XX_CMD_TIMER_TASK_CLOSE (405)
|
|
#ifdef ALGO_SWITCH_EN
|
|
#define SIA81XX_CMD_ALGORITHM_OPEN (406)
|
|
#define SIA81XX_CMD_ALGORITHM_CLOSE (407)
|
|
#define SIA81XX_CMD_ALGORITHM_STATUS (408)
|
|
#endif
|
|
|
|
#define SIA81XX_CMD_GET_RST_PIN (600)
|
|
#define SIA81XX_CMD_SET_RST_PIN (601)
|
|
|
|
#define SIA81XX_CMD_TEST (777)
|
|
|
|
|
|
#define SIA81XX_ENABLE_LEVEL (1)
|
|
#define SIA81XX_DISABLE_LEVEL (0)
|
|
|
|
/* 10us > pulse width > 0.75us */
|
|
#define MIN_OWI_PULSE_GAP_TIME_US (1)
|
|
#define MAX_OWI_PULSE_GAP_TIME_US (160)
|
|
#define MAX_OWI_RETRY_TIMES (10)
|
|
#define MIN_OWI_MODE (1)
|
|
#define MAX_OWI_MODE (16)
|
|
#define DEFAULT_OWI_MODE (6)
|
|
/* OWI_POLARITY 0 : pulse level == high, 1 : pulse level == low */
|
|
#define OWI_POLARITY SIA81XX_DISABLE_LEVEL
|
|
|
|
|
|
//#define OWI_SUPPORT_WRITE_DATA
|
|
#ifdef OWI_SUPPORT_WRITE_DATA
|
|
#define OWI_DATA_BIG_END
|
|
#endif
|
|
|
|
/* error list */
|
|
#define EPTOUT (100) /* pulse width time out */
|
|
#define EPOLAR (101) /* pulse electrical level opposite with the polarity */
|
|
|
|
struct sia81xx_err {
|
|
unsigned long owi_set_mode_cnt;
|
|
unsigned long owi_set_mode_err_cnt;
|
|
unsigned long owi_write_err_cnt;
|
|
unsigned long owi_polarity_err_cnt;
|
|
unsigned long owi_max_retry_cnt;
|
|
unsigned long owi_max_gap;
|
|
unsigned long owi_max_deviation;
|
|
unsigned long owi_write_data_err_cnt;
|
|
unsigned long owi_write_data_cnt;
|
|
};
|
|
|
|
typedef struct sia81xx_dev_s {
|
|
char name[32];
|
|
unsigned int chip_type;
|
|
struct platform_device *pdev;
|
|
struct i2c_client *client;
|
|
|
|
int disable_pin;
|
|
int rst_pin;
|
|
int owi_pin;
|
|
|
|
unsigned int owi_delay_us;
|
|
unsigned int owi_cur_mode;
|
|
unsigned int owi_polarity;
|
|
|
|
spinlock_t rst_lock;
|
|
spinlock_t owi_lock;
|
|
|
|
struct regmap *regmap;
|
|
unsigned int scene;
|
|
|
|
uint32_t channel_num;
|
|
unsigned int en_xfilter;
|
|
unsigned int en_dyn_ud_vdd;
|
|
unsigned int en_dyn_ud_pvdd;
|
|
unsigned int dyn_ud_vdd_port;
|
|
uint32_t timer_task_hdl;
|
|
uint32_t timer_task_hdl_backup;
|
|
|
|
struct list_head list;
|
|
|
|
struct sia81xx_err err_info;
|
|
}sia81xx_dev_t;
|
|
|
|
struct sia81xx_chip_compat {
|
|
const uint32_t sub_type;
|
|
struct {
|
|
const uint32_t *chips;
|
|
const uint32_t num;
|
|
};
|
|
};
|
|
|
|
static ssize_t sia81xx_cmd_show(struct device* cd,
|
|
struct device_attribute *attr, char* buf);
|
|
static ssize_t sia81xx_cmd_store(struct device* cd,
|
|
struct device_attribute *attr,const char* buf, size_t len);
|
|
|
|
#ifdef DISTINGUISH_CHIP_TYPE
|
|
static ssize_t sia81xx_device_show(struct device* cd,
|
|
struct device_attribute *attr, char* buf);
|
|
static ssize_t sia81xx_device_store(struct device* cd,
|
|
struct device_attribute *attr,const char* buf, size_t len);
|
|
#endif
|
|
|
|
static DEVICE_ATTR_RW(sia81xx_cmd);
|
|
#ifdef DISTINGUISH_CHIP_TYPE
|
|
static DEVICE_ATTR_RW(sia81xx_device);
|
|
#endif
|
|
|
|
static DEFINE_MUTEX(sia81xx_list_mutex);
|
|
static LIST_HEAD(sia81xx_list);
|
|
|
|
|
|
static const char *support_chip_type_name_table[] = {
|
|
[CHIP_TYPE_SIA8101] = "sia8101",
|
|
[CHIP_TYPE_SIA8108] = "sia8108",
|
|
[CHIP_TYPE_SIA8109] = "sia8109",
|
|
[CHIP_TYPE_SIA8152] = "sia8152",
|
|
[CHIP_TYPE_SIA8152S] = "sia8152s",
|
|
[CHIP_TYPE_SIA8100] = "sia8100",
|
|
[CHIP_TYPE_SIA8159] = "sia8159",
|
|
[CHIP_TYPE_SIA81X9] = "sia81x9",
|
|
[CHIP_TYPE_SIA8152X] = "sia8152x"
|
|
};
|
|
|
|
static sia81xx_dev_t *g_default_sia_dev = NULL;
|
|
|
|
static int sia81xx_resume(
|
|
struct sia81xx_dev_s *sia81xx);
|
|
static int sia81xx_suspend(
|
|
struct sia81xx_dev_s *sia81xx);
|
|
/********************************************************************
|
|
* sia81xx misc option
|
|
********************************************************************/
|
|
static void clear_sia81xx_err_info(
|
|
sia81xx_dev_t *sia81xx)
|
|
{
|
|
if(NULL == sia81xx)
|
|
return ;
|
|
|
|
memset(&sia81xx->err_info, 0, sizeof(sia81xx->err_info));
|
|
}
|
|
/********************************************************************
|
|
* end - sia81xx misc option
|
|
********************************************************************/
|
|
|
|
/********************************************************************
|
|
* sia81xx GPIO option
|
|
********************************************************************/
|
|
static __inline void gpio_flipping(
|
|
int pin,
|
|
s64 *intervel_ns)
|
|
{
|
|
static struct timespec64 cur_time, last_time, temp_time;
|
|
|
|
gpio_set_value(pin, !gpio_get_value(pin));
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0))
|
|
ktime_get_real_ts64(&cur_time);
|
|
#else
|
|
getnstimeofday64(&cur_time);
|
|
#endif
|
|
temp_time = timespec64_sub(cur_time, last_time);
|
|
if(NULL != intervel_ns)
|
|
*intervel_ns = timespec64_to_ns(&temp_time);
|
|
last_time = cur_time;
|
|
}
|
|
|
|
static __inline int gpio_produce_one_pulse(
|
|
int pin,
|
|
int polarity,
|
|
unsigned int width_us,
|
|
s64 *real_duty_ns,
|
|
s64 *real_idle_ns)
|
|
{
|
|
if(polarity != gpio_get_value(pin)) {
|
|
pr_err("[ err][%s] %s: EPOLAR \r\n", LOG_FLAG, __func__);
|
|
return -EPOLAR;
|
|
}
|
|
|
|
gpio_flipping(pin, real_idle_ns);
|
|
udelay(width_us);
|
|
gpio_flipping(pin, real_duty_ns);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static __inline int __gpio_produce_one_pulse_cycle(
|
|
int pin,
|
|
int polarity,
|
|
unsigned int duty_time_us,
|
|
unsigned int idle_time_us,
|
|
s64 *real_duty_ns,
|
|
s64 *real_idle_ns)
|
|
{
|
|
int ret = gpio_produce_one_pulse(pin, polarity,
|
|
duty_time_us, real_duty_ns, real_idle_ns);
|
|
if(0 == ret) /* if pulse produce suceess */
|
|
udelay(idle_time_us);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static __inline int gpio_produce_one_pulse_cycle(
|
|
int pin,
|
|
int polarity,
|
|
unsigned int delay_us,
|
|
s64 *real_duty_ns,
|
|
s64 *real_idle_ns)
|
|
{
|
|
return __gpio_produce_one_pulse_cycle(pin, polarity,
|
|
delay_us, delay_us, real_duty_ns, real_idle_ns);
|
|
}
|
|
|
|
static __inline int gpio_produce_pulse_cycles(
|
|
int pin,
|
|
int polarity,
|
|
unsigned int delay_us,
|
|
unsigned int cycles,
|
|
s64 *max_real_delay_ns) {
|
|
|
|
s64 real_duty_ns = 0, real_idle_ns = 0, max_pulse_time = 0;
|
|
bool is_fst_pulse = true;
|
|
int ret = 0;
|
|
|
|
if(0 == pin) {
|
|
pr_err("[ err][%s] %s: 0 == pin \r\n", LOG_FLAG, __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
while(cycles) {
|
|
cycles --;
|
|
|
|
ret = gpio_produce_one_pulse_cycle(pin, polarity,
|
|
delay_us, &real_duty_ns, &real_idle_ns);
|
|
if(0 != ret)
|
|
break;
|
|
|
|
/* monitor pulse with overtime */
|
|
if(false == is_fst_pulse) {
|
|
max_pulse_time =
|
|
max_pulse_time > real_idle_ns ? max_pulse_time : real_idle_ns;
|
|
}
|
|
max_pulse_time =
|
|
max_pulse_time > real_duty_ns ? max_pulse_time : real_duty_ns;
|
|
is_fst_pulse = false;
|
|
|
|
}
|
|
|
|
if(NULL != max_real_delay_ns)
|
|
*max_real_delay_ns = max_pulse_time;
|
|
|
|
return ret;
|
|
}
|
|
/********************************************************************
|
|
* end - sia81xx GPIO option
|
|
********************************************************************/
|
|
|
|
|
|
/********************************************************************
|
|
* sia81xx owi option
|
|
********************************************************************/
|
|
#ifdef OWI_SUPPORT_WRITE_DATA
|
|
static void sia81xx_owi_calc_bit_duty_ns(
|
|
s64 duty_ns,
|
|
s64 idle_ns,
|
|
s64 *duty_time_ns,
|
|
s64 *idle_time_ns,
|
|
const char bit)
|
|
{
|
|
if(0 == !!(bit & (char)0x01)) {
|
|
*duty_time_ns = idle_ns;
|
|
*idle_time_ns = duty_ns;
|
|
} else {
|
|
*duty_time_ns = duty_ns;
|
|
*idle_time_ns = idle_ns;
|
|
}
|
|
}
|
|
|
|
static void sia81xx_owi_calc_bit_duty_us(
|
|
u32 duty_us,
|
|
u32 idle_us,
|
|
u32 *duty_time_us,
|
|
u32 *idle_time_us,
|
|
const char bit)
|
|
{
|
|
s64 duty_time_ns, idle_time_ns;
|
|
|
|
sia81xx_owi_calc_bit_duty_ns((s64)duty_us * NSEC_PER_USEC,
|
|
(s64)idle_us * NSEC_PER_USEC, &duty_time_ns, &idle_time_ns, bit);
|
|
|
|
*duty_time_us = duty_time_ns / NSEC_PER_USEC;
|
|
*idle_time_us = idle_time_ns / NSEC_PER_USEC;
|
|
}
|
|
|
|
static int sia81xx_owi_write_one_bit(
|
|
struct sia81xx_dev_s *sia81xx,
|
|
const char bit,
|
|
s64 *real_duty_ns,
|
|
s64 *real_idle_ns)
|
|
{
|
|
int ret = 0;
|
|
unsigned int duty_time_us, idle_time_us;
|
|
|
|
sia81xx_owi_calc_bit_duty_us(sia81xx->owi_delay_us,
|
|
MIN_OWI_PULSE_GAP_TIME_US, &duty_time_us, &idle_time_us, bit);
|
|
|
|
ret = __gpio_produce_one_pulse_cycle(sia81xx->owi_pin,
|
|
sia81xx->owi_polarity, duty_time_us, idle_time_us,
|
|
real_duty_ns, real_idle_ns);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int sia81xx_owi_start_write_byte(
|
|
struct sia81xx_dev_s *sia81xx)
|
|
{
|
|
/* before start write, gpio level must be opposite with OWI_POLARITY */
|
|
if(gpio_get_value(sia81xx->owi_pin) == sia81xx->owi_polarity) {
|
|
pr_err("[ err][%s] %s: EPOLAR !!! \r\n", LOG_FLAG, __func__);
|
|
return -EPOLAR;
|
|
}
|
|
|
|
/* *******************************************************************
|
|
* for reset flipping timer,
|
|
* otherwise only need gpio_set_value(dev->owi_pin, OWI_POLARITY);
|
|
* *******************************************************************/
|
|
gpio_flipping(sia81xx->owi_pin, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void sia81xx_owi_end_write_byte(
|
|
struct sia81xx_dev_s *sia81xx,
|
|
s64 *real_idle_ns)
|
|
{
|
|
gpio_flipping(sia81xx->owi_pin, real_idle_ns);
|
|
}
|
|
|
|
static s64 sia81xx_owi_calc_deviation_ns(
|
|
struct sia81xx_dev_s *dev,
|
|
s64 duty_time_ns,
|
|
s64 idle_time_ns,
|
|
const char bit)
|
|
{
|
|
s64 deviation = 0;
|
|
|
|
dev->err_info.owi_max_gap =
|
|
dev->err_info.owi_max_gap > duty_time_ns ?
|
|
dev->err_info.owi_max_gap : duty_time_ns;
|
|
|
|
dev->err_info.owi_max_gap =
|
|
dev->err_info.owi_max_gap > idle_time_ns ?
|
|
dev->err_info.owi_max_gap : idle_time_ns;
|
|
|
|
sia81xx_owi_calc_bit_duty_ns(duty_time_ns, idle_time_ns,
|
|
&duty_time_ns, &idle_time_ns, bit);
|
|
|
|
if(duty_time_ns < idle_time_ns)
|
|
deviation = idle_time_ns - duty_time_ns;
|
|
|
|
return deviation;
|
|
}
|
|
|
|
static int sia81xx_owi_write_one_byte(
|
|
struct sia81xx_dev_s *sia81xx,
|
|
const char data)
|
|
{
|
|
int ret = 0, i = 0;
|
|
s64 real_duty_ns = 0, real_idle_ns = 0, last_real_duty_ns = 0;
|
|
s64 max_deviation_ns = 0, cur_deviation_ns = 0;
|
|
char last_bit = 0;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&sia81xx->owi_lock, flags);
|
|
|
|
ret = sia81xx_owi_start_write_byte(sia81xx);
|
|
if(0 != ret)
|
|
goto owi_end_write_one_byte;
|
|
|
|
#ifdef OWI_DATA_BIG_END
|
|
for(i = 7; i >= 0; i--) {
|
|
ret = sia81xx_owi_write_one_bit(sia81xx, (data >> i),
|
|
&real_duty_ns, &real_idle_ns);
|
|
if(0 != ret)
|
|
goto owi_end_write_one_byte;
|
|
|
|
/* when second bit has been sent,
|
|
* can be get first bit's real idle time */
|
|
if(7 > i) {
|
|
cur_deviation_ns = sia81xx_owi_calc_deviation_ns(sia81xx,
|
|
last_real_duty_ns, real_idle_ns, last_bit);
|
|
max_deviation_ns = max_deviation_ns > cur_deviation_ns ?
|
|
max_deviation_ns : cur_deviation_ns;
|
|
|
|
/* occur write error */
|
|
if(0 != cur_deviation_ns)
|
|
break;
|
|
}
|
|
last_bit = (data >> i);
|
|
last_real_duty_ns = real_duty_ns;
|
|
}
|
|
#else
|
|
for(i = 0; i < 8; i++) {
|
|
ret = sia81xx_owi_write_one_bit(sia81xx, (data >> i),
|
|
&real_duty_ns, &real_idle_ns);
|
|
if(0 != ret)
|
|
goto owi_end_write_one_byte;
|
|
|
|
/* when second bit has been sent,
|
|
* can be get first bit's real idle time */
|
|
if(0 < i) {
|
|
cur_deviation_ns = sia81xx_owi_calc_deviation_ns(sia81xx,
|
|
last_real_duty_ns, real_idle_ns, last_bit);
|
|
max_deviation_ns = max_deviation_ns > cur_deviation_ns ?
|
|
max_deviation_ns : cur_deviation_ns;
|
|
|
|
/* occur write error */
|
|
if(0 != cur_deviation_ns)
|
|
break;
|
|
}
|
|
last_bit = (data >> i);
|
|
last_real_duty_ns = real_duty_ns;
|
|
}
|
|
#endif
|
|
|
|
/* end write byte, and get last bit real idle time */
|
|
sia81xx_owi_end_write_byte(sia81xx, &real_idle_ns);
|
|
|
|
/* no write error */
|
|
if(0 == cur_deviation_ns) {
|
|
/* then can be get the last bit's real idle time */
|
|
cur_deviation_ns = sia81xx_owi_calc_deviation_ns(sia81xx,
|
|
last_real_duty_ns, real_idle_ns, last_bit);
|
|
max_deviation_ns = max_deviation_ns > cur_deviation_ns ?
|
|
max_deviation_ns : cur_deviation_ns;
|
|
}
|
|
|
|
owi_end_write_one_byte:
|
|
|
|
spin_unlock_irqrestore(&sia81xx->owi_lock, flags);
|
|
|
|
if(0 != cur_deviation_ns)
|
|
ret = -EPTOUT;
|
|
|
|
if(0 != ret) {
|
|
if(-EPOLAR == ret)
|
|
/* record polarity err */
|
|
sia81xx->err_info.owi_polarity_err_cnt++;
|
|
else
|
|
/* record wirte err */
|
|
sia81xx->err_info.owi_write_err_cnt ++;
|
|
}
|
|
|
|
/* record history max deviation time */
|
|
sia81xx->err_info.owi_max_deviation =
|
|
sia81xx->err_info.owi_max_deviation > max_deviation_ns ?
|
|
sia81xx->err_info.owi_max_deviation : max_deviation_ns;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int sia81xx_owi_write_data(
|
|
struct sia81xx_dev_s *sia81xx,
|
|
unsigned int len,
|
|
const char *buf)
|
|
{
|
|
int ret = 0, i = 0, retry = 0;
|
|
|
|
sia81xx->err_info.owi_write_data_cnt ++;
|
|
|
|
for(i = 0; i < len; i++) {
|
|
|
|
retry = 0;
|
|
|
|
while (retry < MAX_OWI_RETRY_TIMES) {
|
|
|
|
if(0 == (ret = sia81xx_owi_write_one_byte(sia81xx, buf[i])))
|
|
break;
|
|
|
|
/* must be ust msleep, do not use mdelay */
|
|
msleep(1);
|
|
|
|
retry ++;
|
|
}
|
|
|
|
/* record max retry time */
|
|
sia81xx->err_info.owi_max_retry_cnt =
|
|
sia81xx->err_info.owi_max_retry_cnt > retry ?
|
|
sia81xx->err_info.owi_max_retry_cnt : retry;
|
|
|
|
if(retry >= MAX_OWI_RETRY_TIMES) {
|
|
sia81xx->err_info.owi_write_data_err_cnt ++;
|
|
return -EPTOUT;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static void sia81xx_set_owi_polarity(
|
|
struct sia81xx_dev_s *sia81xx)
|
|
{
|
|
gpio_set_value(sia81xx->owi_pin, sia81xx->owi_polarity);
|
|
}
|
|
|
|
static int __sia81xx_owi_write_mode(
|
|
struct sia81xx_dev_s *sia81xx,
|
|
unsigned int cycles)
|
|
{
|
|
int ret = 0;
|
|
s64 max_flipping_ns = 0, last_flipping_ns = 0;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&sia81xx->owi_lock, flags);
|
|
|
|
sia81xx_set_owi_polarity(sia81xx);
|
|
udelay(1500); /* wait for owi reset, longer than 1ms */
|
|
|
|
/* last pulse only flipping once, so pulses = cycles - 1 */
|
|
ret = gpio_produce_pulse_cycles(sia81xx->owi_pin, sia81xx->owi_polarity,
|
|
sia81xx->owi_delay_us, cycles - 1, &max_flipping_ns);
|
|
/* last pulse */
|
|
gpio_flipping(sia81xx->owi_pin, &last_flipping_ns);
|
|
|
|
spin_unlock_irqrestore(&sia81xx->owi_lock, flags);
|
|
|
|
if(0 != ret) {
|
|
if(-EPOLAR == ret)
|
|
/* record polarity err */
|
|
sia81xx->err_info.owi_polarity_err_cnt++;
|
|
else
|
|
/* record wirte err */
|
|
sia81xx->err_info.owi_write_err_cnt ++;
|
|
return ret;
|
|
}
|
|
|
|
/* if cycles == 1, then only flipping gpio, and do not care flipping time */
|
|
if(0 == (cycles - 1))
|
|
return 0;
|
|
|
|
/* get max flipping time */
|
|
max_flipping_ns = max_flipping_ns > last_flipping_ns ?
|
|
max_flipping_ns : last_flipping_ns;
|
|
|
|
/* record history max flipping time */
|
|
sia81xx->err_info.owi_max_gap =
|
|
sia81xx->err_info.owi_max_gap > max_flipping_ns ?
|
|
sia81xx->err_info.owi_max_gap : max_flipping_ns;
|
|
|
|
if((MAX_OWI_PULSE_GAP_TIME_US * NSEC_PER_USEC) <= max_flipping_ns ) {
|
|
/* record wirte err */
|
|
sia81xx->err_info.owi_write_err_cnt ++;
|
|
return -EPTOUT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sia81xx_owi_write_mode(
|
|
struct sia81xx_dev_s *sia81xx,
|
|
unsigned int mode)
|
|
{
|
|
unsigned int retry = 0;
|
|
|
|
if((MAX_OWI_MODE < mode) || (MIN_OWI_MODE > mode))
|
|
return -EINVAL;
|
|
|
|
sia81xx->err_info.owi_set_mode_cnt ++;
|
|
|
|
while(retry < MAX_OWI_RETRY_TIMES) {
|
|
|
|
if(0 == __sia81xx_owi_write_mode(sia81xx, mode))
|
|
break;
|
|
|
|
/* must be use msleep, do not use mdelay */
|
|
msleep(1);
|
|
|
|
retry ++;
|
|
}
|
|
|
|
/* record max retry time */
|
|
sia81xx->err_info.owi_max_retry_cnt =
|
|
sia81xx->err_info.owi_max_retry_cnt > retry ?
|
|
sia81xx->err_info.owi_max_retry_cnt : retry;
|
|
|
|
if(retry >= MAX_OWI_RETRY_TIMES) {
|
|
sia81xx->err_info.owi_set_mode_err_cnt ++;
|
|
return -EPTOUT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sia81xx_owi_init(
|
|
sia81xx_dev_t *sia81xx,
|
|
unsigned int owi_mode)
|
|
{
|
|
sia81xx->owi_delay_us = MIN_OWI_PULSE_GAP_TIME_US;
|
|
sia81xx->owi_polarity = OWI_POLARITY;
|
|
if((owi_mode >= MIN_OWI_MODE) && (owi_mode <= MAX_OWI_MODE))
|
|
sia81xx->owi_cur_mode = owi_mode;
|
|
else
|
|
sia81xx->owi_cur_mode = DEFAULT_OWI_MODE;
|
|
|
|
pr_debug("[debug][%s] %s: running mode = %u \r\n",
|
|
LOG_FLAG, __func__, sia81xx->owi_cur_mode);
|
|
|
|
return 0;
|
|
}
|
|
/********************************************************************
|
|
* end - sia81xx owis option
|
|
********************************************************************/
|
|
|
|
|
|
/********************************************************************
|
|
* sia81xx chip option
|
|
********************************************************************/
|
|
static bool is_chip_type_supported(unsigned int chip_type)
|
|
{
|
|
if (chip_type >= ARRAY_SIZE(support_chip_type_name_table))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool sia81xx_is_chip_en(sia81xx_dev_t *sia81xx)
|
|
{
|
|
if (0 == sia81xx->disable_pin) {
|
|
if (SIA81XX_ENABLE_LEVEL == gpio_get_value(sia81xx->rst_pin))
|
|
return true;
|
|
} else {
|
|
if (sia81xx_regmap_get_chip_en(sia81xx->regmap, sia81xx->chip_type))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int sia81xx_reg_init(
|
|
struct sia81xx_dev_s *sia81xx)
|
|
{
|
|
if(NULL == sia81xx->client)
|
|
return 0;
|
|
|
|
if (CHIP_TYPE_SIA8101 == sia81xx->chip_type
|
|
&& 0 != sia81xx->channel_num) {
|
|
sia81xx_regmap_defaults(g_default_sia_dev->regmap,
|
|
sia81xx->chip_type, sia81xx->scene, sia81xx->channel_num);
|
|
} else {
|
|
sia81xx_regmap_defaults(sia81xx->regmap,
|
|
sia81xx->chip_type, sia81xx->scene, sia81xx->channel_num);
|
|
}
|
|
|
|
udelay(100);
|
|
if (0 != sia81xx_regmap_check_chip_id(sia81xx->regmap, sia81xx->chip_type)) {
|
|
pr_warn("[ warn][%s] %s: sia81xx_regmap_check_chip_id failed !!! \r\n",
|
|
LOG_FLAG, __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* useless in any case */
|
|
//sia81xx_regmap_set_xfilter(sia81xx->regmap,
|
|
// sia81xx->chip_type, sia81xx->en_xfilter);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sia81xx_resume(
|
|
struct sia81xx_dev_s *sia81xx)
|
|
{
|
|
unsigned long flags;
|
|
int default_sia_is_open = 0;
|
|
|
|
pr_debug("[debug][%s] %s: NHrunning \r\n", LOG_FLAG, __func__);
|
|
|
|
if (NULL == sia81xx)
|
|
return -ENODEV;
|
|
|
|
if (is_chip_type_supported(sia81xx->chip_type) &&
|
|
!sia81xx_is_chip_en(sia81xx)) {
|
|
|
|
if (0 == sia81xx->disable_pin) {
|
|
if (CHIP_TYPE_SIA8101 == sia81xx->chip_type
|
|
&& 0 != sia81xx->channel_num) {
|
|
if (likely(NULL != g_default_sia_dev)) {
|
|
default_sia_is_open = gpio_get_value(g_default_sia_dev->rst_pin);
|
|
if (1 == default_sia_is_open)
|
|
sia81xx_suspend(g_default_sia_dev);
|
|
} else {
|
|
pr_err("[ err][%s] %s: g_default_sia_dev == NULL !!! \r\n",
|
|
LOG_FLAG, __func__);
|
|
goto err_sia81xx_resume;
|
|
}
|
|
}
|
|
|
|
/* power up chip */
|
|
if(0 != sia81xx_owi_write_mode(sia81xx, sia81xx->owi_cur_mode))
|
|
goto err_sia81xx_resume;
|
|
|
|
spin_lock_irqsave(&sia81xx->rst_lock, flags);
|
|
pr_info("%s:set rst_pin High\n",__func__);
|
|
gpio_set_value(sia81xx->rst_pin, SIA81XX_ENABLE_LEVEL);
|
|
mdelay(1); /* wait chip power up, the time must be > 1ms */
|
|
spin_unlock_irqrestore(&sia81xx->rst_lock, flags);
|
|
|
|
if (CHIP_TYPE_SIA8109 == sia81xx->chip_type || CHIP_TYPE_SIA81X9 == sia81xx->chip_type)
|
|
mdelay(39); /* for sia8109 gain rising. */
|
|
}
|
|
|
|
sia81xx_reg_init(sia81xx);
|
|
sia81xx_regmap_set_chip_on(sia81xx->regmap,
|
|
sia81xx->chip_type, sia81xx->scene);
|
|
sia81xx_regmap_check_trimming(sia81xx->regmap, sia81xx->chip_type);
|
|
|
|
|
|
if (0 == sia81xx->disable_pin) {
|
|
if (CHIP_TYPE_SIA8101 == sia81xx->chip_type
|
|
&& 0 != sia81xx->channel_num) {
|
|
if (1 == default_sia_is_open)
|
|
sia81xx_resume(g_default_sia_dev);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sia81xx->en_dyn_ud_vdd || sia81xx->en_dyn_ud_pvdd)
|
|
sia81xx_timer_task_start(sia81xx->timer_task_hdl);
|
|
|
|
return 0;
|
|
|
|
err_sia81xx_resume:
|
|
pr_err("[ err][%s] %s: error !!! \r\n", LOG_FLAG, __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int sia81xx_suspend(
|
|
struct sia81xx_dev_s *sia81xx)
|
|
{
|
|
unsigned long flags;
|
|
|
|
pr_debug("[debug][%s] %s: running \r\n", LOG_FLAG, __func__);
|
|
|
|
if (NULL == sia81xx)
|
|
return -ENODEV;
|
|
|
|
if (sia81xx->en_dyn_ud_vdd || sia81xx->en_dyn_ud_pvdd)
|
|
sia81xx_timer_task_stop(sia81xx->timer_task_hdl);
|
|
|
|
if (is_chip_type_supported(sia81xx->chip_type) &&
|
|
sia81xx_is_chip_en(sia81xx)) {
|
|
|
|
sia81xx_regmap_set_chip_off(sia81xx->regmap, sia81xx->chip_type);
|
|
|
|
if (0 == sia81xx->disable_pin) {
|
|
spin_lock_irqsave(&sia81xx->rst_lock, flags);
|
|
pr_info("%s:set rst_pin low\n",__func__);
|
|
/* power off chip */
|
|
gpio_set_value(sia81xx->rst_pin, SIA81XX_DISABLE_LEVEL);
|
|
mdelay(1); /* wait chip power off, the time must be > 1ms */
|
|
|
|
spin_unlock_irqrestore(&sia81xx->rst_lock, flags);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sia81xx_reboot(
|
|
struct sia81xx_dev_s *sia81xx)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = sia81xx_suspend(sia81xx);
|
|
if(0 != ret)
|
|
return ret;
|
|
|
|
return sia81xx_resume(sia81xx);
|
|
}
|
|
|
|
static int sia81xx_set_mode(
|
|
struct sia81xx_dev_s *sia81xx,
|
|
unsigned int mode)
|
|
{
|
|
int ret = 0;
|
|
|
|
if((MAX_OWI_MODE < mode) || (MIN_OWI_MODE > mode)) {
|
|
pr_err("[ err][%s] %s: error mode = %u !!! \r\n",
|
|
LOG_FLAG, __func__, mode);
|
|
return -EINVAL;
|
|
}
|
|
|
|
sia81xx->owi_cur_mode = mode;
|
|
|
|
ret = sia81xx_reboot(sia81xx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int sia81xx_dev_init(
|
|
sia81xx_dev_t *sia81xx,
|
|
struct device_node *sia81xx_of_node)
|
|
{
|
|
int ret = 0;
|
|
int owi_mode = DEFAULT_OWI_MODE;
|
|
int en_xfilter = 0;
|
|
int en_dyn_ud_vdd = 0;
|
|
int en_dyn_ud_pvdd = 0;
|
|
int dyn_ud_vdd_port = 0;
|
|
int timer_task_hdl = 0;
|
|
int channel_num = 0;
|
|
|
|
pr_debug("[debug][%s] %s: running \r\n", LOG_FLAG, __func__);
|
|
|
|
ret = of_property_read_u32(sia81xx_of_node,
|
|
"channel_num", &channel_num);
|
|
if(0 != ret) {
|
|
channel_num = 0;
|
|
}
|
|
|
|
ret = of_property_read_u32(sia81xx_of_node, "en_x_filter", &en_xfilter);
|
|
if((0 != ret) || (1 != en_xfilter)) {
|
|
en_xfilter = 0;
|
|
}
|
|
|
|
ret = of_property_read_u32(sia81xx_of_node,
|
|
"timer_task_hdl", &timer_task_hdl);
|
|
if(0 != ret) {
|
|
timer_task_hdl = SIA81XX_TIMER_TASK_INVALID_HDL;
|
|
}
|
|
|
|
ret = of_property_read_u32(sia81xx_of_node,
|
|
"en_dynamic_updata_vdd", &en_dyn_ud_vdd);
|
|
if((0 != ret) || (1 != en_dyn_ud_vdd)) {
|
|
en_dyn_ud_vdd = 0;
|
|
}
|
|
|
|
ret = of_property_read_u32(sia81xx_of_node,
|
|
"en_dynamic_updata_pvdd", &en_dyn_ud_pvdd);
|
|
if((0 != ret) || (1 != en_dyn_ud_pvdd)) {
|
|
en_dyn_ud_pvdd = 0;
|
|
}
|
|
|
|
ret = of_property_read_u32(sia81xx_of_node,
|
|
"dynamic_updata_vdd_port", &dyn_ud_vdd_port);
|
|
if(0 != ret) {
|
|
pr_err("[ err][%s] %s: get dynamic_updata_vdd_port "
|
|
"form dts fail, ret = %d !!! \r\n",
|
|
LOG_FLAG, __func__, dyn_ud_vdd_port);
|
|
en_dyn_ud_vdd = 0;
|
|
}
|
|
|
|
ret = of_property_read_u32(sia81xx_of_node, "owi_mode", &owi_mode);
|
|
if(0 != ret || MIN_OWI_MODE > owi_mode || MAX_OWI_MODE < owi_mode) {
|
|
pr_err("[ err][%s] %s: get owi_mode "
|
|
"form dts fail, ret = %d, owi_mode = %d !!! \r\n",
|
|
LOG_FLAG, __func__, ret, owi_mode);
|
|
owi_mode = DEFAULT_OWI_MODE;
|
|
}
|
|
|
|
clear_sia81xx_err_info(sia81xx);
|
|
|
|
sia81xx->channel_num = (uint32_t)channel_num;
|
|
sia81xx->en_xfilter = (unsigned int)en_xfilter;
|
|
sia81xx->timer_task_hdl = (uint32_t)timer_task_hdl;
|
|
sia81xx->en_dyn_ud_vdd = (unsigned int)en_dyn_ud_vdd;
|
|
sia81xx->en_dyn_ud_pvdd = (unsigned int)en_dyn_ud_pvdd;
|
|
sia81xx->dyn_ud_vdd_port = (unsigned int)dyn_ud_vdd_port;
|
|
sia81xx->scene = AUDIO_SCENE_PLAYBACK;
|
|
sia81xx_owi_init(sia81xx, (unsigned int)owi_mode);
|
|
|
|
sia81xx_suspend(sia81xx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sia81xx_dev_remove(
|
|
sia81xx_dev_t *sia81xx)
|
|
{
|
|
pr_debug("[debug][%s] %s: running \r\n", LOG_FLAG, __func__);
|
|
|
|
sia81xx->timer_task_hdl = SIA81XX_TIMER_TASK_INVALID_HDL;
|
|
|
|
sia81xx_suspend(sia81xx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef ALGO_SWITCH_EN
|
|
static int sia81xx_algo_en_write(
|
|
struct sia81xx_dev_s *sia81xx,
|
|
int32_t enable)
|
|
{
|
|
unsigned long cal_handle = 0;
|
|
|
|
pr_debug("[debug][%s] %s: tuning port = %d \r\n",
|
|
LOG_FLAG, __func__, sia81xx->dyn_ud_vdd_port);
|
|
|
|
if (NULL == tuning_if_opt.open || NULL == tuning_if_opt.write) {
|
|
pr_err("[ err][%s] %s: NULL == tuning_if_opt.open/write \r\n",
|
|
LOG_FLAG, __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
cal_handle = tuning_if_opt.open(sia81xx->dyn_ud_vdd_port);
|
|
if (0 == cal_handle) {
|
|
pr_err("[ err][%s] %s: NULL == cal_handle \r\n",
|
|
LOG_FLAG, __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return tuning_if_opt.write(cal_handle,
|
|
SIXTH_SIA81XX_RX_MODULE,
|
|
SIXTH_SIA81XX_RX_ENABLE,
|
|
sizeof(enable), (uint8_t *)&enable);
|
|
}
|
|
|
|
static int sia81xx_algo_en_read(
|
|
struct sia81xx_dev_s *sia81xx,
|
|
int32_t* enable)
|
|
{
|
|
unsigned long cal_handle = 0;
|
|
|
|
pr_debug("[debug][%s] %s: tuning port = %d \r\n",
|
|
LOG_FLAG, __func__, sia81xx->dyn_ud_vdd_port);
|
|
|
|
if (NULL == tuning_if_opt.open || NULL == tuning_if_opt.write) {
|
|
pr_err("[ err][%s] %s: NULL == tuning_if_opt.open/write \r\n",
|
|
LOG_FLAG, __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
cal_handle = tuning_if_opt.open(sia81xx->dyn_ud_vdd_port);
|
|
if (0 == cal_handle) {
|
|
pr_err("[ err][%s] %s: NULL == cal_handle \r\n",
|
|
LOG_FLAG, __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return tuning_if_opt.read(cal_handle,
|
|
SIXTH_SIA81XX_RX_MODULE,
|
|
SIXTH_SIA81XX_RX_ENABLE,
|
|
sizeof(*enable), (uint8_t *)enable);
|
|
}
|
|
#endif
|
|
/********************************************************************
|
|
* end - sia81xx chip option
|
|
********************************************************************/
|
|
|
|
#ifdef DISTINGUISH_CHIP_TYPE
|
|
/********************************************************************
|
|
* device attr option
|
|
********************************************************************/
|
|
static ssize_t sia81xx_device_show(
|
|
struct device* cd,
|
|
struct device_attribute *attr,
|
|
char* buf)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t sia81xx_device_store(
|
|
struct device* cd,
|
|
struct device_attribute *attr,
|
|
const char* buf,
|
|
size_t len)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static ssize_t sia81xx_cmd_show(
|
|
struct device* cd,
|
|
struct device_attribute *attr,
|
|
char* buf)
|
|
{
|
|
sia81xx_dev_t *sia81xx = (sia81xx_dev_t *)dev_get_drvdata(cd);
|
|
char tb[1024];
|
|
char chip_id = 0;
|
|
char vals[0x64];
|
|
int owi_pin_val = 0;
|
|
|
|
switch (sia81xx->chip_type) {
|
|
case CHIP_TYPE_SIA8108 :
|
|
case CHIP_TYPE_SIA8109 :
|
|
sia81xx_regmap_read(
|
|
sia81xx->regmap, 0x41, 1, &chip_id);
|
|
break;
|
|
case CHIP_TYPE_SIA8100 :
|
|
pr_debug("[debug][%s] %s: delay = %u \r\n",
|
|
LOG_FLAG, __func__, sia81xx->owi_delay_us);
|
|
return 0;
|
|
default :
|
|
sia81xx_regmap_read(
|
|
sia81xx->regmap, 0x00, 1, &chip_id);
|
|
break;
|
|
}
|
|
|
|
memset(vals, 0, sizeof(vals));
|
|
sia81xx_regmap_read(
|
|
sia81xx->regmap, 0x00, 0x0D + 1, vals);
|
|
|
|
if(0 == sia81xx->disable_pin) {
|
|
owi_pin_val = gpio_get_value(sia81xx->owi_pin);
|
|
}
|
|
|
|
snprintf(tb, 1024, "sia81xx_cmd_show : rst pin status : %d, chip id = 0x%02x \r\n"
|
|
"reg val = %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, "
|
|
"%02x, %02x, %02x, %02x\r\n"
|
|
"rst_pin = %d, owi_pin = %d \r\n"
|
|
"set_mode = %lu, set_mode_err = %lu, polarity_err = %lu \r\n "
|
|
"max_retry = %lu, write_err = %lu, delay = %u, m_gap = %lu \r\n "
|
|
"owi_max_deviation = %lu, owi_write_data_err_cnt = %lu, \r\n"
|
|
"owi_write_data_cnt = %lu \r\n"
|
|
"channel_num = %u, owi_mode = %u, en_x_filter = %u \r\n"
|
|
"sia81xx_disable_pin = %d, \r\n"
|
|
"en_dynamic_updata_vdd = %u, en_dynamic_updata_pvdd = %u, \r\n"
|
|
"dynamic_updata_vdd_port = 0x%x \r\n",
|
|
owi_pin_val, chip_id,
|
|
vals[0], vals[1], vals[2], vals[3], vals[4], vals[5], vals[6],
|
|
vals[7], vals[8], vals[9], vals[10], vals[11], vals[12], vals[13],
|
|
sia81xx->rst_pin, sia81xx->owi_pin,
|
|
sia81xx->err_info.owi_set_mode_cnt,
|
|
sia81xx->err_info.owi_set_mode_err_cnt,
|
|
sia81xx->err_info.owi_polarity_err_cnt,
|
|
sia81xx->err_info.owi_max_retry_cnt,
|
|
sia81xx->err_info.owi_write_err_cnt,
|
|
sia81xx->owi_delay_us,
|
|
sia81xx->err_info.owi_max_gap,
|
|
sia81xx->err_info.owi_max_deviation,
|
|
sia81xx->err_info.owi_write_data_err_cnt,
|
|
sia81xx->err_info.owi_write_data_cnt,
|
|
sia81xx->channel_num, sia81xx->owi_cur_mode, sia81xx->en_xfilter,
|
|
sia81xx->disable_pin,
|
|
sia81xx->en_dyn_ud_vdd, sia81xx->en_dyn_ud_pvdd,
|
|
sia81xx->dyn_ud_vdd_port);
|
|
strcpy(buf, tb);
|
|
|
|
return strlen(buf);
|
|
}
|
|
|
|
static ssize_t sia81xx_cmd_store(
|
|
struct device* cd,
|
|
struct device_attribute *attr,
|
|
const char* buf,
|
|
size_t len)
|
|
{
|
|
const char *split_symb = ",";
|
|
/* in strsep will be modify "cur" value, nor cur[i] value,
|
|
so this point shoud not be defined with "char * const" */
|
|
char *cur = (char *)buf;
|
|
char *after;
|
|
sia81xx_dev_t *sia81xx = (sia81xx_dev_t *)dev_get_drvdata(cd);
|
|
|
|
switch(simple_strtoul(strsep(&cur, split_symb), &after, 10)) {
|
|
case SIA81XX_CMD_POWER_ON :
|
|
sia81xx_resume(sia81xx);
|
|
break;
|
|
case SIA81XX_CMD_POWER_OFF :
|
|
sia81xx_suspend(sia81xx);
|
|
break;
|
|
case SIA81XX_CMD_GET_MODE :
|
|
{
|
|
pr_debug("[debug][%s] %s: mode = %u \r\n",
|
|
LOG_FLAG, __func__, sia81xx->owi_cur_mode);
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_SET_MODE :
|
|
{
|
|
sia81xx_set_mode(sia81xx,
|
|
simple_strtoul(strsep(&cur, split_symb), &after, 10));
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_GET_REG :
|
|
{
|
|
unsigned char addr = (unsigned char)simple_strtoul(
|
|
strsep(&cur, split_symb), &after, 16);
|
|
char val;
|
|
|
|
if(0 != sia81xx_regmap_read(sia81xx->regmap, addr, 1, &val)) {
|
|
pr_debug("[debug][%s] %s: err regmap_read \r\n",
|
|
LOG_FLAG, __func__);
|
|
} else {
|
|
pr_debug("[debug][%s] %s: addr = 0x%02x, val = 0x%02x \r\n",
|
|
LOG_FLAG, __func__, addr, val);
|
|
}
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_SET_REG :
|
|
{
|
|
unsigned char addr = (unsigned int)simple_strtoul(
|
|
strsep(&cur, split_symb), &after, 16);
|
|
char val = (char)simple_strtoul(
|
|
strsep(&cur, split_symb), &after, 16);
|
|
|
|
if(0 != sia81xx_regmap_write(sia81xx->regmap, addr, 1, &val)) {
|
|
pr_err("[ err][%s] %s: regmap_write \r\n",
|
|
LOG_FLAG, __func__);
|
|
}
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_SET_OWI_DELAY :
|
|
{
|
|
unsigned int temp_us = (unsigned int)simple_strtoul(
|
|
strsep(&cur, split_symb), &after, 10);
|
|
if(temp_us < MAX_OWI_PULSE_GAP_TIME_US) /* only for test, pulse width must be < 1ms */
|
|
sia81xx->owi_delay_us = temp_us;
|
|
else
|
|
sia81xx->owi_delay_us = MAX_OWI_PULSE_GAP_TIME_US;
|
|
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_CLR_ERROR :
|
|
{
|
|
memset(&sia81xx->err_info, 0, sizeof(sia81xx->err_info));
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_WRITE_BYTE :
|
|
{
|
|
#ifdef OWI_SUPPORT_WRITE_DATA
|
|
char data = simple_strtoul(strsep(&cur, split_symb), &after, 10);
|
|
sia81xx_owi_write_data(sia81xx, 1, &data);
|
|
#else
|
|
pr_info("[ info][%s] %s: the option "
|
|
"OWI_SUPPORT_WRITE_DATA unsupport!! \r\n", LOG_FLAG, __func__);
|
|
#endif
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_SOCKET_OPEN :
|
|
{
|
|
#ifdef SIA81XX_TUNING
|
|
sia81xx_open_sock_server();
|
|
#endif
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_SOCKET_CLOSE:
|
|
{
|
|
#ifdef SIA81XX_TUNING
|
|
sia81xx_close_sock_server();
|
|
#endif
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_VDD_SET_OPEN :
|
|
{
|
|
sia81xx->en_dyn_ud_vdd = 1;
|
|
sia81xx_auto_set_vdd_probe(
|
|
sia81xx->timer_task_hdl,
|
|
sia81xx->chip_type,
|
|
sia81xx->channel_num,
|
|
sia81xx->regmap,
|
|
sia81xx->dyn_ud_vdd_port,
|
|
SIA81XX_AUTO_VDD_EN_SET(sia81xx->en_dyn_ud_vdd) |
|
|
SIA81XX_AUTO_PVDD_EN_SET(sia81xx->en_dyn_ud_pvdd));
|
|
pr_debug("[debug][%s] %s: set auto vdd state %u, port 0x%04x \r\n",
|
|
LOG_FLAG, __func__,
|
|
sia81xx->en_dyn_ud_vdd,
|
|
sia81xx->dyn_ud_vdd_port);
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_VDD_SET_CLOSE :
|
|
{
|
|
//sia81xx_disable_auto_set_vdd(sia81xx->dyn_ud_vdd_port);
|
|
sia81xx->en_dyn_ud_vdd = 0;
|
|
|
|
sia81xx_set_auto_set_vdd_work_state(
|
|
sia81xx->timer_task_hdl,
|
|
sia81xx->channel_num,
|
|
sia81xx->en_dyn_ud_vdd);
|
|
|
|
sia81xx_auto_set_vdd_remove(
|
|
sia81xx->timer_task_hdl,
|
|
sia81xx->channel_num);
|
|
//sia81xx_close_set_vdd_server(0x400c/* SLIMBUS_6_RX */);
|
|
pr_debug("[debug][%s] %s: set auto vdd state %u, port 0x%04x \r\n",
|
|
LOG_FLAG, __func__,
|
|
sia81xx->en_dyn_ud_vdd,
|
|
sia81xx->dyn_ud_vdd_port);
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_TIMER_TASK_OPEN :
|
|
{
|
|
sia81xx->timer_task_hdl = sia81xx->timer_task_hdl_backup;
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_TIMER_TASK_CLOSE :
|
|
{
|
|
sia81xx_timer_task_stop(sia81xx->timer_task_hdl);
|
|
sia81xx->timer_task_hdl_backup = sia81xx->timer_task_hdl;
|
|
sia81xx->timer_task_hdl = SIA81XX_TIMER_TASK_INVALID_HDL;
|
|
break;
|
|
}
|
|
#ifdef ALGO_SWITCH_EN
|
|
case SIA81XX_CMD_ALGORITHM_OPEN :
|
|
{
|
|
sia81xx_algo_en_write(sia81xx, 1);
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_ALGORITHM_CLOSE :
|
|
{
|
|
sia81xx_algo_en_write(sia81xx, 0);
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_ALGORITHM_STATUS :
|
|
{
|
|
int32_t algo_en;
|
|
if (0 != sia81xx_algo_en_read(sia81xx, &algo_en)) {
|
|
pr_debug("[debug][%s] %s: err sia81xx_algo_en_read \r\n",
|
|
LOG_FLAG, __func__);
|
|
} else {
|
|
pr_debug("[debug][%s] %s: algo_en = %d \r\n",
|
|
LOG_FLAG, __func__, algo_en);
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
case SIA81XX_CMD_GET_RST_PIN:
|
|
{
|
|
if (0 == sia81xx->disable_pin) {
|
|
unsigned int gpio_value = 0xff;
|
|
|
|
gpio_value = gpio_get_value(sia81xx->rst_pin);
|
|
pr_debug("[debug][%s] %s: get reset pin value, pin num:%u, value:%u \r\n",
|
|
LOG_FLAG, __func__, sia81xx->rst_pin, gpio_value);
|
|
}
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_SET_RST_PIN:
|
|
{
|
|
if (0 == sia81xx->disable_pin) {
|
|
unsigned char value = (unsigned int)simple_strtoul(
|
|
strsep(&cur, split_symb), &after, 16);
|
|
|
|
/* power on/off chip */
|
|
gpio_set_value(sia81xx->rst_pin, value);
|
|
mdelay(1); /* wait chip power off, the time must be > 1ms */
|
|
|
|
pr_debug("[debug][%s] %s: set reset pin %u to %u \r\n",
|
|
LOG_FLAG, __func__, sia81xx->rst_pin, value);
|
|
}
|
|
break;
|
|
}
|
|
case SIA81XX_CMD_TEST :
|
|
{
|
|
break;
|
|
}
|
|
default :
|
|
return -ENOIOCTLCMD;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
/********************************************************************
|
|
* end - device attr option
|
|
********************************************************************/
|
|
|
|
|
|
/********************************************************************
|
|
* sia81xx codec driver
|
|
********************************************************************/
|
|
static int sia81xx_power_get(
|
|
struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,16,28))
|
|
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
|
|
sia81xx_dev_t *sia81xx = snd_soc_component_get_drvdata(component);
|
|
#else
|
|
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
|
sia81xx_dev_t *sia81xx = snd_soc_codec_get_drvdata(codec);
|
|
#endif
|
|
|
|
ucontrol->value.integer.value[0] =
|
|
(unsigned long)sia81xx_is_chip_en(sia81xx);
|
|
|
|
pr_debug("[debug][%s] %s: ucontrol = %ld, channel_num = %d \r\n",
|
|
LOG_FLAG, __func__, ucontrol->value.integer.value[0], sia81xx->channel_num);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sia81xx_power_set(
|
|
struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,16,28))
|
|
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
|
|
sia81xx_dev_t *sia81xx = snd_soc_component_get_drvdata(component);
|
|
#else
|
|
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
|
sia81xx_dev_t *sia81xx = snd_soc_codec_get_drvdata(codec);
|
|
#endif
|
|
|
|
pr_debug("[debug][%s] %s: ucontrol = %ld, rst = %d, channel_num = %d \r\n",
|
|
LOG_FLAG, __func__, ucontrol->value.integer.value[0],
|
|
sia81xx->rst_pin, sia81xx->channel_num);
|
|
|
|
if(1 == ucontrol->value.integer.value[0]) {
|
|
sia81xx_resume(sia81xx);
|
|
} else {
|
|
sia81xx_suspend(sia81xx);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sia81xx_audio_scene_get(
|
|
struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,16,28))
|
|
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
|
|
sia81xx_dev_t *sia81xx = snd_soc_component_get_drvdata(component);
|
|
#else
|
|
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
|
sia81xx_dev_t *sia81xx = snd_soc_codec_get_drvdata(codec);
|
|
#endif
|
|
|
|
ucontrol->value.integer.value[0] = sia81xx->scene;
|
|
|
|
pr_debug("[debug][%s] %s: ucontrol = %ld, channle = %d \r\n",
|
|
LOG_FLAG, __func__, ucontrol->value.integer.value[0], sia81xx->channel_num);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sia81xx_audio_scene_set(
|
|
struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,16,28))
|
|
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
|
|
sia81xx_dev_t *sia81xx = snd_soc_component_get_drvdata(component);
|
|
#else
|
|
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
|
sia81xx_dev_t *sia81xx = snd_soc_codec_get_drvdata(codec);
|
|
#endif
|
|
|
|
pr_debug("[debug][%s] %s: ucontrol = %ld, rst = %d, channle = %d \r\n",
|
|
LOG_FLAG, __func__, ucontrol->value.integer.value[0],
|
|
sia81xx->rst_pin, sia81xx->channel_num);
|
|
|
|
if(AUDIO_SCENE_NUM <= ucontrol->value.integer.value[0]) {
|
|
sia81xx->scene = AUDIO_SCENE_PLAYBACK;
|
|
pr_err("[ err][%s] %s: set audio scene val = %ld !!! \r\n",
|
|
LOG_FLAG, __func__, ucontrol->value.integer.value[0]);
|
|
} else {
|
|
sia81xx->scene = ucontrol->value.integer.value[0];
|
|
}
|
|
|
|
if (sia81xx_is_chip_en(sia81xx))
|
|
sia81xx_reboot(sia81xx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef ALGO_SWITCH_EN
|
|
static int sia81xx_algo_en_get(
|
|
struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,16,28))
|
|
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
|
|
sia81xx_dev_t *sia81xx = snd_soc_component_get_drvdata(component);
|
|
#else
|
|
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
|
sia81xx_dev_t *sia81xx = snd_soc_codec_get_drvdata(codec);
|
|
#endif
|
|
int32_t algo_en = 0;
|
|
|
|
pr_debug("[debug][%s] %s: ucontrol = %ld \r\n",
|
|
LOG_FLAG, __func__, ucontrol->value.integer.value[0]);
|
|
|
|
if (sia81xx_is_chip_en(sia81xx))
|
|
sia81xx_algo_en_read(sia81xx, &algo_en);
|
|
|
|
ucontrol->value.integer.value[0] = algo_en;
|
|
return 0;
|
|
}
|
|
|
|
static int sia81xx_algo_en_set(
|
|
struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,16,28))
|
|
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
|
|
sia81xx_dev_t *sia81xx = snd_soc_component_get_drvdata(component);
|
|
#else
|
|
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
|
sia81xx_dev_t *sia81xx = snd_soc_codec_get_drvdata(codec);
|
|
#endif
|
|
|
|
pr_debug("[debug][%s] %s: ucontrol = %ld\r\n",
|
|
LOG_FLAG, __func__, ucontrol->value.integer.value[0]);
|
|
|
|
if (sia81xx_is_chip_en(sia81xx))
|
|
if (0 >= sia81xx_algo_en_write(sia81xx,
|
|
ucontrol->value.integer.value[0]))
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static const char *const power_function[] = { "Off", "On" };
|
|
#ifdef ALGO_SWITCH_EN
|
|
static const char *const algo_enable[] = { "Off", "On" };
|
|
#endif
|
|
static const char *const audio_scene[] = { "Playback", "Voice", "Receiver", "Factory" };
|
|
|
|
static const struct soc_enum power_enum =
|
|
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(power_function), power_function);
|
|
#ifdef ALGO_SWITCH_EN
|
|
static const struct soc_enum algo_enum =
|
|
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(algo_enable), algo_enable);
|
|
#endif
|
|
static const struct soc_enum audio_scene_enum =
|
|
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(audio_scene), audio_scene);
|
|
|
|
static const struct snd_kcontrol_new sia81xx_controls[] = {
|
|
SOC_ENUM_EXT("Sia81xx Power", power_enum,
|
|
sia81xx_power_get, sia81xx_power_set),
|
|
#ifdef ALGO_SWITCH_EN
|
|
SOC_ENUM_EXT("Sia81xx Algo", algo_enum,
|
|
sia81xx_algo_en_get, sia81xx_algo_en_set),
|
|
#endif
|
|
SOC_ENUM_EXT("Sia81xx Audio Scene", audio_scene_enum,
|
|
sia81xx_audio_scene_get, sia81xx_audio_scene_set)
|
|
};
|
|
|
|
static int sia81xx_spkr_pa_event(
|
|
struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol,
|
|
int event)
|
|
{
|
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,16,28))
|
|
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
|
sia81xx_dev_t *sia81xx = snd_soc_component_get_drvdata(component);
|
|
#else
|
|
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
|
|
sia81xx_dev_t *sia81xx = snd_soc_codec_get_drvdata(codec);
|
|
#endif
|
|
|
|
pr_debug("[debug][%s] %s: msg = %d \r\n", LOG_FLAG, __func__, event);
|
|
|
|
switch (event) {
|
|
case SND_SOC_DAPM_POST_PMU :
|
|
sia81xx_resume(sia81xx);
|
|
break;
|
|
case SND_SOC_DAPM_PRE_PMD :
|
|
sia81xx_suspend(sia81xx);
|
|
break;
|
|
default :
|
|
pr_err("[ err][%s] %s: msg = %d \r\n", LOG_FLAG, __func__, event);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_dapm_widget sia81xx_dapm_widgets[] = {
|
|
SND_SOC_DAPM_INPUT("IN"),
|
|
|
|
SND_SOC_DAPM_OUT_DRV_E("SPKR DRV", 0, 0, 0, NULL, 0,
|
|
sia81xx_spkr_pa_event, SND_SOC_DAPM_POST_PMU |
|
|
SND_SOC_DAPM_PRE_PMD),
|
|
|
|
SND_SOC_DAPM_OUTPUT("SPKR")
|
|
};
|
|
|
|
/*static const struct snd_soc_dapm_route sia81xx_audio_map[] = {
|
|
{"SPKR DRV", NULL, "IN"},
|
|
{"SPKR", NULL, "SPKR DRV"}
|
|
};*/
|
|
|
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,16,28))
|
|
static int sia81xx_component_probe(
|
|
struct snd_soc_component *component)
|
|
{
|
|
pr_info("[debug][%s] %s: running \r\n", LOG_FLAG, __func__);
|
|
return 0;
|
|
}
|
|
|
|
static void sia81xx_component_remove(
|
|
struct snd_soc_component *component)
|
|
{
|
|
pr_info("[debug][%s] %s: running \r\n", LOG_FLAG, __func__);
|
|
return;
|
|
}
|
|
#else
|
|
static int sia81xx_codec_probe(
|
|
struct snd_soc_codec *codec)
|
|
{
|
|
pr_info("[debug][%s] %s: running \r\n", LOG_FLAG, __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int sia81xx_codec_remove(
|
|
struct snd_soc_codec *codec)
|
|
{
|
|
pr_info("[debug][%s] %s: running \r\n", LOG_FLAG, __func__);
|
|
return 0;
|
|
}
|
|
|
|
static struct regmap *sia81xx_codec_get_regmap(struct device *dev)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0))
|
|
static struct snd_soc_component_driver soc_component_dev_sia81xx = {
|
|
.probe = sia81xx_component_probe,
|
|
.remove = sia81xx_component_remove,
|
|
.controls = sia81xx_controls,
|
|
.num_controls = ARRAY_SIZE(sia81xx_controls),
|
|
.dapm_widgets = sia81xx_dapm_widgets,
|
|
.num_dapm_widgets = ARRAY_SIZE(sia81xx_dapm_widgets),
|
|
.dapm_routes = NULL, //sia81xx_audio_map,
|
|
.num_dapm_routes = 0,//ARRAY_SIZE(sia81xx_audio_map),
|
|
};
|
|
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0) && LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0))
|
|
static struct snd_soc_codec_driver soc_codec_dev_sia81xx = {
|
|
.probe = sia81xx_codec_probe,
|
|
.remove = sia81xx_codec_remove,
|
|
.get_regmap = sia81xx_codec_get_regmap,
|
|
.component_driver = {
|
|
.controls = sia81xx_controls,
|
|
.num_controls = ARRAY_SIZE(sia81xx_controls),
|
|
.dapm_widgets = sia81xx_dapm_widgets,
|
|
.num_dapm_widgets = ARRAY_SIZE(sia81xx_dapm_widgets),
|
|
.dapm_routes = NULL, //sia81xx_audio_map,
|
|
.num_dapm_routes = 0,//ARRAY_SIZE(sia81xx_audio_map),
|
|
},
|
|
};
|
|
#else
|
|
static struct snd_soc_codec_driver soc_codec_dev_sia81xx = {
|
|
.probe = sia81xx_codec_probe,
|
|
.remove = sia81xx_codec_remove,
|
|
.get_regmap = sia81xx_codec_get_regmap,
|
|
.controls = sia81xx_controls,
|
|
.num_controls = ARRAY_SIZE(sia81xx_controls),
|
|
.dapm_widgets = sia81xx_dapm_widgets,
|
|
.num_dapm_widgets = ARRAY_SIZE(sia81xx_dapm_widgets),
|
|
.dapm_routes = NULL, //sia81xx_audio_map,
|
|
.num_dapm_routes = 0,//ARRAY_SIZE(sia81xx_audio_map),
|
|
};
|
|
#endif
|
|
*/
|
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,16,28))
|
|
static struct snd_soc_component_driver soc_component_dev_sia81xx = {
|
|
.probe = sia81xx_component_probe,
|
|
.remove = sia81xx_component_remove,
|
|
.controls = sia81xx_controls,
|
|
.num_controls = ARRAY_SIZE(sia81xx_controls),
|
|
.dapm_widgets = sia81xx_dapm_widgets,
|
|
.num_dapm_widgets = ARRAY_SIZE(sia81xx_dapm_widgets),
|
|
.dapm_routes = NULL, //sia81xx_audio_map,
|
|
.num_dapm_routes = 0,//ARRAY_SIZE(sia81xx_audio_map),
|
|
};
|
|
#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4,8,17) && LINUX_VERSION_CODE <= KERNEL_VERSION(4,16,28))
|
|
static struct snd_soc_codec_driver soc_codec_dev_sia81xx = {
|
|
.probe = sia81xx_codec_probe,
|
|
.remove = sia81xx_codec_remove,
|
|
.get_regmap = sia81xx_codec_get_regmap,
|
|
.component_driver = {
|
|
.controls = sia81xx_controls,
|
|
.num_controls = ARRAY_SIZE(sia81xx_controls),
|
|
.dapm_widgets = sia81xx_dapm_widgets,
|
|
.num_dapm_widgets = ARRAY_SIZE(sia81xx_dapm_widgets),
|
|
.dapm_routes = NULL, //sia81xx_audio_map,
|
|
.num_dapm_routes = 0,//ARRAY_SIZE(sia81xx_audio_map),
|
|
},
|
|
};
|
|
#else
|
|
static struct snd_soc_codec_driver soc_codec_dev_sia81xx = {
|
|
.probe = sia81xx_codec_probe,
|
|
.remove = sia81xx_codec_remove,
|
|
.get_regmap = sia81xx_codec_get_regmap,
|
|
.controls = sia81xx_controls,
|
|
.num_controls = ARRAY_SIZE(sia81xx_controls),
|
|
.dapm_widgets = sia81xx_dapm_widgets,
|
|
.num_dapm_widgets = ARRAY_SIZE(sia81xx_dapm_widgets),
|
|
.dapm_routes = NULL, //sia81xx_audio_map,
|
|
.num_dapm_routes = 0,//ARRAY_SIZE(sia81xx_audio_map),
|
|
};
|
|
#endif
|
|
|
|
/********************************************************************
|
|
* end - sia81xx codec driver
|
|
********************************************************************/
|
|
|
|
|
|
|
|
|
|
/********************************************************************
|
|
* sia81xx driver common
|
|
********************************************************************/
|
|
static unsigned int sia81xx_list_count(void)
|
|
{
|
|
unsigned count = 0;
|
|
sia81xx_dev_t *sia81xx = NULL;
|
|
|
|
mutex_lock(&sia81xx_list_mutex);
|
|
|
|
list_for_each_entry(sia81xx, &sia81xx_list, list) {
|
|
count ++;
|
|
}
|
|
|
|
mutex_unlock(&sia81xx_list_mutex);
|
|
|
|
return count;
|
|
}
|
|
|
|
static sia81xx_dev_t *find_sia81xx_dev(
|
|
const char *name,
|
|
struct device_node *of_node)
|
|
{
|
|
sia81xx_dev_t *sia81xx = NULL, *find = NULL;
|
|
|
|
if((NULL == name) && (NULL == of_node)) {
|
|
pr_err("[ err][%s] %s: NULL == input parameter \r\n",
|
|
LOG_FLAG, __func__);
|
|
return NULL;
|
|
}
|
|
|
|
mutex_lock(&sia81xx_list_mutex);
|
|
|
|
list_for_each_entry(sia81xx, &sia81xx_list, list) {
|
|
if(NULL != name) {
|
|
if(0 == strcmp(sia81xx->name, name)) {
|
|
find = sia81xx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* make sure that the sia81xx platform dev had been created */
|
|
if((NULL != of_node) && (NULL != sia81xx->pdev)) {
|
|
if(of_node == sia81xx->pdev->dev.of_node) {
|
|
find = sia81xx;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&sia81xx_list_mutex);
|
|
|
|
return find;
|
|
}
|
|
|
|
static void add_sia81xx_dev(
|
|
sia81xx_dev_t *sia81xx)
|
|
{
|
|
struct device_node *of_node = NULL;
|
|
|
|
if(NULL == sia81xx) {
|
|
pr_err("[ err][%s] %s: NULL == sia81xx \r\n", LOG_FLAG, __func__);
|
|
return ;
|
|
}
|
|
|
|
if(NULL != sia81xx->pdev)
|
|
of_node = sia81xx->pdev->dev.of_node;
|
|
|
|
if(NULL != find_sia81xx_dev(sia81xx->name, of_node))
|
|
return ;
|
|
|
|
mutex_lock(&sia81xx_list_mutex);
|
|
list_add(&sia81xx->list, &sia81xx_list);
|
|
mutex_unlock(&sia81xx_list_mutex);
|
|
|
|
pr_debug("[debug][%s] %s: add sia81xx dev : %s, count = %u \r\n",
|
|
LOG_FLAG, __func__, sia81xx->name, sia81xx_list_count());
|
|
}
|
|
|
|
static void del_sia81xx_dev(
|
|
sia81xx_dev_t *sia81xx)
|
|
{
|
|
if(NULL == sia81xx) {
|
|
pr_err("[ err][%s] %s: NULL == sia81xx \r\n", LOG_FLAG, __func__);
|
|
return ;
|
|
}
|
|
|
|
pr_debug("[debug][%s] %s: del sia81xx dev : %s, count = %u \r\n",
|
|
LOG_FLAG, __func__, sia81xx->name, sia81xx_list_count());
|
|
|
|
mutex_lock(&sia81xx_list_mutex);
|
|
list_del(&sia81xx->list);
|
|
mutex_unlock(&sia81xx_list_mutex);
|
|
}
|
|
|
|
static sia81xx_dev_t *get_sia81xx_dev(
|
|
struct device *dev,
|
|
struct device_node *sia81xx_of_node)
|
|
{
|
|
int ret = 0;
|
|
sia81xx_dev_t *sia81xx = NULL;
|
|
const char *sia81xx_dev_name = NULL;
|
|
|
|
if(NULL == dev) {
|
|
pr_err("[ err][%s] %s: NULL == dev \r\n", LOG_FLAG, __func__);
|
|
return NULL;
|
|
}
|
|
|
|
/* check dev has been created or not by "si,sia81xx-dev-name" or of_node */
|
|
ret = of_property_read_string_index(dev->of_node,
|
|
"si,sia81xx-dev-name",
|
|
0,
|
|
&sia81xx_dev_name);
|
|
if(0 != ret) {
|
|
/* should been had one of "name" or "of_node" at least */
|
|
if(NULL == sia81xx_of_node)
|
|
return NULL;
|
|
|
|
sia81xx = find_sia81xx_dev(NULL, sia81xx_of_node);
|
|
if(NULL == sia81xx) {
|
|
/* don't use devm_kzalloc() */
|
|
sia81xx = kzalloc(sizeof(sia81xx_dev_t), GFP_KERNEL);
|
|
if(NULL == sia81xx) {
|
|
pr_err("[ err][%s] %s: dev[%s] cannot create "
|
|
"memory for sia81xx\n",
|
|
LOG_FLAG, __func__, dev->init_name);
|
|
return NULL;
|
|
}
|
|
|
|
/* default name */
|
|
snprintf(sia81xx->name, strlen("sia81xx-dummy.%u"),
|
|
"sia81xx-dummy.%u", sia81xx_list_count());
|
|
}
|
|
} else {
|
|
sia81xx = find_sia81xx_dev(sia81xx_dev_name, sia81xx_of_node);
|
|
if(NULL == sia81xx) {
|
|
/* don't use devm_kzalloc() */
|
|
sia81xx = kzalloc(sizeof(sia81xx_dev_t), GFP_KERNEL);
|
|
if(NULL == sia81xx) {
|
|
pr_err("[ err][%s] %s: dev[%s] cannot create "
|
|
"memory for sia81xx\n",
|
|
LOG_FLAG, __func__, dev->init_name);
|
|
return NULL;
|
|
}
|
|
|
|
strcpy(sia81xx->name, sia81xx_dev_name);
|
|
}
|
|
}
|
|
|
|
add_sia81xx_dev(sia81xx);
|
|
|
|
pr_debug("[debug][%s] %s: get dev name : %s \r\n",
|
|
LOG_FLAG, __func__, sia81xx->name);
|
|
|
|
return sia81xx;
|
|
}
|
|
|
|
static void put_sia81xx_dev(sia81xx_dev_t *sia81xx)
|
|
{
|
|
if(NULL == sia81xx) {
|
|
pr_err("[ err][%s] %s: NULL == sia81xx \r\n", LOG_FLAG, __func__);
|
|
return ;
|
|
}
|
|
|
|
pr_debug("[debug][%s] %s: put dev name : %s, pdev = %p, client = %p \r\n",
|
|
LOG_FLAG, __func__,
|
|
sia81xx->name, sia81xx->pdev, sia81xx->client);
|
|
|
|
if((NULL != sia81xx->pdev) || (NULL != sia81xx->client))
|
|
return ;
|
|
|
|
del_sia81xx_dev(sia81xx);
|
|
kfree(sia81xx);
|
|
}
|
|
|
|
static unsigned int get_chip_type(const char *name)
|
|
{
|
|
int i = 0, len = 0;
|
|
|
|
if(NULL == name)
|
|
return CHIP_TYPE_UNKNOWN;
|
|
|
|
pr_info("[ info][%s] %s: chip : %s \r\n",
|
|
LOG_FLAG, __func__, name);
|
|
|
|
len = strlen(name);
|
|
for (i = 0; i < ARRAY_SIZE(support_chip_type_name_table); i ++) {
|
|
if (strlen(support_chip_type_name_table[i]) == len &&
|
|
0 == memcmp(support_chip_type_name_table[i], name, len)) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return CHIP_TYPE_UNKNOWN;
|
|
}
|
|
|
|
/* CHIP_TYPE_SIA81X9 */
|
|
static const uint32_t sia81x9_list[] = {
|
|
CHIP_TYPE_SIA8109, // first chip reg range should cover all other chips
|
|
CHIP_TYPE_SIA8159
|
|
};
|
|
|
|
/* CHIP_TYPE_SIA8152X */
|
|
static const uint32_t sia8152x_list[] = {
|
|
CHIP_TYPE_SIA8152S, // first chip reg range should cover all other chips
|
|
CHIP_TYPE_SIA8152
|
|
};
|
|
|
|
/* compatible chips should have same i2c address */
|
|
static const struct sia81xx_chip_compat sia81xx_compat_table[] = {
|
|
{
|
|
CHIP_TYPE_SIA81X9,
|
|
{sia81x9_list, ARRAY_SIZE(sia81x9_list)}
|
|
},
|
|
{
|
|
CHIP_TYPE_SIA8152X,
|
|
{sia8152x_list, ARRAY_SIZE(sia8152x_list)}
|
|
}
|
|
};
|
|
|
|
unsigned int get_one_available_chip_type(unsigned int chip_type)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(sia81xx_compat_table); i++) {
|
|
if (chip_type == sia81xx_compat_table[i].sub_type) {
|
|
if (0 < sia81xx_compat_table[i].num)
|
|
//pr_info("[ info][%s] %s: chip_type = %u \r\n",
|
|
// LOG_FLAG, __func__, sia81xx_compat_table[i].chips[0]);
|
|
return sia81xx_compat_table[i].chips[0];
|
|
}
|
|
}
|
|
|
|
return chip_type;
|
|
}
|
|
|
|
#ifdef DISTINGUISH_CHIP_TYPE
|
|
static int check_sia81xx_status(sia81xx_dev_t *sia81xx)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (0 == sia81xx->disable_pin)
|
|
sia81xx_resume(sia81xx);
|
|
|
|
ret = sia81xx_regmap_check_chip_id(
|
|
sia81xx->regmap, sia81xx->chip_type);
|
|
|
|
if (0 == sia81xx->disable_pin)
|
|
sia81xx_suspend(sia81xx);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
void sia81xx_compatible_chips_adapt(
|
|
sia81xx_dev_t *sia81xx)
|
|
{
|
|
int i = 0, j = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(sia81xx_compat_table); i++) {
|
|
if (sia81xx->chip_type == sia81xx_compat_table[i].sub_type) {
|
|
|
|
if (0 == sia81xx->disable_pin)
|
|
sia81xx_resume(sia81xx);
|
|
|
|
for (j = 0; j < sia81xx_compat_table[i].num; j++) {
|
|
if (NULL != sia81xx_compat_table[i].chips
|
|
&& 0 == sia81xx_regmap_check_chip_id(
|
|
sia81xx->regmap, sia81xx_compat_table[i].chips[j])) {
|
|
sia81xx->chip_type = sia81xx_compat_table[i].chips[j];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (0 == sia81xx->disable_pin)
|
|
sia81xx_suspend(sia81xx);
|
|
|
|
if (j >= sia81xx_compat_table[i].num)
|
|
sia81xx->chip_type = CHIP_TYPE_UNKNOWN;
|
|
|
|
pr_info("[ info][%s] %s: chip_type = %u \r\n",
|
|
LOG_FLAG, __func__, sia81xx->chip_type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DISTINGUISH_CHIP_TYPE
|
|
/* check sia81xx is available */
|
|
if (CHIP_TYPE_UNKNOWN == sia81xx->chip_type ||
|
|
0 != check_sia81xx_status(sia81xx)) {
|
|
|
|
sia81xx->chip_type = CHIP_TYPE_UNKNOWN;
|
|
sia81xx->en_dyn_ud_pvdd = 0;
|
|
pr_info("[ info][%s] %s: there is no sia81xx device \r\n",
|
|
LOG_FLAG, __func__);
|
|
} else {
|
|
device_create_file(&sia81xx->pdev->dev, &dev_attr_sia81xx_device);
|
|
|
|
pr_info("[ info][%s] %s: sia81xx device is available \r\n",
|
|
LOG_FLAG, __func__);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/********************************************************************
|
|
* end - sia81xx driver common
|
|
********************************************************************/
|
|
|
|
|
|
/********************************************************************
|
|
* i2c bus driver
|
|
********************************************************************/
|
|
static int sia81xx_i2c_probe(
|
|
struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
sia81xx_dev_t *sia81xx = NULL;
|
|
struct regmap *regmap = NULL;
|
|
struct device_node *sia81xx_of_node = NULL;
|
|
const char *chip_type_name = NULL;
|
|
unsigned int chip_type = CHIP_TYPE_UNKNOWN;
|
|
int ret = 0;
|
|
char chip_id_num = 0;
|
|
|
|
pr_info("[ info][%s] %s: i2c addr = 0x%02x \r\n",
|
|
LOG_FLAG, __func__, client->addr);
|
|
|
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
|
pr_err("[ err][%s] %s: i2c_check_functionality return -ENODEV \r\n",
|
|
LOG_FLAG, __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
sia81xx_of_node = of_parse_phandle(client->dev.of_node,
|
|
"si,sia81xx-dev", 0);
|
|
|
|
/* get chip type name */
|
|
ret = of_property_read_string_index(sia81xx_of_node,
|
|
"si,sia81xx_type",
|
|
0,
|
|
&chip_type_name);
|
|
if(0 != ret) {
|
|
pr_err("[ err][%s] %s: get si,sia81xx_type return %d !!! \r\n",
|
|
LOG_FLAG, __func__, ret);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* get chip type value,
|
|
* and check the chip type Whether or not to be supported */
|
|
chip_type = get_chip_type(chip_type_name);
|
|
if(CHIP_TYPE_UNKNOWN == chip_type) {
|
|
pr_err("[ err][%s] %s: CHIP_TYPE_UNKNOWN == chip_type !!! \r\n",
|
|
LOG_FLAG, __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
regmap = sia81xx_regmap_init(client,
|
|
get_one_available_chip_type(chip_type));
|
|
if (IS_ERR(regmap)) {
|
|
pr_err("[ err][%s] %s: regmap_init_i2c error !!! \r\n",
|
|
LOG_FLAG, __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
sia81xx = get_sia81xx_dev(&client->dev, sia81xx_of_node);
|
|
if(NULL == sia81xx) {
|
|
pr_err("[ err][%s] %s: get_sia81xx_dev error !!! \r\n",
|
|
LOG_FLAG, __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
sia81xx->regmap = regmap;
|
|
|
|
/* save i2c client */
|
|
sia81xx->client = client;
|
|
|
|
/* sava driver private data to the dev's driver data */
|
|
dev_set_drvdata(&client->dev, sia81xx);
|
|
|
|
/* compatible awinic pa */
|
|
if (sia81xx->chip_type == CHIP_TYPE_SIA8152) {
|
|
sia81xx_regmap_read(sia81xx->regmap, 0x00, 1, &chip_id_num);
|
|
pr_info("%s: sia8152_chip_id = 0x%02x \n", __func__, chip_id_num);
|
|
if (chip_id_num != 0x52)
|
|
sia81xx->chip_type = CHIP_TYPE_UNKNOWN;
|
|
}
|
|
|
|
// for sia8101 stereo
|
|
if (CHIP_TYPE_SIA8101 == sia81xx->chip_type
|
|
&& 0 == sia81xx->channel_num)
|
|
g_default_sia_dev = sia81xx;
|
|
|
|
/* A temporary solution for customer, it should be ensure that,
|
|
* the sia81xx_probe() is executed before sia81xx_i2c_probe() execute */
|
|
sia81xx_compatible_chips_adapt(sia81xx);
|
|
|
|
/* probe other sub module */ /* update info if it's already probed */
|
|
if (1 == sia81xx->en_dyn_ud_vdd || 1 == sia81xx->en_dyn_ud_pvdd) {
|
|
sia81xx_auto_set_vdd_probe(
|
|
sia81xx->timer_task_hdl,
|
|
sia81xx->chip_type,
|
|
sia81xx->channel_num,
|
|
sia81xx->regmap,
|
|
sia81xx->dyn_ud_vdd_port,
|
|
SIA81XX_AUTO_VDD_EN_SET(sia81xx->en_dyn_ud_vdd) |
|
|
SIA81XX_AUTO_PVDD_EN_SET(sia81xx->en_dyn_ud_pvdd));
|
|
}
|
|
/* end - probe other sub module */
|
|
|
|
/* power down chip in any case when phone start up */
|
|
sia81xx_suspend(sia81xx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sia81xx_i2c_remove(
|
|
struct i2c_client *client)
|
|
{
|
|
int ret = 0;
|
|
sia81xx_dev_t *sia81xx = NULL;
|
|
|
|
pr_info("[ info][%s] %s: remove \r\n", LOG_FLAG, __func__);
|
|
|
|
sia81xx = (sia81xx_dev_t *)dev_get_drvdata(&client->dev);
|
|
if(NULL == sia81xx)
|
|
return 0;
|
|
|
|
sia81xx_regmap_remove(sia81xx->regmap);
|
|
sia81xx->regmap = NULL;
|
|
|
|
sia81xx->client= NULL;
|
|
|
|
put_sia81xx_dev(sia81xx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct i2c_device_id si_sia81xx_i2c_id[] = {
|
|
{ SIA81XX_I2C_NAME, 0 },
|
|
{ }
|
|
};
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id si_sia81xx_i2c_match[] = {
|
|
{.compatible = "si,sia81xx-i2c"},
|
|
{.compatible = "si,sia8101-i2c"},
|
|
{.compatible = "si,sia8108-i2c"},
|
|
{.compatible = "si,sia8109-i2c"},
|
|
{.compatible = "si,sia8152-i2c"},
|
|
{.compatible = "si,sia8152s-i2c"},
|
|
{.compatible = "si,sia8159-i2c"},
|
|
{.compatible = "si,sia81x9-i2c"},
|
|
{.compatible = "si,sia8152x-i2c"},
|
|
{},
|
|
};
|
|
#endif
|
|
|
|
static struct i2c_driver si_sia81xx_i2c_driver = {
|
|
.driver = {
|
|
.name = SIA81XX_I2C_NAME,
|
|
.owner = THIS_MODULE,
|
|
#ifdef CONFIG_OF
|
|
.of_match_table = si_sia81xx_i2c_match,
|
|
#endif
|
|
},
|
|
.probe = sia81xx_i2c_probe,
|
|
.remove = sia81xx_i2c_remove,
|
|
.id_table = si_sia81xx_i2c_id,
|
|
};
|
|
|
|
/********************************************************************
|
|
* end - i2c bus driver
|
|
********************************************************************/
|
|
|
|
/********************************************************************
|
|
* sia81xx dev driver
|
|
********************************************************************/
|
|
|
|
/* power manage options */
|
|
static int sia81xx_pm_suspend(
|
|
struct device *dev)
|
|
{
|
|
#if 0
|
|
sia81xx_dev_t *sia81xx = NULL;
|
|
|
|
pr_debug("[debug][%s] %s: running \r\n", LOG_FLAG, __func__);
|
|
|
|
|
|
sia81xx = (sia81xx_dev_t *)dev_get_drvdata(dev);
|
|
|
|
return sia81xx_suspend(sia81xx);
|
|
#else
|
|
pr_debug("[debug][%s] %s: running \r\n", LOG_FLAG, __func__);
|
|
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static int sia81xx_pm_resume(struct device *dev)
|
|
{
|
|
#if 0
|
|
sia81xx_dev_t *sia81xx = NULL;
|
|
|
|
pr_debug("[debug][%s] %s: running \r\n", LOG_FLAG, __func__);
|
|
|
|
sia81xx = (sia81xx_dev_t *)dev_get_drvdata(dev);
|
|
|
|
return sia81xx_resume(sia81xx);
|
|
#else
|
|
pr_debug("[debug][%s] %s: running \r\n", LOG_FLAG, __func__);
|
|
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static const struct dev_pm_ops si_sia81xx_pm_ops = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(sia81xx_pm_suspend, sia81xx_pm_resume)
|
|
};
|
|
|
|
static int sia81xx_probe(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
sia81xx_dev_t *sia81xx = NULL;
|
|
const char *chip_type_name = NULL;
|
|
unsigned int chip_type = CHIP_TYPE_UNKNOWN;
|
|
struct pinctrl *sia81xx_pinctrl = NULL;
|
|
struct pinctrl_state *pinctrl_state = NULL;
|
|
int disable_pin = 0, owi_pin = 0, rst_pin = 0;
|
|
|
|
pr_info("[ info][%s] %s: probe \r\n", LOG_FLAG, __func__);
|
|
|
|
/* get chip type name */
|
|
ret = of_property_read_string_index(pdev->dev.of_node,
|
|
"si,sia81xx_type",
|
|
0,
|
|
&chip_type_name);
|
|
if(0 != ret) {
|
|
pr_err("[ err][%s] %s: get si,sia81xx_type return %d !!! \r\n",
|
|
LOG_FLAG, __func__, ret);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* get chip type value,
|
|
* and check the chip type Whether or not to be supported */
|
|
chip_type = get_chip_type(chip_type_name);
|
|
if(CHIP_TYPE_UNKNOWN == chip_type) {
|
|
pr_err("[ err][%s] %s: CHIP_TYPE_UNKNOWN == chip_type !!! \r\n",
|
|
LOG_FLAG, __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
ret = of_property_read_u32(pdev->dev.of_node,
|
|
"si,sia81xx_disable_pin", &disable_pin);
|
|
if(0 != ret) {
|
|
pr_err("[ err][%s] %s: get si,sia81xx_disable_pin return %d !!! \r\n",
|
|
LOG_FLAG, __func__, ret);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if(0 == disable_pin) {
|
|
/* get reset gpio pin's pinctrl info */
|
|
sia81xx_pinctrl = devm_pinctrl_get(&pdev->dev);
|
|
if(NULL == sia81xx_pinctrl) {
|
|
pr_err("[ err][%s] %s: NULL == pinctrl !!! \r\n",
|
|
LOG_FLAG, __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* get owi gpio pin's specify pinctrl state */
|
|
pinctrl_state = pinctrl_lookup_state(sia81xx_pinctrl, "sia81xx_gpio");
|
|
if(NULL == pinctrl_state) {
|
|
pr_err("[ err][%s] %s: NULL == pinctrl_state !!! \r\n",
|
|
LOG_FLAG, __func__);
|
|
ret = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
/* set this pinctrl state, make this pin works in the gpio mode */
|
|
if(0 != (ret = pinctrl_select_state(sia81xx_pinctrl, pinctrl_state))) {
|
|
pr_err("[ err][%s] %s: error pinctrl_select_state return %d \r\n",
|
|
LOG_FLAG, __func__, ret);
|
|
ret = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
/* get reset pin's sn */
|
|
rst_pin = of_get_named_gpio(
|
|
pdev->dev.of_node, "si,sia81xx_reset", 0);
|
|
if(rst_pin < 0) {
|
|
pr_err("[ err][%s] %s: rst_pin < 0 !!! \r\n", LOG_FLAG, __func__);
|
|
ret = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
/* get owi pin's sn */
|
|
owi_pin = of_get_named_gpio(
|
|
pdev->dev.of_node, "si,sia81xx_owi", 0);
|
|
if(owi_pin < 0) {
|
|
pr_err("[ err][%s] %s: owi_pin < 0 !!! \r\n", LOG_FLAG, __func__);
|
|
ret = -ENODEV;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
sia81xx = get_sia81xx_dev(&pdev->dev, pdev->dev.of_node);
|
|
if(NULL == sia81xx) {
|
|
pr_err("[ err][%s] %s: get_sia81xx_dev error !!! \r\n",
|
|
LOG_FLAG, __func__);
|
|
ret = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
sia81xx->chip_type = chip_type;
|
|
sia81xx->rst_pin = rst_pin;
|
|
sia81xx->owi_pin = owi_pin;
|
|
sia81xx->disable_pin = disable_pin;
|
|
|
|
spin_lock_init(&sia81xx->owi_lock);
|
|
spin_lock_init(&sia81xx->rst_lock);
|
|
|
|
if(0 == disable_pin) {
|
|
/* set rst pin's direction */
|
|
gpio_direction_output(sia81xx->rst_pin, SIA81XX_DISABLE_LEVEL);
|
|
/* set owi pin's direction */
|
|
gpio_direction_output(sia81xx->owi_pin, OWI_POLARITY);
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,16,28))
|
|
ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_sia81xx, NULL, 0);
|
|
#else
|
|
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sia81xx, NULL, 0);
|
|
#endif
|
|
|
|
if(0 != ret) {
|
|
pr_err("[ err][%s] %s: snd_soc_register_codec fail \r\n",
|
|
LOG_FLAG, __func__);
|
|
goto put_dev_out;
|
|
}
|
|
|
|
ret = sia81xx_dev_init(sia81xx, pdev->dev.of_node);
|
|
if(0 != ret) {
|
|
pr_err("[ err][%s] %s: sia81xx_dev_init fail \r\n",
|
|
LOG_FLAG, __func__);
|
|
goto put_dev_out;
|
|
}
|
|
|
|
device_create_file(&pdev->dev, &dev_attr_sia81xx_cmd);
|
|
|
|
/* save platform dev */
|
|
sia81xx->pdev = pdev;
|
|
|
|
/* sava driver private data to the dev's driver data */
|
|
dev_set_drvdata(&pdev->dev, sia81xx);
|
|
|
|
/* probe other sub module */
|
|
if(1 == sia81xx->en_dyn_ud_vdd || 1 == sia81xx->en_dyn_ud_pvdd) {
|
|
sia81xx_auto_set_vdd_probe(
|
|
sia81xx->timer_task_hdl,
|
|
sia81xx->chip_type,
|
|
sia81xx->channel_num,
|
|
sia81xx->regmap,
|
|
sia81xx->dyn_ud_vdd_port,
|
|
SIA81XX_AUTO_VDD_EN_SET(sia81xx->en_dyn_ud_vdd) |
|
|
SIA81XX_AUTO_PVDD_EN_SET(sia81xx->en_dyn_ud_pvdd));
|
|
}
|
|
/* end - probe other sub module */
|
|
|
|
out:
|
|
if(0 == disable_pin) {
|
|
devm_pinctrl_put(sia81xx_pinctrl);
|
|
}
|
|
|
|
return ret;
|
|
|
|
put_dev_out:
|
|
if(0 == disable_pin) {
|
|
devm_pinctrl_put(sia81xx_pinctrl);
|
|
}
|
|
put_sia81xx_dev(sia81xx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int sia81xx_remove(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
sia81xx_dev_t *sia81xx = NULL;
|
|
|
|
pr_info("[ info][%s] %s: remove \r\n", LOG_FLAG, __func__);
|
|
|
|
sia81xx = (sia81xx_dev_t *)dev_get_drvdata(&pdev->dev);
|
|
if(NULL == sia81xx)
|
|
return 0;
|
|
|
|
/* remove other sub module */
|
|
sia81xx_auto_set_vdd_remove(
|
|
sia81xx->timer_task_hdl,
|
|
sia81xx->channel_num);
|
|
/* end - remove other sub module */
|
|
|
|
ret = sia81xx_dev_remove(sia81xx);
|
|
if(0 != ret) {
|
|
pr_err("[ err][%s] %s: sia81xx_dev_remove return : %d \r\n",
|
|
LOG_FLAG, __func__, ret);
|
|
}
|
|
|
|
sia81xx->pdev = NULL;
|
|
|
|
put_sia81xx_dev(sia81xx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void sia81xx_shutdown(struct platform_device *pdev)
|
|
{
|
|
sia81xx_dev_t *sia81xx = NULL;
|
|
|
|
pr_debug("[debug][%s] %s: running \r\n", LOG_FLAG, __func__);
|
|
|
|
if (NULL == pdev)
|
|
return ;
|
|
|
|
sia81xx = (sia81xx_dev_t *)dev_get_drvdata(&pdev->dev);
|
|
|
|
sia81xx_suspend(sia81xx);
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id si_sia81xx_dt_match[] = {
|
|
{ .compatible = "si,sia81xx" },
|
|
{ .compatible = "si,sia8100" },
|
|
{ .compatible = "si,sia8101" },
|
|
{ .compatible = "si,sia8108" },
|
|
{ .compatible = "si,sia8109" },
|
|
{ .compatible = "si,sia8152" },
|
|
{ .compatible = "si,sia8152s" },
|
|
{ .compatible = "si,sia8159" },
|
|
{ .compatible = "si,sia81x9" },
|
|
{ .compatible = "si,sia8152x" },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, si_sia81xx_dt_match);
|
|
#endif
|
|
|
|
static struct platform_driver si_sia81xx_dev_driver = {
|
|
.probe = sia81xx_probe,
|
|
.remove = sia81xx_remove,
|
|
.shutdown = sia81xx_shutdown,
|
|
.driver = {
|
|
.name = SIA81XX_NAME,
|
|
.owner = THIS_MODULE,
|
|
.pm = &si_sia81xx_pm_ops,
|
|
#ifdef CONFIG_OF
|
|
.of_match_table = si_sia81xx_dt_match,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
/********************************************************************
|
|
* end - sia81xx dev driver
|
|
********************************************************************/
|
|
|
|
static int __init sia81xx_pa_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
pr_info("[ info][%s] %s: sia81xx driver version : %s \r\n",
|
|
LOG_FLAG, __func__, SIA81XX_DRIVER_VERSION);
|
|
|
|
sia81xx_timer_task_init();
|
|
|
|
//init algorithm lib communication interface
|
|
if(NULL != tuning_if_opt.init) {
|
|
tuning_if_opt.init();
|
|
}
|
|
|
|
//init auto set vdd funtion
|
|
sia81xx_set_vdd_init();
|
|
|
|
//init online tuning funtion
|
|
#ifdef SIA81XX_TUNING
|
|
sia81xx_sock_init();
|
|
#endif
|
|
|
|
ret = platform_driver_register(&si_sia81xx_dev_driver);
|
|
if (ret) {
|
|
pr_err("[ err][%s] %s: si_sia81xx_dev error, ret = %d !!! \r\n",
|
|
LOG_FLAG, __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = i2c_add_driver(&si_sia81xx_i2c_driver);
|
|
if (ret) {
|
|
pr_err("[ err][%s] %s: i2c_add_driver error, ret = %d !!! \r\n",
|
|
LOG_FLAG, __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit sia81xx_pa_exit(void)
|
|
{
|
|
pr_info("[ info][%s] %s: running \r\n", LOG_FLAG, __func__);
|
|
|
|
//exit online tuning funtion
|
|
#ifdef SIA81XX_TUNING
|
|
sia81xx_sock_exit();
|
|
#endif
|
|
|
|
//exit auto set vdd funtion
|
|
sia81xx_set_vdd_exit();
|
|
|
|
//exit algorithm lib communication interface
|
|
if(NULL != tuning_if_opt.exit) {
|
|
tuning_if_opt.exit();
|
|
}
|
|
|
|
sia81xx_timer_task_exit();
|
|
|
|
i2c_del_driver(&si_sia81xx_i2c_driver);
|
|
|
|
platform_driver_unregister(&si_sia81xx_dev_driver);
|
|
}
|
|
|
|
module_init(sia81xx_pa_init);
|
|
module_exit(sia81xx_pa_exit);
|
|
|
|
MODULE_AUTHOR("yun shi <yun.shi@si-in.com>");
|
|
MODULE_DESCRIPTION("SI-IN SIA81xx ASoC driver");
|
|
MODULE_LICENSE("GPL");
|
|
|