1978 lines
48 KiB
C
1978 lines
48 KiB
C
|
/*
|
||
|
* aw8896.c aw8896 codec module
|
||
|
*
|
||
|
* Version: v1.0.16
|
||
|
*
|
||
|
* Copyright (c) 2017 AWINIC Technology CO., LTD
|
||
|
*
|
||
|
* Author: Nick Li <liweilei@awinic.com.cn>
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify it
|
||
|
* under the terms of the GNU General Public License as published by the
|
||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||
|
* option) any later version.
|
||
|
*/
|
||
|
#define DEBUG
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/i2c.h>
|
||
|
#include <sound/core.h>
|
||
|
#include <sound/pcm.h>
|
||
|
#include <sound/pcm_params.h>
|
||
|
#include <sound/soc.h>
|
||
|
#include <linux/of_gpio.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/device.h>
|
||
|
#include <linux/firmware.h>
|
||
|
#include <linux/i2c.h>
|
||
|
#include <linux/debugfs.h>
|
||
|
#include <linux/version.h>
|
||
|
#include <linux/input.h>
|
||
|
#include <sound/tlv.h>
|
||
|
#include <linux/version.h>
|
||
|
#include <sound/control.h>
|
||
|
#include "aw8896.h"
|
||
|
#include "aw8896_reg.h"
|
||
|
|
||
|
/******************************************************
|
||
|
*
|
||
|
* Marco
|
||
|
*
|
||
|
******************************************************/
|
||
|
#define AW8896_I2C_NAME "aw889x_smartpa"
|
||
|
|
||
|
#define AW8896_VERSION "v1.0.16"
|
||
|
|
||
|
#define AW8896_RATES SNDRV_PCM_RATE_8000_48000
|
||
|
#define AW8896_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
|
||
|
SNDRV_PCM_FMTBIT_S24_LE | \
|
||
|
SNDRV_PCM_FMTBIT_S32_LE)
|
||
|
|
||
|
#define AWINIC_I2C_REGMAP
|
||
|
|
||
|
#define AW_I2C_RETRIES 5
|
||
|
#define AW_I2C_RETRY_DELAY 5 /* 5ms */
|
||
|
#define AW_READ_CHIPID_RETRIES 5
|
||
|
#define AW_READ_CHIPID_RETRY_DELAY_US 5000
|
||
|
|
||
|
#define AW8896_MAX_DSP_START_TRY_COUNT 20
|
||
|
|
||
|
|
||
|
static int aw8896_spk_control;
|
||
|
static int aw8896_rcv_control;
|
||
|
|
||
|
#define AW8896_FW_NAME_MAX 64
|
||
|
#define AW8896_MAX_FIRMWARE_LOAD_CNT 20
|
||
|
static char *aw8896_reg_name = "aw8896_reg.bin";
|
||
|
static char *aw8896_fw_name = "aw8896_fw_e.bin";
|
||
|
|
||
|
/******************************************************
|
||
|
*
|
||
|
* aw882xx distinguish between codecs and components by version
|
||
|
*
|
||
|
******************************************************/
|
||
|
#ifdef AW_KERNEL_VER_OVER_4_19_1
|
||
|
static const struct aw_componet_codec_ops aw_componet_codec_ops = {
|
||
|
.aw_snd_soc_kcontrol_codec = snd_soc_kcontrol_component,
|
||
|
.aw_snd_soc_codec_get_drvdata = snd_soc_component_get_drvdata,
|
||
|
.aw_snd_soc_add_codec_controls = snd_soc_add_component_controls,
|
||
|
.aw_snd_soc_unregister_codec = snd_soc_unregister_component,
|
||
|
.aw_snd_soc_register_codec = snd_soc_register_component,
|
||
|
};
|
||
|
#else
|
||
|
static const struct aw_componet_codec_ops aw_componet_codec_ops = {
|
||
|
.aw_snd_soc_kcontrol_codec = snd_soc_kcontrol_codec,
|
||
|
.aw_snd_soc_codec_get_drvdata = snd_soc_codec_get_drvdata,
|
||
|
.aw_snd_soc_add_codec_controls = snd_soc_add_codec_controls,
|
||
|
.aw_snd_soc_unregister_codec = snd_soc_unregister_codec,
|
||
|
.aw_snd_soc_register_codec = snd_soc_register_codec,
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static aw_snd_soc_codec_t *aw_get_codec(struct snd_soc_dai *dai)
|
||
|
{
|
||
|
#ifdef AW_KERNEL_VER_OVER_4_19_1
|
||
|
return dai->component;
|
||
|
#else
|
||
|
return dai->codec;
|
||
|
#endif
|
||
|
}
|
||
|
static unsigned char aw8896_dsp_fw_data[AW8896_DSP_FW_SIZE] = {0};
|
||
|
static int aw8896_i2c_write(struct aw8896 *aw8896,
|
||
|
unsigned char reg_addr, unsigned int reg_data);
|
||
|
static int aw8896_i2c_read(struct aw8896 *aw8896,
|
||
|
unsigned char reg_addr, unsigned int *reg_data);
|
||
|
|
||
|
static void aw8896_interrupt_setup(struct aw8896 *aw8896);
|
||
|
static void aw8896_interrupt_clear(struct aw8896 *aw8896);
|
||
|
|
||
|
/******************************************************
|
||
|
*
|
||
|
* aw8896 i2c write/read
|
||
|
*
|
||
|
******************************************************/
|
||
|
#ifndef AWINIC_I2C_REGMAP
|
||
|
static int i2c_write(struct aw8896 *aw8896,
|
||
|
unsigned char addr, unsigned int reg_data)
|
||
|
{
|
||
|
int ret;
|
||
|
u8 wbuf[512] = {0};
|
||
|
|
||
|
struct i2c_msg msgs[] = {
|
||
|
{
|
||
|
.addr = aw8896->i2c->addr,
|
||
|
.flags = 0,
|
||
|
.len = 3,
|
||
|
.buf = wbuf,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
wbuf[0] = addr;
|
||
|
wbuf[1] = (unsigned char)((reg_data & 0xff00)>>8);
|
||
|
wbuf[2] = (unsigned char)(reg_data & 0x00ff);
|
||
|
|
||
|
ret = i2c_transfer(aw8896->i2c->adapter, msgs, 1);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s: i2c write error: %d\n", __func__, ret);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int i2c_read(struct aw8896 *aw8896,
|
||
|
unsigned char addr, unsigned int *reg_data)
|
||
|
{
|
||
|
int ret;
|
||
|
unsigned char rbuf[512] = {0};
|
||
|
unsigned int get_data;
|
||
|
|
||
|
struct i2c_msg msgs[] = {
|
||
|
{
|
||
|
.addr = aw8896->i2c->addr,
|
||
|
.flags = 0,
|
||
|
.len = 1,
|
||
|
.buf = &addr,
|
||
|
},
|
||
|
{
|
||
|
.addr = aw8896->i2c->addr,
|
||
|
.flags = I2C_M_RD,
|
||
|
.len = 2,
|
||
|
.buf = rbuf,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
ret = i2c_transfer(aw8896->i2c->adapter, msgs, 2);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s: i2c read error: %d\n", __func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
get_data = (unsigned int)(rbuf[0] & 0x00ff);
|
||
|
get_data <<= 8;
|
||
|
get_data |= (unsigned int)rbuf[1];
|
||
|
|
||
|
*reg_data = get_data;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static int aw8896_i2c_write(struct aw8896 *aw8896,
|
||
|
unsigned char reg_addr, unsigned int reg_data)
|
||
|
{
|
||
|
int ret = -1;
|
||
|
unsigned char cnt = 0;
|
||
|
|
||
|
while (cnt < AW_I2C_RETRIES) {
|
||
|
#ifdef AWINIC_I2C_REGMAP
|
||
|
ret = regmap_write(aw8896->regmap, reg_addr, reg_data);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s: regmap_write cnt=%d error=%d\n",
|
||
|
__func__, cnt, ret);
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
#else
|
||
|
ret = i2c_write(aw8896, reg_addr, reg_data);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s: i2c_write cnt=%d error=%d\n",
|
||
|
__func__, cnt, ret);
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
#endif
|
||
|
cnt++;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int aw8896_i2c_read(struct aw8896 *aw8896,
|
||
|
unsigned char reg_addr, unsigned int *reg_data)
|
||
|
{
|
||
|
int ret = -1;
|
||
|
unsigned char cnt = 0;
|
||
|
|
||
|
while (cnt < AW_I2C_RETRIES) {
|
||
|
#ifdef AWINIC_I2C_REGMAP
|
||
|
ret = regmap_read(aw8896->regmap, reg_addr, reg_data);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s: regmap_read cnt=%d error=%d\n",
|
||
|
__func__, cnt, ret);
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
#else
|
||
|
ret = i2c_read(aw8896, reg_addr, reg_data);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s: i2c_read cnt=%d error=%d\n",
|
||
|
__func__, cnt, ret);
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
#endif
|
||
|
cnt++;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int aw8896_i2c_writes(struct aw8896 *aw8896,
|
||
|
unsigned char reg_addr,
|
||
|
unsigned char *buf,
|
||
|
unsigned int len)
|
||
|
{
|
||
|
int ret = -1;
|
||
|
unsigned char *data;
|
||
|
|
||
|
data = kmalloc(len+1, GFP_KERNEL);
|
||
|
if (data == NULL)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
data[0] = reg_addr;
|
||
|
memcpy(&data[1], buf, len);
|
||
|
|
||
|
ret = i2c_master_send(aw8896->i2c, data, len+1);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s: i2c send error\n", __func__);
|
||
|
|
||
|
kfree(data);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
/******************************************************
|
||
|
*
|
||
|
* aw8896 control
|
||
|
*
|
||
|
******************************************************/
|
||
|
static bool aw8896_is_hmute(struct aw8896 *aw8896)
|
||
|
{
|
||
|
unsigned int reg_val = 0;
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_PWMCTRL, ®_val);
|
||
|
return (reg_val & (AW8896_BIT_PWMCTRL_HMUTE)) ? true : false;
|
||
|
}
|
||
|
|
||
|
static void aw8896_run_mute(struct aw8896 *aw8896, bool mute)
|
||
|
{
|
||
|
unsigned int reg_val = 0;
|
||
|
unsigned int status_reg_val = 0;
|
||
|
|
||
|
pr_debug("%s enter\n", __func__);
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_PWMCTRL, ®_val);
|
||
|
if (mute)
|
||
|
reg_val |= AW8896_BIT_PWMCTRL_HMUTE;
|
||
|
else
|
||
|
reg_val &= (~AW8896_BIT_PWMCTRL_HMUTE);
|
||
|
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_PWMCTRL, reg_val);
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSST, &status_reg_val);
|
||
|
|
||
|
pr_debug("%s System Status Register[%x] = 0x%x\n",
|
||
|
__func__, AW8896_REG_SYSST, status_reg_val);
|
||
|
|
||
|
}
|
||
|
|
||
|
static void aw8896_run_pwd(struct aw8896 *aw8896, bool pwd)
|
||
|
{
|
||
|
unsigned int reg_val = 0;
|
||
|
|
||
|
pr_debug("%s enter\n", __func__);
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSCTRL, ®_val);
|
||
|
if (pwd)
|
||
|
reg_val |= AW8896_BIT_SYSCTRL_PWDN;
|
||
|
else
|
||
|
reg_val &= (~AW8896_BIT_SYSCTRL_PWDN);
|
||
|
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_SYSCTRL, reg_val);
|
||
|
}
|
||
|
|
||
|
static void aw8896_run_amp_off(struct aw8896 *aw8896, bool apm_off)
|
||
|
{
|
||
|
unsigned int reg_val = 0;
|
||
|
|
||
|
pr_debug("%s enter\n", __func__);
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSCTRL, ®_val);
|
||
|
if (apm_off)
|
||
|
reg_val |= AW8896_BIT_SYSCTRL_BSTPD;
|
||
|
else
|
||
|
reg_val &= (~AW8896_BIT_SYSCTRL_BSTPD);
|
||
|
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_SYSCTRL, reg_val);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void aw8896_dsp_enable(struct aw8896 *aw8896, bool dsp)
|
||
|
{
|
||
|
unsigned int reg_val = 0;
|
||
|
|
||
|
pr_debug("%s enter\n", __func__);
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSCTRL, ®_val);
|
||
|
if (dsp)
|
||
|
reg_val &= (~AW8896_BIT_SYSCTRL_DSPBY);
|
||
|
else
|
||
|
reg_val |= AW8896_BIT_SYSCTRL_DSPBY;
|
||
|
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_SYSCTRL, reg_val);
|
||
|
}
|
||
|
|
||
|
static int aw8896_get_iis_status(struct aw8896 *aw8896)
|
||
|
{
|
||
|
int ret = -1;
|
||
|
unsigned int reg_val = 0;
|
||
|
|
||
|
pr_debug("%s enter\n", __func__);
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSST, ®_val);
|
||
|
if (reg_val & AW8896_BIT_SYSST_PLLS)
|
||
|
ret = 0;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int aw8896_get_dsp_status(struct aw8896 *aw8896)
|
||
|
{
|
||
|
int ret = -1;
|
||
|
int retry = 50;
|
||
|
unsigned int reg_val = 0;
|
||
|
|
||
|
pr_debug("%s enter\n", __func__);
|
||
|
while (retry-- > 0) {
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_WDT, ®_val);
|
||
|
if (reg_val)
|
||
|
return 0;
|
||
|
usleep_range(1000, 2000);
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void aw8896_spk_rcv_mode(struct aw8896 *aw8896)
|
||
|
{
|
||
|
unsigned int reg_val = 0;
|
||
|
|
||
|
pr_debug("%s spk_rcv=%d\n", __func__, aw8896->spk_rcv_mode);
|
||
|
|
||
|
if (aw8896->spk_rcv_mode == AW8896_SPEAKER_MODE) {
|
||
|
/* RFB IDAC = 6V */
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_AMPDBG1, ®_val);
|
||
|
reg_val |= AW8896_BIT_AMPDBG1_OPD;
|
||
|
reg_val &= (~AW8896_BIT_AMPDBG1_IPWM_20UA);
|
||
|
reg_val &= (~AW8896_BIT_AMPDBG1_RFB_MASK);
|
||
|
reg_val |= 0x0002;
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_AMPDBG1, reg_val);
|
||
|
|
||
|
/* Speaker Mode */
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSCTRL, ®_val);
|
||
|
reg_val &= (~AW8896_BIT_SYSCTRL_RCV_MODE);
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_SYSCTRL, reg_val);
|
||
|
|
||
|
} else if (aw8896->spk_rcv_mode == AW8896_RECEIVER_MODE) {
|
||
|
/* RFB IDAC = 4V */
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_AMPDBG1, ®_val);
|
||
|
reg_val |= AW8896_BIT_AMPDBG1_OPD;
|
||
|
reg_val |= (AW8896_BIT_AMPDBG1_IPWM_20UA);
|
||
|
reg_val &= (~AW8896_BIT_AMPDBG1_RFB_MASK);
|
||
|
reg_val |= 0x000B;
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_AMPDBG1, reg_val);
|
||
|
|
||
|
/* Receiver Mode */
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSCTRL, ®_val);
|
||
|
reg_val |= AW8896_BIT_SYSCTRL_RCV_MODE;
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_SYSCTRL, reg_val);
|
||
|
} else {
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int aw8896_get_sysint(struct aw8896 *aw8896, uint16_t *sysint)
|
||
|
{
|
||
|
int ret = -1;
|
||
|
unsigned int reg_val = 0;
|
||
|
|
||
|
ret = aw8896_i2c_read(aw8896, AW8896_REG_SYSINT, ®_val);
|
||
|
if (ret < 0)
|
||
|
pr_info("%s: read sysint fail, ret=%d\n",
|
||
|
__func__, ret);
|
||
|
else
|
||
|
*sysint = reg_val;
|
||
|
return ret;
|
||
|
}
|
||
|
static int aw8896_set_intmask(struct aw8896 *aw8896, bool flag)
|
||
|
{
|
||
|
if (flag)
|
||
|
aw8896_interrupt_setup(aw8896);
|
||
|
else
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_SYSINTM,
|
||
|
AW8896_BIT_SYSINTM_MASK);
|
||
|
return 0;
|
||
|
}
|
||
|
static void aw8896_start(struct aw8896 *aw8896)
|
||
|
{
|
||
|
int ret = -1;
|
||
|
uint16_t sysint = 0;
|
||
|
int i;
|
||
|
|
||
|
pr_debug("%s enter\n", __func__);
|
||
|
|
||
|
aw8896_run_pwd(aw8896, false);
|
||
|
for (i = 0; i < AW8896_MAX_DSP_START_TRY_COUNT; i++) {
|
||
|
ret = aw8896_get_iis_status(aw8896);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s: get no iis signal, ret=%d\n",
|
||
|
__func__, ret);
|
||
|
usleep_range(2000, 2000 + 20);
|
||
|
if (i == AW8896_MAX_DSP_START_TRY_COUNT - 1)
|
||
|
break;
|
||
|
} else {
|
||
|
aw8896_dsp_enable(aw8896, true);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
aw8896_run_mute(aw8896, false);
|
||
|
|
||
|
ret = aw8896_get_sysint(aw8896, &sysint);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s: get_sysint fail, ret=%d\n",
|
||
|
__func__, ret);
|
||
|
else
|
||
|
pr_info("%s: get_sysint=0x%04x\n",
|
||
|
__func__, sysint);
|
||
|
|
||
|
ret = aw8896_get_sysint(aw8896, &sysint);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s: get_sysint fail, ret=%d\n",
|
||
|
__func__, ret);
|
||
|
else
|
||
|
pr_info("%s: get_sysint=0x%04x\n",
|
||
|
__func__, sysint);
|
||
|
|
||
|
aw8896_set_intmask(aw8896, true);
|
||
|
}
|
||
|
|
||
|
static void aw8896_stop(struct aw8896 *aw8896)
|
||
|
{
|
||
|
int ret = -1;
|
||
|
uint16_t sysint = 0;
|
||
|
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
|
||
|
aw8896_run_mute(aw8896, true);
|
||
|
aw8896_set_intmask(aw8896, false);
|
||
|
ret = aw8896_get_sysint(aw8896, &sysint);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s: get_sysint fail, ret=%d\n",
|
||
|
__func__, ret);
|
||
|
else
|
||
|
pr_info("%s: get_sysint=0x%04x\n",
|
||
|
__func__, sysint);
|
||
|
aw8896_dsp_enable(aw8896, false);
|
||
|
usleep_range(1000, 2000);
|
||
|
aw8896_run_pwd(aw8896, true);
|
||
|
}
|
||
|
|
||
|
static void aw8896_dsp_container_update(struct aw8896 *aw8896,
|
||
|
struct aw8896_container *aw8896_cont,
|
||
|
int base)
|
||
|
{
|
||
|
int i = 0;
|
||
|
#ifdef AWINIC_DSP_I2C_WRITES
|
||
|
unsigned char tmp_val = 0;
|
||
|
unsigned int tmp_len = 0;
|
||
|
#else
|
||
|
int tmp_val = 0;
|
||
|
int addr = 0;
|
||
|
#endif
|
||
|
|
||
|
pr_debug("%s enter\n", __func__);
|
||
|
|
||
|
#ifdef AWINIC_DSP_I2C_WRITES
|
||
|
/* i2c writes */
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_DSPMADD, base);
|
||
|
for (i = 0; i < aw8896_cont->len; i += 2) {
|
||
|
tmp_val = aw8896_cont->data[i+0];
|
||
|
aw8896_cont->data[i+0] = aw8896_cont->data[i+1];
|
||
|
aw8896_cont->data[i+1] = tmp_val;
|
||
|
}
|
||
|
for (i = 0; i < aw8896_cont->len; i += MAX_RAM_WRITE_BYTE_SIZE) {
|
||
|
if ((aw8896_cont->len - i) < MAX_RAM_WRITE_BYTE_SIZE)
|
||
|
tmp_len = aw8896_cont->len - i;
|
||
|
else
|
||
|
tmp_len = MAX_RAM_WRITE_BYTE_SIZE;
|
||
|
|
||
|
aw8896_i2c_writes(aw8896, AW8896_REG_DSPMDAT,
|
||
|
&aw8896_cont->data[i], tmp_len);
|
||
|
}
|
||
|
#else
|
||
|
/* i2c write */
|
||
|
for (i = 0; i < aw8896_cont->len; i += 2) {
|
||
|
tmp_val = (aw8896_cont->data[i+1]<<8) + aw8896_cont->data[i+0];
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_DSPMADD, base+addr);
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_DSPMDAT, tmp_val);
|
||
|
addr++;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
pr_debug("%s exit\n", __func__);
|
||
|
}
|
||
|
|
||
|
static int aw8896_fw_loaded(const struct firmware *cont, struct aw8896 *aw8896)
|
||
|
{
|
||
|
struct aw8896_container *aw8896_fw;
|
||
|
/* int i; */
|
||
|
int ret = -1;
|
||
|
|
||
|
pr_debug("%s enter\n", __func__);
|
||
|
|
||
|
aw8896->dsp_fw_state = AW8896_DSP_FW_FAIL;
|
||
|
|
||
|
if (!cont) {
|
||
|
pr_err("%s: failed to read %s\n", __func__, aw8896_fw_name);
|
||
|
release_firmware(cont);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
pr_info("%s: loaded %s - size: %zu\n", __func__, aw8896_fw_name,
|
||
|
cont ? cont->size : 0);
|
||
|
if (cont->size > AW8896_DSP_FW_SIZE) {
|
||
|
pr_err("%s: Errof:Size exceeds the limit of 512\n", __func__);
|
||
|
release_firmware(cont);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
aw8896_fw = kzalloc(cont->size+sizeof(int), GFP_KERNEL);
|
||
|
if (!aw8896_fw) {
|
||
|
release_firmware(cont);
|
||
|
pr_err("%s: Error allocating memory\n", __func__);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
aw8896_fw->len = cont->size;
|
||
|
memcpy(aw8896_fw->data, cont->data, cont->size);
|
||
|
|
||
|
aw8896_dsp_container_update(aw8896, aw8896_fw,
|
||
|
AW8896_DSP_FW_BASE);
|
||
|
memcpy(aw8896_fw->data, cont->data, cont->size);
|
||
|
aw8896_dsp_container_update(aw8896, aw8896_fw,
|
||
|
AW8896_DSP_FW_BACKUP_BASE);
|
||
|
memcpy(aw8896_dsp_fw_data, cont->data, cont->size);
|
||
|
release_firmware(cont);
|
||
|
|
||
|
aw8896->dsp_fw_len = aw8896_fw->len;
|
||
|
kfree(aw8896_fw);
|
||
|
|
||
|
pr_info("%s: fw update complete\n", __func__);
|
||
|
|
||
|
aw8896_dsp_enable(aw8896, true);
|
||
|
|
||
|
usleep_range(1000, 2000);
|
||
|
|
||
|
ret = aw8896_get_dsp_status(aw8896);
|
||
|
if (ret) {
|
||
|
aw8896->init = 0;
|
||
|
aw8896_dsp_enable(aw8896, false);
|
||
|
aw8896_run_mute(aw8896, true);
|
||
|
pr_info("%s: dsp working wdt, dsp fw update failed\n",
|
||
|
__func__);
|
||
|
} else {
|
||
|
if (aw8896->call_in_kctl == 0) {
|
||
|
aw8896_spk_rcv_mode(aw8896);
|
||
|
aw8896_start(aw8896);
|
||
|
}
|
||
|
if (!(aw8896->flags & AW8896_FLAG_SKIP_INTERRUPTS)) {
|
||
|
aw8896_interrupt_clear(aw8896);
|
||
|
aw8896_interrupt_setup(aw8896);
|
||
|
}
|
||
|
aw8896->init = 1;
|
||
|
aw8896->dsp_fw_state = AW8896_DSP_FW_OK;
|
||
|
pr_info("%s: init ok\n", __func__);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int aw8896_load_fw(struct aw8896 *aw8896)
|
||
|
{
|
||
|
const struct firmware *cont = NULL;
|
||
|
int ret;
|
||
|
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
|
||
|
if (aw8896->dsp_fw_state == AW8896_DSP_FW_OK) {
|
||
|
pr_info("%s: dsp fw ok\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
aw8896->dsp_fw_state = AW8896_DSP_FW_PENDING;
|
||
|
|
||
|
aw8896_run_mute(aw8896, true);
|
||
|
aw8896_dsp_enable(aw8896, false);
|
||
|
|
||
|
ret = request_firmware(&cont, aw8896_fw_name, aw8896->dev);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s: failed to read %s\n", __func__, aw8896_fw_name);
|
||
|
release_firmware(cont);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
return aw8896_fw_loaded(cont, aw8896);
|
||
|
}
|
||
|
|
||
|
static void aw8896_reg_container_update(struct aw8896 *aw8896,
|
||
|
struct aw8896_container *aw8896_cont)
|
||
|
{
|
||
|
int i = 0;
|
||
|
int reg_addr = 0;
|
||
|
int reg_val = 0;
|
||
|
|
||
|
pr_debug("%s enter\n", __func__);
|
||
|
|
||
|
for (i = 0; i < aw8896_cont->len; i += 4) {
|
||
|
reg_addr = (aw8896_cont->data[i+1]<<8) + aw8896_cont->data[i+0];
|
||
|
reg_val = (aw8896_cont->data[i+3]<<8) + aw8896_cont->data[i+2];
|
||
|
pr_debug("%s: reg=0x%04x, val = 0x%04x\n",
|
||
|
__func__, reg_addr, reg_val);
|
||
|
aw8896_i2c_write(aw8896, (unsigned char)reg_addr,
|
||
|
(unsigned int)reg_val);
|
||
|
}
|
||
|
|
||
|
pr_debug("%s exit\n", __func__);
|
||
|
}
|
||
|
|
||
|
static int aw8896_reg_loaded(const struct firmware *cont, struct aw8896 *aw8896)
|
||
|
{
|
||
|
struct aw8896_container *aw8896_reg = NULL;
|
||
|
int ret = -1;
|
||
|
int i = 0;
|
||
|
int iis_check_max = 5;
|
||
|
unsigned int reg_val = 0;
|
||
|
|
||
|
if (!cont) {
|
||
|
pr_err("%s: failed to read %s\n", __func__, aw8896_reg_name);
|
||
|
release_firmware(cont);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
pr_info("%s: loaded %s - size: %zu\n", __func__, aw8896_reg_name,
|
||
|
cont ? cont->size : 0);
|
||
|
|
||
|
aw8896_reg = kzalloc(cont->size+sizeof(int), GFP_KERNEL);
|
||
|
if (!aw8896_reg) {
|
||
|
release_firmware(cont);
|
||
|
pr_err("%s: error allocating memory\n", __func__);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
aw8896_reg->len = cont->size;
|
||
|
memcpy(aw8896_reg->data, cont->data, cont->size);
|
||
|
release_firmware(cont);
|
||
|
|
||
|
aw8896_reg_container_update(aw8896, aw8896_reg);
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_DBGCTRL, ®_val);
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_DBGCTRL,
|
||
|
reg_val | AW8896_BIT_DBGCTRL_DISNCKRST);
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_I2SCTRL, ®_val);
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_I2SCTRL,
|
||
|
reg_val & (~AW8896_BIT_I2SCTRL_INPLEV));
|
||
|
|
||
|
kfree(aw8896_reg);
|
||
|
|
||
|
for (i = 0; i < iis_check_max; i++) {
|
||
|
ret = aw8896_get_iis_status(aw8896);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s: get no iis signal, ret=%d\n",
|
||
|
__func__, ret);
|
||
|
} else {
|
||
|
usleep_range(5000, 6000);
|
||
|
ret = aw8896_load_fw(aw8896);
|
||
|
if (ret) {
|
||
|
pr_err("%s: fw loading requested failed: %d\n",
|
||
|
__func__, ret);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
usleep_range(2000, 3000);
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int aw8896_load_reg(struct aw8896 *aw8896)
|
||
|
{
|
||
|
const struct firmware *cont = NULL;
|
||
|
int ret;
|
||
|
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
|
||
|
ret = request_firmware(&cont, aw8896_reg_name, aw8896->dev);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s: failed to read %s\n", __func__, aw8896_reg_name);
|
||
|
release_firmware(cont);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
return aw8896_reg_loaded(cont, aw8896);
|
||
|
}
|
||
|
|
||
|
static void aw8896_cold_start(struct aw8896 *aw8896)
|
||
|
{
|
||
|
int ret = -1;
|
||
|
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
|
||
|
ret = aw8896_load_reg(aw8896);
|
||
|
if (ret)
|
||
|
pr_err("%s: reg/fw loading requested failed: %d\n",
|
||
|
__func__, ret);
|
||
|
}
|
||
|
|
||
|
static void aw8896_smartpa_cfg(struct aw8896 *aw8896, bool flag)
|
||
|
{
|
||
|
pr_info("%s, flag = %d\n", __func__, flag);
|
||
|
|
||
|
if (flag == true) {
|
||
|
if (aw8896->init == 0) {
|
||
|
pr_info("%s, init = %d\n", __func__, aw8896->init);
|
||
|
aw8896_cold_start(aw8896);
|
||
|
} else {
|
||
|
aw8896_spk_rcv_mode(aw8896);
|
||
|
aw8896_start(aw8896);
|
||
|
}
|
||
|
} else {
|
||
|
aw8896_stop(aw8896);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/******************************************************
|
||
|
*
|
||
|
* kcontrol
|
||
|
*
|
||
|
******************************************************/
|
||
|
static const char *const spk_function[] = { "Off", "On" };
|
||
|
static const char *const rcv_function[] = { "Off", "On" };
|
||
|
static const char *const hmute_function[] = { "Off", "On" };
|
||
|
static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 50, 0);
|
||
|
|
||
|
struct soc_mixer_control aw8896_mixer = {
|
||
|
.reg = AW8896_REG_DSPCFG,
|
||
|
.shift = AW8896_VOL_REG_SHIFT,
|
||
|
.max = AW8896_VOLUME_MAX,
|
||
|
.min = AW8896_VOLUME_MIN,
|
||
|
};
|
||
|
|
||
|
static int aw8896_volume_info(struct snd_kcontrol *kcontrol,
|
||
|
struct snd_ctl_elem_info *uinfo)
|
||
|
{
|
||
|
struct soc_mixer_control *mc =
|
||
|
(struct soc_mixer_control *) kcontrol->private_value;
|
||
|
|
||
|
/* set kcontrol info */
|
||
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||
|
uinfo->count = 1;
|
||
|
uinfo->value.integer.min = 0;
|
||
|
uinfo->value.integer.max = mc->max - mc->min;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int aw8896_volume_get(struct snd_kcontrol *kcontrol,
|
||
|
struct snd_ctl_elem_value *ucontrol)
|
||
|
{
|
||
|
aw_snd_soc_codec_t *codec =
|
||
|
aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol);
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
int value;
|
||
|
struct soc_mixer_control *mc =
|
||
|
(struct soc_mixer_control *) kcontrol->private_value;
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_DSPCFG, &value);
|
||
|
ucontrol->value.integer.value[0] = (value >> mc->shift)
|
||
|
&(~AW8896_BIT_DSPCFG_VOL_MASK);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int aw8896_volume_put(struct snd_kcontrol *kcontrol,
|
||
|
struct snd_ctl_elem_value *ucontrol)
|
||
|
{
|
||
|
struct soc_mixer_control *mc =
|
||
|
(struct soc_mixer_control *) kcontrol->private_value;
|
||
|
aw_snd_soc_codec_t *codec =
|
||
|
aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol);
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
unsigned int value, reg_value;
|
||
|
|
||
|
/* value is right */
|
||
|
value = ucontrol->value.integer.value[0];
|
||
|
if (value > (mc->max-mc->min) || value < 0) {
|
||
|
pr_err("%s:value over range\n", __func__);
|
||
|
return -EPERM;
|
||
|
}
|
||
|
|
||
|
/* smartpa have clk */
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSST, ®_value);
|
||
|
if (!(reg_value&AW8896_BIT_SYSST_PLLS)) {
|
||
|
pr_err("%s: NO I2S CLK ,cat not write reg\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
/* cal real value */
|
||
|
value = value << mc->shift&AW8896_BIT_DSPCFG_VOL_MASK;
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_DSPCFG, ®_value);
|
||
|
value = value | (reg_value&0x00ff);
|
||
|
|
||
|
/* write value */
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSCTRL, ®_value);
|
||
|
if (reg_value&AW8896_BIT_SYSCTRL_DSPBY) {
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_DSPCFG, value);
|
||
|
} else {
|
||
|
aw8896_dsp_enable(aw8896, false);
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_DSPCFG, value);
|
||
|
aw8896_dsp_enable(aw8896, true);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct snd_kcontrol_new aw8896_volume = {
|
||
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||
|
.name = "aw8896_rx_volume",
|
||
|
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
|
||
|
SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||
|
.tlv.p = (digital_gain),
|
||
|
.info = aw8896_volume_info,
|
||
|
.get = aw8896_volume_get,
|
||
|
.put = aw8896_volume_put,
|
||
|
.private_value = (unsigned long)&aw8896_mixer,
|
||
|
};
|
||
|
|
||
|
static int aw8896_spk_get(struct snd_kcontrol *kcontrol,
|
||
|
struct snd_ctl_elem_value *ucontrol)
|
||
|
{
|
||
|
pr_debug("%s: aw8896_spk_control=%d\n", __func__, aw8896_spk_control);
|
||
|
ucontrol->value.integer.value[0] = aw8896_spk_control;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int aw8896_spk_set(struct snd_kcontrol *kcontrol,
|
||
|
struct snd_ctl_elem_value *ucontrol)
|
||
|
{
|
||
|
aw_snd_soc_codec_t *codec =
|
||
|
aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol);
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
|
||
|
pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n",
|
||
|
__func__, ucontrol->value.integer.value[0]);
|
||
|
if (ucontrol->value.integer.value[0] == aw8896_spk_control)
|
||
|
return 1;
|
||
|
|
||
|
aw8896_spk_control = ucontrol->value.integer.value[0];
|
||
|
|
||
|
aw8896->spk_rcv_mode = AW8896_SPEAKER_MODE;
|
||
|
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int aw8896_rcv_get(struct snd_kcontrol *kcontrol,
|
||
|
struct snd_ctl_elem_value *ucontrol)
|
||
|
{
|
||
|
pr_debug("%s: aw8896_rcv_control=%d\n", __func__, aw8896_rcv_control);
|
||
|
ucontrol->value.integer.value[0] = aw8896_rcv_control;
|
||
|
return 0;
|
||
|
}
|
||
|
static int aw8896_rcv_set(struct snd_kcontrol *kcontrol,
|
||
|
struct snd_ctl_elem_value *ucontrol)
|
||
|
{
|
||
|
aw_snd_soc_codec_t *codec =
|
||
|
aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol);
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
|
||
|
pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n",
|
||
|
__func__, ucontrol->value.integer.value[0]);
|
||
|
if (ucontrol->value.integer.value[0] == aw8896_rcv_control)
|
||
|
return 1;
|
||
|
|
||
|
aw8896_rcv_control = ucontrol->value.integer.value[0];
|
||
|
|
||
|
aw8896->spk_rcv_mode = AW8896_RECEIVER_MODE;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int aw8896_hmute_get(struct snd_kcontrol *kcontrol,
|
||
|
struct snd_ctl_elem_value *ucontrol)
|
||
|
{
|
||
|
aw_snd_soc_codec_t *codec =
|
||
|
aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol);
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
bool is_mute = true;
|
||
|
|
||
|
is_mute = aw8896_is_hmute(aw8896);
|
||
|
ucontrol->value.integer.value[0] = is_mute ? 1 : 0;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int aw8896_hmute_set(struct snd_kcontrol *kcontrol,
|
||
|
struct snd_ctl_elem_value *ucontrol)
|
||
|
{
|
||
|
aw_snd_soc_codec_t *codec =
|
||
|
aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol);
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
bool is_mute = true;
|
||
|
int cur_state = 0;
|
||
|
|
||
|
pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n",
|
||
|
__func__, ucontrol->value.integer.value[0]);
|
||
|
|
||
|
is_mute = aw8896_is_hmute(aw8896);
|
||
|
cur_state = is_mute ? 1 : 0;
|
||
|
if (ucontrol->value.integer.value[0] == cur_state)
|
||
|
return 1;
|
||
|
|
||
|
if (is_mute) {
|
||
|
if (aw8896->init == 0) {
|
||
|
aw8896->call_in_kctl = 1;
|
||
|
aw8896_cold_start(aw8896);
|
||
|
aw8896->call_in_kctl = 0;
|
||
|
}
|
||
|
aw8896_run_amp_off(aw8896, false);
|
||
|
aw8896_run_mute(aw8896, false);
|
||
|
} else {
|
||
|
aw8896_run_mute(aw8896, true);
|
||
|
usleep_range(1000, 2000);
|
||
|
aw8896_run_amp_off(aw8896, true);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct soc_enum aw8896_snd_enum[] = {
|
||
|
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
|
||
|
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rcv_function), rcv_function),
|
||
|
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(hmute_function), hmute_function),
|
||
|
};
|
||
|
|
||
|
static struct snd_kcontrol_new aw8896_controls[] = {
|
||
|
SOC_ENUM_EXT("aw8896_speaker_switch", aw8896_snd_enum[0],
|
||
|
aw8896_spk_get, aw8896_spk_set),
|
||
|
SOC_ENUM_EXT("aw8896_receiver_switch", aw8896_snd_enum[1],
|
||
|
aw8896_rcv_get, aw8896_rcv_set),
|
||
|
SOC_ENUM_EXT("aw8896_playback_spk_mute", aw8896_snd_enum[2],
|
||
|
aw8896_hmute_get, aw8896_hmute_set),
|
||
|
};
|
||
|
|
||
|
static void aw8896_add_codec_controls(struct aw8896 *aw8896)
|
||
|
{
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
|
||
|
aw_componet_codec_ops.aw_snd_soc_add_codec_controls(aw8896->codec,
|
||
|
aw8896_controls,
|
||
|
ARRAY_SIZE(aw8896_controls));
|
||
|
|
||
|
aw_componet_codec_ops.aw_snd_soc_add_codec_controls(aw8896->codec,
|
||
|
&aw8896_volume, 1);
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************
|
||
|
*
|
||
|
* Digital Audio Interface
|
||
|
*
|
||
|
******************************************************/
|
||
|
static int aw8896_startup(struct snd_pcm_substream *substream,
|
||
|
struct snd_soc_dai *dai)
|
||
|
{
|
||
|
aw_snd_soc_codec_t *codec = aw_get_codec(dai);
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
|
||
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||
|
pr_info("%s: playback enter\n", __func__);
|
||
|
mutex_lock(&aw8896->lock);
|
||
|
aw8896_run_pwd(aw8896, false);
|
||
|
mutex_unlock(&aw8896->lock);
|
||
|
} else {
|
||
|
pr_info("%s: capture enter\n", __func__);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int aw8896_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||
|
{
|
||
|
aw_snd_soc_codec_t *codec = aw_get_codec(dai);
|
||
|
|
||
|
pr_info("%s: fmt=0x%x\n", __func__, fmt);
|
||
|
|
||
|
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||
|
case SND_SOC_DAIFMT_I2S:
|
||
|
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) !=
|
||
|
SND_SOC_DAIFMT_CBS_CFS) {
|
||
|
dev_err(codec->dev,
|
||
|
"%s: invalid codec mode\n", __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
dev_err(codec->dev, "%s: unsupported DAI format %d\n", __func__,
|
||
|
(fmt & SND_SOC_DAIFMT_FORMAT_MASK));
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int aw8896_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
||
|
int clk_id, unsigned int freq, int dir)
|
||
|
{
|
||
|
aw_snd_soc_codec_t *codec = aw_get_codec(codec_dai);
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
|
||
|
pr_info("%s: freq=%d\n", __func__, freq);
|
||
|
|
||
|
aw8896->sysclk = freq;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int aw8896_hw_params(struct snd_pcm_substream *substream,
|
||
|
struct snd_pcm_hw_params *params,
|
||
|
struct snd_soc_dai *dai)
|
||
|
{
|
||
|
aw_snd_soc_codec_t *codec = aw_get_codec(dai);
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
unsigned int rate;
|
||
|
int reg_value, tmp_value;
|
||
|
int width;
|
||
|
/* Supported */
|
||
|
|
||
|
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) {
|
||
|
pr_debug("%s: requested rate: %d, sample size: %d\n",
|
||
|
__func__, rate,
|
||
|
snd_pcm_format_width(params_format(params)));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&aw8896->lock);
|
||
|
/* get rate param */
|
||
|
aw8896->rate = rate = params_rate(params);
|
||
|
pr_debug("%s: requested rate: %d, sample size: %d\n", __func__, rate,
|
||
|
snd_pcm_format_width(params_format(params)));
|
||
|
/* match rate */
|
||
|
switch (rate) {
|
||
|
case 8000:
|
||
|
reg_value = AW8896_BIT_I2SCTRL_SR_8K;
|
||
|
break;
|
||
|
case 16000:
|
||
|
reg_value = AW8896_BIT_I2SCTRL_SR_16K;
|
||
|
break;
|
||
|
case 32000:
|
||
|
reg_value = AW8896_BIT_I2SCTRL_SR_32K;
|
||
|
break;
|
||
|
case 44100:
|
||
|
reg_value = AW8896_BIT_I2SCTRL_SR_44K;
|
||
|
break;
|
||
|
case 48000:
|
||
|
reg_value = AW8896_BIT_I2SCTRL_SR_48K;
|
||
|
break;
|
||
|
default:
|
||
|
reg_value = AW8896_BIT_I2SCTRL_SR_48K;
|
||
|
pr_err("%s: rate can not support\n", __func__);
|
||
|
break;
|
||
|
}
|
||
|
/* set chip rate */
|
||
|
if (-1 != reg_value) {
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_I2SCTRL, &tmp_value);
|
||
|
reg_value = reg_value |
|
||
|
(tmp_value & (~AW8896_BIT_I2SCTRL_SR_MASK));
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_I2SCTRL, reg_value);
|
||
|
}
|
||
|
|
||
|
/* get bit width */
|
||
|
width = params_width(params);
|
||
|
pr_debug("%s: width = %d\n", __func__, width);
|
||
|
switch (width) {
|
||
|
case 16:
|
||
|
reg_value = AW8896_BIT_I2SCTRL_FMS_16BIT;
|
||
|
break;
|
||
|
case 20:
|
||
|
reg_value = AW8896_BIT_I2SCTRL_FMS_20BIT;
|
||
|
break;
|
||
|
case 24:
|
||
|
reg_value = AW8896_BIT_I2SCTRL_FMS_24BIT;
|
||
|
break;
|
||
|
case 32:
|
||
|
reg_value = AW8896_BIT_I2SCTRL_FMS_32BIT;
|
||
|
break;
|
||
|
default:
|
||
|
reg_value = AW8896_BIT_I2SCTRL_FMS_16BIT;
|
||
|
pr_err("%s: width can not support\n", __func__);
|
||
|
break;
|
||
|
}
|
||
|
/* set width */
|
||
|
if (-1 != reg_value) {
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_I2SCTRL, &tmp_value);
|
||
|
reg_value = reg_value |
|
||
|
(tmp_value&(~AW8896_BIT_I2SCTRL_FMS_MASK));
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_I2SCTRL, reg_value);
|
||
|
}
|
||
|
mutex_unlock(&aw8896->lock);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int aw8896_mute(struct snd_soc_dai *dai, int mute, int stream)
|
||
|
{
|
||
|
aw_snd_soc_codec_t *codec = aw_get_codec(dai);
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
|
||
|
pr_info("%s: mute state=%d\n", __func__, mute);
|
||
|
|
||
|
if (!(aw8896->flags & AW8896_FLAG_DSP_START_ON_MUTE))
|
||
|
return 0;
|
||
|
|
||
|
if (stream != SNDRV_PCM_STREAM_PLAYBACK) {
|
||
|
pr_info("%s: not playback\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (mute) {
|
||
|
/* Stop DSP */
|
||
|
mutex_lock(&aw8896->lock);
|
||
|
aw8896_smartpa_cfg(aw8896, false);
|
||
|
mutex_unlock(&aw8896->lock);
|
||
|
} else {
|
||
|
/* Start DSP */
|
||
|
mutex_lock(&aw8896->lock);
|
||
|
aw8896_smartpa_cfg(aw8896, true);
|
||
|
mutex_unlock(&aw8896->lock);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void aw8896_shutdown(struct snd_pcm_substream *substream,
|
||
|
struct snd_soc_dai *dai)
|
||
|
{
|
||
|
aw_snd_soc_codec_t *codec = aw_get_codec(dai);
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
|
||
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||
|
pr_info("%s: playback shutdown\n", __func__);
|
||
|
mutex_lock(&aw8896->lock);
|
||
|
aw8896->rate = 0;
|
||
|
aw8896_run_pwd(aw8896, true);
|
||
|
mutex_unlock(&aw8896->lock);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static const struct snd_soc_dai_ops aw8896_dai_ops = {
|
||
|
.startup = aw8896_startup,
|
||
|
.set_fmt = aw8896_set_fmt,
|
||
|
.set_sysclk = aw8896_set_dai_sysclk,
|
||
|
.hw_params = aw8896_hw_params,
|
||
|
.mute_stream = aw8896_mute,
|
||
|
.shutdown = aw8896_shutdown,
|
||
|
};
|
||
|
|
||
|
static struct snd_soc_dai_driver aw8896_dai[] = {
|
||
|
{
|
||
|
.name = "aw8896-aif",
|
||
|
.id = 1,
|
||
|
.playback = {
|
||
|
.stream_name = "Speaker_Playback",
|
||
|
.channels_min = 1,
|
||
|
.channels_max = 2,
|
||
|
.rates = AW8896_RATES,
|
||
|
.formats = AW8896_FORMATS,
|
||
|
},
|
||
|
.capture = {
|
||
|
.stream_name = "Speaker_Capture",
|
||
|
.channels_min = 1,
|
||
|
.channels_max = 2,
|
||
|
.rates = AW8896_RATES,
|
||
|
.formats = AW8896_FORMATS,
|
||
|
},
|
||
|
.ops = &aw8896_dai_ops,
|
||
|
.symmetric_rates = 1,
|
||
|
.symmetric_channels = 1,
|
||
|
.symmetric_samplebits = 1,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
|
||
|
/*****************************************************
|
||
|
*
|
||
|
* codec driver
|
||
|
*
|
||
|
*****************************************************/
|
||
|
static int aw8896_probe(aw_snd_soc_codec_t *codec)
|
||
|
{
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
int ret = 0;
|
||
|
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
|
||
|
aw8896->codec = codec;
|
||
|
|
||
|
aw8896_add_codec_controls(aw8896);
|
||
|
|
||
|
if (codec->dev->of_node)
|
||
|
dev_set_name(codec->dev, "%s", "aw8896_smartpa");
|
||
|
|
||
|
pr_info("%s exit\n", __func__);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
#ifdef AW_KERNEL_VER_OVER_4_19_1
|
||
|
static void aw8896_remove(struct snd_soc_component *component)
|
||
|
{
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
}
|
||
|
#else
|
||
|
static int aw8896_remove(aw_snd_soc_codec_t *codec)
|
||
|
{
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
struct regmap *aw8896_get_regmap(struct device *dev)
|
||
|
{
|
||
|
struct aw8896 *aw8896 = dev_get_drvdata(dev);
|
||
|
|
||
|
return aw8896->regmap;
|
||
|
}
|
||
|
|
||
|
static unsigned int aw8896_codec_read(aw_snd_soc_codec_t *codec,
|
||
|
unsigned int reg)
|
||
|
{
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
unsigned int value = 0;
|
||
|
int ret;
|
||
|
|
||
|
pr_debug("%s:enter\n", __func__);
|
||
|
if (aw8896_reg_access[reg]®_RD_ACCESS) {
|
||
|
ret = aw8896_i2c_read(aw8896, reg, &value);
|
||
|
if (ret < 0) {
|
||
|
pr_debug("%s: read register failed\n", __func__);
|
||
|
return ret;
|
||
|
}
|
||
|
} else {
|
||
|
pr_debug("%s:Register 0x%x NO read access\n", __func__, reg);
|
||
|
return -EPERM;
|
||
|
}
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
static int aw8896_codec_write(aw_snd_soc_codec_t *codec,
|
||
|
unsigned int reg, unsigned int value)
|
||
|
{
|
||
|
int ret;
|
||
|
struct aw8896 *aw8896 =
|
||
|
aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec);
|
||
|
|
||
|
pr_debug("%s:enter ,reg is 0x%x value is 0x%x\n", __func__, reg, value);
|
||
|
if (aw8896_reg_access[reg]®_WR_ACCESS) {
|
||
|
ret = aw8896_i2c_write(aw8896, reg, value);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
pr_debug("%s: Register 0x%x NO write access\n", __func__, reg);
|
||
|
|
||
|
return -EPERM;
|
||
|
}
|
||
|
|
||
|
static const struct snd_soc_dapm_widget aw8896_dapm_widgets[] = {
|
||
|
SND_SOC_DAPM_DAC_E("DAC", "Speaker_Playback", SND_SOC_NOPM, 0, 0, NULL,
|
||
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
|
||
|
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
|
||
|
SND_SOC_DAPM_OUTPUT("SPK"),
|
||
|
};
|
||
|
|
||
|
static const struct snd_soc_dapm_route aw8896_audio_map[] = {
|
||
|
/* sink, control, source */
|
||
|
{"SPK", NULL, "DAC"},
|
||
|
};
|
||
|
|
||
|
#ifdef AW_KERNEL_VER_OVER_4_19_1
|
||
|
static const struct snd_soc_component_driver soc_codec_dev_aw8896 = {
|
||
|
.probe = aw8896_probe,
|
||
|
.remove = aw8896_remove,
|
||
|
.read = aw8896_codec_read,
|
||
|
.write = aw8896_codec_write,
|
||
|
.dapm_widgets = aw8896_dapm_widgets,
|
||
|
.num_dapm_widgets = ARRAY_SIZE(aw8896_dapm_widgets),
|
||
|
.dapm_routes = aw8896_audio_map,
|
||
|
.num_dapm_routes = ARRAY_SIZE(aw8896_audio_map),
|
||
|
};
|
||
|
#else
|
||
|
static const struct snd_soc_codec_driver soc_codec_dev_aw8896 = {
|
||
|
.probe = aw8896_probe,
|
||
|
.remove = aw8896_remove,
|
||
|
.read = aw8896_codec_read,
|
||
|
.write = aw8896_codec_write,
|
||
|
.component_driver = {
|
||
|
.dapm_widgets = aw8896_dapm_widgets,
|
||
|
.num_dapm_widgets = ARRAY_SIZE(aw8896_dapm_widgets),
|
||
|
.dapm_routes = aw8896_audio_map,
|
||
|
.num_dapm_routes = ARRAY_SIZE(aw8896_audio_map),
|
||
|
},
|
||
|
.reg_cache_size = AW8896_REG_MAX,
|
||
|
.reg_word_size = 2,
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
/*****************************************************
|
||
|
*
|
||
|
* regmap
|
||
|
*
|
||
|
*****************************************************/
|
||
|
bool aw8896_writeable_register(struct device *dev, unsigned int reg)
|
||
|
{
|
||
|
/* enable read access for all registers */
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
bool aw8896_readable_register(struct device *dev, unsigned int reg)
|
||
|
{
|
||
|
/* enable read access for all registers */
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
bool aw8896_volatile_register(struct device *dev, unsigned int reg)
|
||
|
{
|
||
|
/* enable read access for all registers */
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static const struct regmap_config aw8896_regmap = {
|
||
|
.reg_bits = 8,
|
||
|
.val_bits = 16,
|
||
|
|
||
|
.max_register = AW8896_MAX_REGISTER,
|
||
|
.writeable_reg = aw8896_writeable_register,
|
||
|
.readable_reg = aw8896_readable_register,
|
||
|
.volatile_reg = aw8896_volatile_register,
|
||
|
.cache_type = REGCACHE_RBTREE,
|
||
|
};
|
||
|
|
||
|
/******************************************************
|
||
|
*
|
||
|
* irq
|
||
|
*
|
||
|
******************************************************/
|
||
|
static void aw8896_interrupt_setup(struct aw8896 *aw8896)
|
||
|
{
|
||
|
unsigned int reg_val;
|
||
|
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSINTM, ®_val);
|
||
|
|
||
|
/* i2s status interrupt unmask */
|
||
|
reg_val &= (~AW8896_BIT_SYSINTM_PLLM);
|
||
|
reg_val &= (~AW8896_BIT_SYSINTM_CLKM);
|
||
|
reg_val &= (~AW8896_BIT_SYSINTM_NOCLKM);
|
||
|
|
||
|
/* uvlo interrupt unmask */
|
||
|
reg_val &= (~AW8896_BIT_SYSINTM_UVLOM);
|
||
|
|
||
|
/* over temperature interrupt unmask */
|
||
|
reg_val &= (~AW8896_BIT_SYSINTM_OTHM);
|
||
|
|
||
|
/* dsp watchdog status interrupt unmask */
|
||
|
reg_val &= (~AW8896_BIT_SYSINTM_WDM);
|
||
|
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_SYSINTM, reg_val);
|
||
|
}
|
||
|
|
||
|
static void aw8896_interrupt_clear(struct aw8896 *aw8896)
|
||
|
{
|
||
|
unsigned int reg_val;
|
||
|
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSST, ®_val);
|
||
|
pr_info("%s: reg SYSST=0x%x\n", __func__, reg_val);
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSINT, ®_val);
|
||
|
pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val);
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSINTM, ®_val);
|
||
|
pr_info("%s: reg SYSINTM=0x%x\n", __func__, reg_val);
|
||
|
}
|
||
|
|
||
|
static irqreturn_t aw8896_irq(int irq, void *data)
|
||
|
{
|
||
|
struct aw8896 *aw8896 = data;
|
||
|
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
|
||
|
aw8896_interrupt_clear(aw8896);
|
||
|
|
||
|
pr_info("%s exit\n", __func__);
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
/*****************************************************
|
||
|
*
|
||
|
* device tree
|
||
|
*
|
||
|
*****************************************************/
|
||
|
static int aw8896_parse_dt(struct device *dev, struct aw8896 *aw8896,
|
||
|
struct device_node *np)
|
||
|
{
|
||
|
|
||
|
aw8896->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
|
||
|
if (aw8896->reset_gpio < 0) {
|
||
|
dev_err(dev,
|
||
|
"%s: no reset gpio provided, will not HW reset device\n",
|
||
|
__func__);
|
||
|
|
||
|
return -EPERM;
|
||
|
}
|
||
|
|
||
|
dev_info(dev, "%s: reset gpio provided ok\n", __func__);
|
||
|
|
||
|
aw8896->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0);
|
||
|
if (aw8896->irq_gpio < 0)
|
||
|
dev_info(dev, "%s: no irq gpio provided.\n", __func__);
|
||
|
else
|
||
|
dev_info(dev, "%s: irq gpio provided ok.\n", __func__);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int aw8896_hw_reset(struct aw8896 *aw8896)
|
||
|
{
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
|
||
|
if (aw8896 && gpio_is_valid(aw8896->reset_gpio)) {
|
||
|
gpio_set_value_cansleep(aw8896->reset_gpio, 1);
|
||
|
usleep_range(1000, 2000);
|
||
|
gpio_set_value_cansleep(aw8896->reset_gpio, 0);
|
||
|
usleep_range(1000, 2000);
|
||
|
} else {
|
||
|
dev_err(aw8896->dev, "%s: failed\n", __func__);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*****************************************************
|
||
|
*
|
||
|
* check chip id
|
||
|
*
|
||
|
*****************************************************/
|
||
|
int aw8896_read_chipid(struct aw8896 *aw8896)
|
||
|
{
|
||
|
int ret = -1;
|
||
|
unsigned int cnt = 0;
|
||
|
unsigned int reg = 0;
|
||
|
|
||
|
while (cnt < AW_READ_CHIPID_RETRIES) {
|
||
|
ret = aw8896_i2c_read(aw8896, AW8896_REG_ID, ®);
|
||
|
if (ret < 0) {
|
||
|
dev_err(aw8896->dev,
|
||
|
"%s: failed to read register AW8896_REG_ID: %d\n",
|
||
|
__func__, ret);
|
||
|
|
||
|
return -EIO;
|
||
|
}
|
||
|
switch (reg) {
|
||
|
case 0x0310:
|
||
|
pr_info("%s aw8896 detected\n", __func__);
|
||
|
aw8896->flags |= AW8896_FLAG_SKIP_INTERRUPTS;
|
||
|
aw8896->flags |= AW8896_FLAG_DSP_START_ON_MUTE;
|
||
|
aw8896->flags |= AW8896_FLAG_DSP_START_ON;
|
||
|
aw8896->chipid = AW8990_ID;
|
||
|
pr_info("%s aw8896->flags=0x%x\n",
|
||
|
__func__, aw8896->flags);
|
||
|
return 0;
|
||
|
default:
|
||
|
pr_info("%s unsupported device revision (0x%x)\n",
|
||
|
__func__, reg);
|
||
|
break;
|
||
|
}
|
||
|
cnt++;
|
||
|
|
||
|
usleep_range(AW_READ_CHIPID_RETRY_DELAY_US,
|
||
|
AW_READ_CHIPID_RETRY_DELAY_US + 1000);
|
||
|
}
|
||
|
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
/******************************************************
|
||
|
*
|
||
|
* sys group attribute: reg
|
||
|
*
|
||
|
******************************************************/
|
||
|
static ssize_t aw8896_reg_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct aw8896 *aw8896 = dev_get_drvdata(dev);
|
||
|
|
||
|
unsigned int databuf[2] = {0};
|
||
|
|
||
|
if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2)
|
||
|
aw8896_i2c_write(aw8896, databuf[0], databuf[1]);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t aw8896_reg_show(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
struct aw8896 *aw8896 = dev_get_drvdata(dev);
|
||
|
ssize_t len = 0;
|
||
|
unsigned char i = 0;
|
||
|
unsigned int reg_val = 0;
|
||
|
|
||
|
for (i = 0; i < AW8896_REG_MAX; i++) {
|
||
|
if (!(aw8896_reg_access[i]®_RD_ACCESS))
|
||
|
continue;
|
||
|
aw8896_i2c_read(aw8896, i, ®_val);
|
||
|
len += snprintf(buf+len, PAGE_SIZE-len, "reg:0x%02x=0x%04x\n",
|
||
|
i, reg_val);
|
||
|
}
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
static ssize_t aw8896_dsp_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct aw8896 *aw8896 = dev_get_drvdata(dev);
|
||
|
|
||
|
unsigned int databuf[16] = {0};
|
||
|
unsigned int reg_val = 0;
|
||
|
int ret;
|
||
|
|
||
|
ret = kstrtoint(buf, 0, &databuf[0]);
|
||
|
if (ret < 0) {
|
||
|
dev_err(dev, "%s, read buf %s failed\n",
|
||
|
__func__, buf);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
if (databuf[0] == 1) {
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSST, ®_val);
|
||
|
if (reg_val & AW8896_BIT_SYSST_PLLS) {
|
||
|
aw8896->init = 0;
|
||
|
aw8896->dsp_fw_state = AW8896_DSP_FW_FAIL;
|
||
|
aw8896_smartpa_cfg(aw8896, false);
|
||
|
aw8896_smartpa_cfg(aw8896, true);
|
||
|
} else {
|
||
|
count += snprintf((char *)(buf+count),
|
||
|
PAGE_SIZE-count,
|
||
|
"aw8896 plls=%d, no iis signal\n",
|
||
|
reg_val&AW8896_BIT_SYSST_PLLS);
|
||
|
}
|
||
|
} else {
|
||
|
aw8896_dsp_enable(aw8896, false);
|
||
|
}
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t aw8896_dsp_show(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
struct aw8896 *aw8896 = dev_get_drvdata(dev);
|
||
|
ssize_t len = 0;
|
||
|
unsigned int i = 0;
|
||
|
unsigned int addr = 0;
|
||
|
unsigned int reg_val = 0;
|
||
|
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSCTRL, ®_val);
|
||
|
|
||
|
if (reg_val & AW8896_BIT_SYSCTRL_DSPBY) {
|
||
|
len += snprintf(buf+len, PAGE_SIZE-len, "aw8896 dsp bypass\n");
|
||
|
} else {
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_SYSST, ®_val);
|
||
|
if (reg_val & AW8896_BIT_SYSST_PLLS) {
|
||
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
"aw8896 dsp working\n");
|
||
|
|
||
|
aw8896_dsp_enable(aw8896, false);
|
||
|
|
||
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
"aw8896 dsp firmware:\n");
|
||
|
addr = 0;
|
||
|
for (i = 0; i < aw8896->dsp_fw_len; i += 2) {
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_DSPMADD,
|
||
|
AW8896_DSP_FW_BASE+addr);
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_DSPMDAT,
|
||
|
®_val);
|
||
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
"0x%02x,0x%02x,",
|
||
|
reg_val&0x00FF, (reg_val>>8));
|
||
|
if ((i/2+1)%4 == 0)
|
||
|
len += snprintf(buf+len,
|
||
|
PAGE_SIZE-len, "\n");
|
||
|
addr++;
|
||
|
}
|
||
|
len += snprintf(buf+len, PAGE_SIZE-len, "\n");
|
||
|
|
||
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
"aw8896 dsp firmware backup:\n");
|
||
|
addr = 0;
|
||
|
for (i = 0; i < aw8896->dsp_fw_len; i += 2) {
|
||
|
aw8896_i2c_write(aw8896, AW8896_REG_DSPMADD,
|
||
|
AW8896_DSP_FW_BACKUP_BASE+addr);
|
||
|
aw8896_i2c_read(aw8896, AW8896_REG_DSPMDAT,
|
||
|
®_val);
|
||
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
"0x%02x,0x%02x,",
|
||
|
reg_val&0x00FF, (reg_val>>8));
|
||
|
if ((i/2+1)%4 == 0)
|
||
|
len += snprintf(buf+len,
|
||
|
PAGE_SIZE-len, "\n");
|
||
|
addr++;
|
||
|
}
|
||
|
len += snprintf(buf+len, PAGE_SIZE-len, "\n");
|
||
|
|
||
|
aw8896_dsp_enable(aw8896, true);
|
||
|
} else {
|
||
|
len += snprintf((char *)(buf+len), PAGE_SIZE-len,
|
||
|
"aw8896 plls=%d, no iis signal\n",
|
||
|
reg_val&AW8896_BIT_SYSST_PLLS);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
static ssize_t aw8896_spk_rcv_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct aw8896 *aw8896 = dev_get_drvdata(dev);
|
||
|
int ret;
|
||
|
unsigned int databuf[2] = {0};
|
||
|
|
||
|
ret = kstrtoint(buf, 0, &databuf[0]);
|
||
|
if (ret < 0) {
|
||
|
dev_err(dev, "%s, read buf %s failed\n",
|
||
|
__func__, buf);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
aw8896->spk_rcv_mode = databuf[0];
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t aw8896_spk_rcv_show(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
struct aw8896 *aw8896 = dev_get_drvdata(dev);
|
||
|
ssize_t len = 0;
|
||
|
|
||
|
if (aw8896->spk_rcv_mode == AW8896_SPEAKER_MODE) {
|
||
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
"aw8896 spk_rcv: %d, speaker mode\n",
|
||
|
aw8896->spk_rcv_mode);
|
||
|
} else if (aw8896->spk_rcv_mode == AW8896_RECEIVER_MODE) {
|
||
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
"aw8896 spk_rcv: %d, receiver mode\n",
|
||
|
aw8896->spk_rcv_mode);
|
||
|
} else {
|
||
|
len += snprintf(buf+len, PAGE_SIZE-len,
|
||
|
"aw8896 spk_rcv: %d, unknown mode\n",
|
||
|
aw8896->spk_rcv_mode);
|
||
|
}
|
||
|
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
static DEVICE_ATTR(reg, 0644, aw8896_reg_show, aw8896_reg_store);
|
||
|
static DEVICE_ATTR(dsp, 0644, aw8896_dsp_show, aw8896_dsp_store);
|
||
|
static DEVICE_ATTR(spk_rcv, 0644,
|
||
|
aw8896_spk_rcv_show, aw8896_spk_rcv_store);
|
||
|
|
||
|
static struct attribute *aw8896_attributes[] = {
|
||
|
&dev_attr_reg.attr,
|
||
|
&dev_attr_dsp.attr,
|
||
|
&dev_attr_spk_rcv.attr,
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
static struct attribute_group aw8896_attribute_group = {
|
||
|
.attrs = aw8896_attributes
|
||
|
};
|
||
|
|
||
|
|
||
|
/******************************************************
|
||
|
*
|
||
|
* i2c driver
|
||
|
*
|
||
|
******************************************************/
|
||
|
int aw8896_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||
|
{
|
||
|
struct snd_soc_dai_driver *dai;
|
||
|
struct aw8896 *aw8896;
|
||
|
struct device_node *np = i2c->dev.of_node;
|
||
|
int irq_flags = 0;
|
||
|
int ret = -1;
|
||
|
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
pr_info("%s: driver version %s\n", __func__, AW8896_VERSION);
|
||
|
|
||
|
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
|
||
|
dev_err(&i2c->dev, "check_functionality failed\n");
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
aw8896 = devm_kzalloc(&i2c->dev, sizeof(struct aw8896), GFP_KERNEL);
|
||
|
if (aw8896 == NULL)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
aw8896->dev = &i2c->dev;
|
||
|
aw8896->i2c = i2c;
|
||
|
|
||
|
/* aw8896 regmap */
|
||
|
aw8896->regmap = devm_regmap_init_i2c(i2c, &aw8896_regmap);
|
||
|
if (IS_ERR(aw8896->regmap)) {
|
||
|
ret = PTR_ERR(aw8896->regmap);
|
||
|
dev_err(&i2c->dev,
|
||
|
"%s: failed to allocate register map: %d\n",
|
||
|
__func__, ret);
|
||
|
goto err_regmap;
|
||
|
}
|
||
|
|
||
|
i2c_set_clientdata(i2c, aw8896);
|
||
|
mutex_init(&aw8896->lock);
|
||
|
|
||
|
/* aw8896 rst & int */
|
||
|
if (np) {
|
||
|
ret = aw8896_parse_dt(&i2c->dev, aw8896, np);
|
||
|
if (ret) {
|
||
|
dev_err(&i2c->dev,
|
||
|
"%s: failed to parse device tree node\n",
|
||
|
__func__);
|
||
|
goto err_parse_dt;
|
||
|
}
|
||
|
} else {
|
||
|
aw8896->reset_gpio = -1;
|
||
|
aw8896->irq_gpio = -1;
|
||
|
}
|
||
|
|
||
|
if (gpio_is_valid(aw8896->reset_gpio)) {
|
||
|
ret = devm_gpio_request_one(&i2c->dev, aw8896->reset_gpio,
|
||
|
GPIOF_OUT_INIT_LOW, "aw8896_rst");
|
||
|
if (ret) {
|
||
|
dev_err(&i2c->dev,
|
||
|
"%s: rst request failed\n", __func__);
|
||
|
goto err_reset_gpio_request;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (gpio_is_valid(aw8896->irq_gpio)) {
|
||
|
ret = devm_gpio_request_one(&i2c->dev, aw8896->irq_gpio,
|
||
|
GPIOF_DIR_IN, "aw8896_int");
|
||
|
if (ret) {
|
||
|
dev_err(&i2c->dev,
|
||
|
"%s: int request failed\n", __func__);
|
||
|
goto err_irq_gpio_request;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* hardware reset */
|
||
|
aw8896_hw_reset(aw8896);
|
||
|
|
||
|
/* aw8896 chip id */
|
||
|
ret = aw8896_read_chipid(aw8896);
|
||
|
if (ret < 0) {
|
||
|
dev_err(&i2c->dev,
|
||
|
"%s: aw8896_read_chipid failed ret=%d\n",
|
||
|
__func__, ret);
|
||
|
goto err_id;
|
||
|
}
|
||
|
|
||
|
/* aw8896 device name */
|
||
|
if (i2c->dev.of_node) {
|
||
|
dev_set_name(&i2c->dev, "%s", "aw8896_smartpa");
|
||
|
} else {
|
||
|
dev_err(&i2c->dev,
|
||
|
"%s failed to set device name: %d\n",
|
||
|
__func__, ret);
|
||
|
}
|
||
|
|
||
|
/* register codec */
|
||
|
dai = devm_kzalloc(&i2c->dev, sizeof(aw8896_dai), GFP_KERNEL);
|
||
|
if (!dai)
|
||
|
goto err_dai_kzalloc;
|
||
|
|
||
|
memcpy(dai, aw8896_dai, sizeof(aw8896_dai));
|
||
|
pr_info("%s dai->name(%s)\n", __func__, dai->name);
|
||
|
|
||
|
ret = aw_componet_codec_ops.aw_snd_soc_register_codec(&i2c->dev,
|
||
|
&soc_codec_dev_aw8896,
|
||
|
dai, ARRAY_SIZE(aw8896_dai));
|
||
|
if (ret < 0) {
|
||
|
dev_err(&i2c->dev,
|
||
|
"%s failed to register aw8896: %d\n",
|
||
|
__func__, ret);
|
||
|
goto err_register_codec;
|
||
|
}
|
||
|
|
||
|
/* aw8896 irq */
|
||
|
if (gpio_is_valid(aw8896->irq_gpio) &&
|
||
|
!(aw8896->flags & AW8896_FLAG_SKIP_INTERRUPTS)) {
|
||
|
/* register irq handler */
|
||
|
irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
|
||
|
ret = devm_request_threaded_irq(&i2c->dev,
|
||
|
gpio_to_irq(aw8896->irq_gpio),
|
||
|
NULL, aw8896_irq, irq_flags,
|
||
|
"aw8896", aw8896);
|
||
|
if (ret != 0) {
|
||
|
dev_err(&i2c->dev, "failed to request IRQ %d: %d\n",
|
||
|
gpio_to_irq(aw8896->irq_gpio), ret);
|
||
|
goto err_irq;
|
||
|
}
|
||
|
} else {
|
||
|
dev_info(&i2c->dev, "%s skipping IRQ registration\n", __func__);
|
||
|
/* disable feature support if gpio was invalid */
|
||
|
aw8896->flags |= AW8896_FLAG_SKIP_INTERRUPTS;
|
||
|
}
|
||
|
|
||
|
dev_set_drvdata(&i2c->dev, aw8896);
|
||
|
ret = sysfs_create_group(&i2c->dev.kobj, &aw8896_attribute_group);
|
||
|
if (ret < 0) {
|
||
|
dev_info(&i2c->dev,
|
||
|
"%s error creating sysfs attr files\n", __func__);
|
||
|
goto err_sysfs;
|
||
|
}
|
||
|
aw8896->call_in_kctl = 0;
|
||
|
pr_info("%s probe completed successfully!\n", __func__);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
err_sysfs:
|
||
|
devm_free_irq(&i2c->dev, gpio_to_irq(aw8896->irq_gpio), aw8896);
|
||
|
err_irq:
|
||
|
aw_componet_codec_ops.aw_snd_soc_unregister_codec(&i2c->dev);
|
||
|
err_register_codec:
|
||
|
devm_kfree(&i2c->dev, dai);
|
||
|
dai = NULL;
|
||
|
err_dai_kzalloc:
|
||
|
err_id:
|
||
|
if (gpio_is_valid(aw8896->irq_gpio))
|
||
|
devm_gpio_free(&i2c->dev, aw8896->irq_gpio);
|
||
|
err_irq_gpio_request:
|
||
|
if (gpio_is_valid(aw8896->reset_gpio))
|
||
|
devm_gpio_free(&i2c->dev, aw8896->reset_gpio);
|
||
|
err_reset_gpio_request:
|
||
|
err_parse_dt:
|
||
|
err_regmap:
|
||
|
devm_kfree(&i2c->dev, aw8896);
|
||
|
aw8896 = NULL;
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(aw8896_i2c_probe);
|
||
|
|
||
|
int aw8896_i2c_remove(struct i2c_client *i2c)
|
||
|
{
|
||
|
struct aw8896 *aw8896 = i2c_get_clientdata(i2c);
|
||
|
|
||
|
pr_info("%s enter\n", __func__);
|
||
|
|
||
|
if (gpio_to_irq(aw8896->irq_gpio))
|
||
|
devm_free_irq(&i2c->dev, gpio_to_irq(aw8896->irq_gpio), aw8896);
|
||
|
|
||
|
aw_componet_codec_ops.aw_snd_soc_unregister_codec(&i2c->dev);
|
||
|
|
||
|
if (gpio_is_valid(aw8896->irq_gpio))
|
||
|
devm_gpio_free(&i2c->dev, aw8896->irq_gpio);
|
||
|
if (gpio_is_valid(aw8896->reset_gpio))
|
||
|
devm_gpio_free(&i2c->dev, aw8896->reset_gpio);
|
||
|
|
||
|
devm_kfree(&i2c->dev, aw8896);
|
||
|
aw8896 = NULL;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(aw8896_i2c_remove);
|
||
|
|
||
|
#if 0
|
||
|
static const struct i2c_device_id aw8896_i2c_id[] = {
|
||
|
{ AW8896_I2C_NAME, 0 },
|
||
|
{ }
|
||
|
};
|
||
|
MODULE_DEVICE_TABLE(i2c, aw8896_i2c_id);
|
||
|
|
||
|
static const struct of_device_id aw8896_dt_match[] = {
|
||
|
{ .compatible = "awinic,aw8896_smartpa" },
|
||
|
{ },
|
||
|
};
|
||
|
|
||
|
static struct i2c_driver aw8896_i2c_driver = {
|
||
|
.driver = {
|
||
|
.name = AW8896_I2C_NAME,
|
||
|
.owner = THIS_MODULE,
|
||
|
.of_match_table = of_match_ptr(aw8896_dt_match),
|
||
|
},
|
||
|
.probe = aw8896_i2c_probe,
|
||
|
.remove = aw8896_i2c_remove,
|
||
|
.id_table = aw8896_i2c_id,
|
||
|
};
|
||
|
|
||
|
static int __init aw8896_i2c_init(void)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
pr_info("aw8896 driver version %s\n", AW8896_VERSION);
|
||
|
|
||
|
ret = i2c_add_driver(&aw8896_i2c_driver);
|
||
|
if (ret) {
|
||
|
pr_err("fail to add aw8896 device into i2c\n");
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
module_init(aw8896_i2c_init);
|
||
|
|
||
|
static void __exit aw8896_i2c_exit(void)
|
||
|
{
|
||
|
i2c_del_driver(&aw8896_i2c_driver);
|
||
|
}
|
||
|
module_exit(aw8896_i2c_exit);
|
||
|
#endif
|
||
|
|
||
|
MODULE_DESCRIPTION("ASoC AW8896 Smart PA Driver");
|
||
|
MODULE_LICENSE("GPL v2");
|