mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-28 22:44:08 -04:00
Val Packett <val@packett.cool> says: The Awinic smart speaker/amp drivers were merged in a very "downstream-brained" state, where configuration was only really determined by the binary "firmware" (register list) file instead of properly participating in the ASoC system. Let's start untangling this mess. This series makes aw88261 actually usable on devices like fairphone-fp5, motorola-dubai and xiaomi-pipa. Link: https://patch.msgid.link/20260529200550.529719-1-val@packett.cool
1362 lines
35 KiB
C
1362 lines
35 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
//
|
|
// aw88261.c -- AW88261 ALSA SoC Audio driver
|
|
//
|
|
// Copyright (c) 2023 awinic Technology CO., LTD
|
|
//
|
|
// Author: Jimmy Zhang <zhangjianming@awinic.com>
|
|
// Author: Weidong Wang <wangweidong.a@awinic.com>
|
|
//
|
|
|
|
#include <linux/i2c.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <sound/soc.h>
|
|
#include <sound/pcm_params.h>
|
|
#include <sound/tlv.h>
|
|
#include "aw88261.h"
|
|
#include "aw88395/aw88395_data_type.h"
|
|
#include "aw88395/aw88395_device.h"
|
|
|
|
static const struct regmap_config aw88261_remap_config = {
|
|
.val_bits = 16,
|
|
.reg_bits = 8,
|
|
.max_register = AW88261_REG_MAX,
|
|
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
|
.val_format_endian = REGMAP_ENDIAN_BIG,
|
|
};
|
|
|
|
static void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
|
|
{
|
|
unsigned int volume = min(value, (unsigned int)AW88261_MUTE_VOL);
|
|
|
|
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL2_REG,
|
|
~AW88261_VOL_MASK, DB_TO_REG_VAL(volume));
|
|
}
|
|
|
|
static void aw88261_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
|
|
{
|
|
if (flag)
|
|
regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
|
|
~AW88261_I2STXEN_MASK, AW88261_I2STXEN_ENABLE_VALUE);
|
|
else
|
|
regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
|
|
~AW88261_I2STXEN_MASK, AW88261_I2STXEN_DISABLE_VALUE);
|
|
}
|
|
|
|
static void aw88261_dev_pwd(struct aw_device *aw_dev, bool pwd)
|
|
{
|
|
if (pwd)
|
|
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
|
|
~AW88261_PWDN_MASK, AW88261_PWDN_POWER_DOWN_VALUE);
|
|
else
|
|
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
|
|
~AW88261_PWDN_MASK, AW88261_PWDN_WORKING_VALUE);
|
|
}
|
|
|
|
static void aw88261_dev_amppd(struct aw_device *aw_dev, bool amppd)
|
|
{
|
|
if (amppd)
|
|
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
|
|
~AW88261_AMPPD_MASK, AW88261_AMPPD_POWER_DOWN_VALUE);
|
|
else
|
|
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
|
|
~AW88261_AMPPD_MASK, AW88261_AMPPD_WORKING_VALUE);
|
|
}
|
|
|
|
static void aw88261_dev_mute(struct aw_device *aw_dev, bool is_mute)
|
|
{
|
|
if (is_mute) {
|
|
aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
|
|
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
|
|
~AW88261_HMUTE_MASK, AW88261_HMUTE_ENABLE_VALUE);
|
|
} else {
|
|
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
|
|
~AW88261_HMUTE_MASK, AW88261_HMUTE_DISABLE_VALUE);
|
|
aw88261_dev_set_volume(aw_dev, aw_dev->volume_desc.ctl_volume);
|
|
}
|
|
}
|
|
|
|
static void aw88261_dev_clear_int_status(struct aw_device *aw_dev)
|
|
{
|
|
unsigned int int_status;
|
|
|
|
/* read int status and clear */
|
|
regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status);
|
|
/* make sure int status is clear */
|
|
regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status);
|
|
|
|
dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", int_status);
|
|
}
|
|
|
|
static int aw88261_dev_get_iis_status(struct aw_device *aw_dev)
|
|
{
|
|
unsigned int reg_val;
|
|
int ret;
|
|
|
|
ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, ®_val);
|
|
if (ret)
|
|
return ret;
|
|
if ((reg_val & AW88261_BIT_PLL_CHECK) != AW88261_BIT_PLL_CHECK) {
|
|
dev_dbg(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw88261_dev_check_pll(struct aw_device *aw_dev)
|
|
{
|
|
int ret, i;
|
|
|
|
for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
|
|
ret = aw88261_dev_get_iis_status(aw_dev);
|
|
if (ret) {
|
|
dev_dbg(aw_dev->dev, "mode1 iis signal check error");
|
|
usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
|
|
} else {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return -EPERM;
|
|
}
|
|
|
|
static int aw88261_dev_configure_syspll(struct aw88261 *aw88261)
|
|
{
|
|
struct aw_device *aw_dev = aw88261->aw_pa;
|
|
int ret;
|
|
|
|
/* Configure TDM slots (I2S is represented as no slots) */
|
|
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
|
|
~AW88261_SLOT_NUM_MASK, aw88261->slot_num_value);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
|
|
~AW88261_I2S_TX_SLOTVLD_MASK,
|
|
aw88261->tx_slotvld_mask);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
|
|
~AW88261_I2S_RXL_SLOTVLD_MASK,
|
|
aw88261->rxl_slotvld_mask);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
|
|
~AW88261_I2S_RXR_SLOTVLD_MASK,
|
|
aw88261->rxr_slotvld_mask);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* PLL divider must be used for 8/16/32 kHz modes */
|
|
ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
|
|
~AW88261_CCO_MUX_MASK, aw88261->cco_mux_value);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* The word clock (WCK) defines the beginning of a frame */
|
|
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
|
|
~AW88261_I2SSR_MASK, aw88261->sr_value);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* The bit clock (BCK) defines the length of a frame */
|
|
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
|
|
~AW88261_I2SBCK_MASK,
|
|
(aw88261->tdm_bck_value != AW88261_TDM_BCK_UNSET)
|
|
? aw88261->tdm_bck_value : aw88261->bck_value);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* The logical frame size is the width of data for 1 slot */
|
|
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
|
|
~AW88261_I2SFS_MASK, aw88261->fs_value);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* The I2S interface mode (Philips standard, LSB/MSB justified) */
|
|
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
|
|
~AW88261_I2SMD_MASK, aw88261->md_value);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* The polarity of the bit clock (BCK) */
|
|
ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
|
|
~AW88261_BCKINV_MASK, aw88261->bck_inv_value);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return aw88261_dev_check_pll(aw_dev);
|
|
}
|
|
|
|
static int aw88261_dev_check_sysst(struct aw_device *aw_dev)
|
|
{
|
|
unsigned int check_val;
|
|
unsigned int reg_val;
|
|
int ret, i;
|
|
|
|
for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
|
|
ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, ®_val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK)
|
|
& AW88261_BIT_SYSST_CHECK;
|
|
if (check_val != AW88261_BIT_SYSST_CHECK) {
|
|
dev_dbg(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
|
|
reg_val, AW88261_BIT_SYSST_CHECK);
|
|
usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -EPERM;
|
|
}
|
|
|
|
static void aw88261_dev_uls_hmute(struct aw_device *aw_dev, bool uls_hmute)
|
|
{
|
|
if (uls_hmute)
|
|
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
|
|
~AW88261_ULS_HMUTE_MASK,
|
|
AW88261_ULS_HMUTE_ENABLE_VALUE);
|
|
else
|
|
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
|
|
~AW88261_ULS_HMUTE_MASK,
|
|
AW88261_ULS_HMUTE_DISABLE_VALUE);
|
|
}
|
|
|
|
static void aw88261_reg_force_set(struct aw88261 *aw88261)
|
|
{
|
|
if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) {
|
|
/* set FORCE_PWM */
|
|
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG,
|
|
~AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
|
|
/* set BOOST_OS_WIDTH */
|
|
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG,
|
|
~AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
|
|
/* set BURST_LOOPR */
|
|
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG,
|
|
~AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
|
|
/* set RSQN_DLY */
|
|
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG,
|
|
~AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
|
|
/* set BURST_SSMODE */
|
|
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG,
|
|
~AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
|
|
/* set BST_BURST */
|
|
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG,
|
|
~AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
|
|
} else {
|
|
dev_dbg(aw88261->aw_pa->dev, "needn't set reg value");
|
|
}
|
|
}
|
|
|
|
static int aw88261_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk)
|
|
{
|
|
u16 reg_icalk, reg_icalkl;
|
|
unsigned int reg_val;
|
|
int ret;
|
|
|
|
ret = regmap_read(aw_dev->regmap, AW88261_EFRH4_REG, ®_val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
reg_icalk = reg_val & (~AW88261_EF_ISN_GESLP_H_MASK);
|
|
|
|
ret = regmap_read(aw_dev->regmap, AW88261_EFRL4_REG, ®_val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
reg_icalkl = reg_val & (~AW88261_EF_ISN_GESLP_L_MASK);
|
|
|
|
reg_icalk = (reg_icalk >> AW88261_ICALK_SHIFT) & (reg_icalkl >> AW88261_ICALKL_SHIFT);
|
|
|
|
if (reg_icalk & (~AW88261_EF_ISN_GESLP_SIGN_MASK))
|
|
reg_icalk = reg_icalk | ~AW88261_EF_ISN_GESLP_NEG;
|
|
|
|
*icalk = (int16_t)reg_icalk;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw88261_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk)
|
|
{
|
|
u16 reg_vcalk, reg_vcalkl;
|
|
unsigned int reg_val;
|
|
int ret;
|
|
|
|
ret = regmap_read(aw_dev->regmap, AW88261_EFRH3_REG, ®_val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
reg_vcalk = (u16)reg_val & (~AW88261_EF_VSN_GESLP_H_MASK);
|
|
|
|
ret = regmap_read(aw_dev->regmap, AW88261_EFRL3_REG, ®_val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
reg_vcalkl = (u16)reg_val & (~AW88261_EF_VSN_GESLP_L_MASK);
|
|
|
|
reg_vcalk = (reg_vcalk >> AW88261_VCALK_SHIFT) & (reg_vcalkl >> AW88261_VCALKL_SHIFT);
|
|
|
|
if (reg_vcalk & AW88261_EF_VSN_GESLP_SIGN_MASK)
|
|
reg_vcalk = reg_vcalk | (~AW88261_EF_VSN_GESLP_NEG);
|
|
*vcalk = (int16_t)reg_vcalk;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw88261_dev_set_vcalb(struct aw_device *aw_dev)
|
|
{
|
|
int16_t icalk_val, vcalk_val;
|
|
int icalk, vcalk, vcalb;
|
|
u32 reg_val;
|
|
int ret;
|
|
|
|
ret = aw88261_dev_get_icalk(aw_dev, &icalk_val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = aw88261_dev_get_vcalk(aw_dev, &vcalk_val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
icalk = AW88261_CABL_BASE_VALUE + AW88261_ICABLK_FACTOR * icalk_val;
|
|
vcalk = AW88261_CABL_BASE_VALUE + AW88261_VCABLK_FACTOR * vcalk_val;
|
|
if (!vcalk)
|
|
return -EINVAL;
|
|
|
|
vcalb = AW88261_VCAL_FACTOR * icalk / vcalk;
|
|
reg_val = (unsigned int)vcalb;
|
|
|
|
dev_dbg(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x",
|
|
icalk, vcalk, vcalb, reg_val);
|
|
ret = regmap_write(aw_dev->regmap, AW88261_VSNTM1_REG, reg_val);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw88261_dev_reg_update(struct aw88261 *aw88261,
|
|
unsigned char *data, unsigned int len)
|
|
{
|
|
struct aw_device *aw_dev = aw88261->aw_pa;
|
|
struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
|
|
unsigned int read_val, efcheck_val, read_vol;
|
|
int data_len, i, ret;
|
|
int16_t *reg_data;
|
|
u16 reg_val;
|
|
u8 reg_addr;
|
|
|
|
if (!len || !data) {
|
|
dev_err(aw_dev->dev, "reg data is null or len is 0");
|
|
return -EINVAL;
|
|
}
|
|
|
|
reg_data = (int16_t *)data;
|
|
data_len = len >> 1;
|
|
|
|
if (data_len & 0x1) {
|
|
dev_err(aw_dev->dev, "data len:%d unsupported", data_len);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < data_len; i += 2) {
|
|
reg_addr = reg_data[i];
|
|
reg_val = reg_data[i + 1];
|
|
|
|
if (reg_addr == AW88261_SYSCTRL_REG) {
|
|
aw88261->amppd_st = reg_val & (~AW88261_AMPPD_MASK);
|
|
ret = regmap_read(aw_dev->regmap, reg_addr, &read_val);
|
|
if (ret)
|
|
break;
|
|
|
|
/* keep all three bits from current hw status */
|
|
read_val &= (~AW88261_AMPPD_MASK) | (~AW88261_PWDN_MASK) |
|
|
(~AW88261_HMUTE_MASK);
|
|
reg_val &= (AW88261_AMPPD_MASK & AW88261_PWDN_MASK & AW88261_HMUTE_MASK);
|
|
reg_val |= read_val;
|
|
|
|
/* enable uls hmute */
|
|
reg_val &= AW88261_ULS_HMUTE_MASK;
|
|
reg_val |= AW88261_ULS_HMUTE_ENABLE_VALUE;
|
|
}
|
|
|
|
if (reg_addr == AW88261_DBGCTRL_REG) {
|
|
efcheck_val = reg_val & (~AW88261_EF_DBMD_MASK);
|
|
if (efcheck_val == AW88261_OR_VALUE)
|
|
aw88261->efuse_check = AW88261_EF_OR_CHECK;
|
|
else
|
|
aw88261->efuse_check = AW88261_EF_AND_CHECK;
|
|
}
|
|
|
|
/* i2stxen */
|
|
if (reg_addr == AW88261_I2SCTRL3_REG) {
|
|
/* close tx */
|
|
reg_val &= AW88261_I2STXEN_MASK;
|
|
reg_val |= AW88261_I2STXEN_DISABLE_VALUE;
|
|
}
|
|
|
|
if (reg_addr == AW88261_SYSCTRL2_REG) {
|
|
read_vol = (reg_val & (~AW88261_VOL_MASK)) >>
|
|
AW88261_VOL_START_BIT;
|
|
aw_dev->volume_desc.init_volume =
|
|
REG_VAL_TO_DB(read_vol);
|
|
}
|
|
|
|
if (reg_addr == AW88261_VSNTM1_REG)
|
|
continue;
|
|
|
|
ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
ret = aw88261_dev_set_vcalb(aw_dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (aw_dev->prof_cur != aw_dev->prof_index)
|
|
vol_desc->ctl_volume = 0;
|
|
|
|
/* keep min volume */
|
|
aw88261_dev_set_volume(aw_dev, vol_desc->mute_volume);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw88261_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
|
|
{
|
|
struct aw_prof_info *prof_info = &aw_dev->prof_info;
|
|
struct aw_prof_desc *prof_desc;
|
|
|
|
if ((index >= aw_dev->prof_info.count) || (index < 0)) {
|
|
dev_err(aw_dev->dev, "index[%d] overflow count[%d]",
|
|
index, aw_dev->prof_info.count);
|
|
return -EINVAL;
|
|
}
|
|
|
|
prof_desc = &aw_dev->prof_info.prof_desc[index];
|
|
|
|
*prof_name = prof_info->prof_name_list[prof_desc->id];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw88261_dev_get_prof_data(struct aw_device *aw_dev, int index,
|
|
struct aw_prof_desc **prof_desc)
|
|
{
|
|
if ((index >= aw_dev->prof_info.count) || (index < 0)) {
|
|
dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n",
|
|
__func__, index, aw_dev->prof_info.count);
|
|
return -EINVAL;
|
|
}
|
|
|
|
*prof_desc = &aw_dev->prof_info.prof_desc[index];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw88261_dev_fw_update(struct aw88261 *aw88261)
|
|
{
|
|
struct aw_device *aw_dev = aw88261->aw_pa;
|
|
struct aw_prof_desc *prof_index_desc;
|
|
struct aw_sec_data_desc *sec_desc;
|
|
char *prof_name;
|
|
int ret;
|
|
|
|
ret = aw88261_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
|
|
if (ret) {
|
|
dev_err(aw_dev->dev, "get prof name failed");
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev_dbg(aw_dev->dev, "start update %s", prof_name);
|
|
|
|
ret = aw88261_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* update reg */
|
|
sec_desc = prof_index_desc->sec_desc;
|
|
ret = aw88261_dev_reg_update(aw88261, sec_desc[AW88395_DATA_TYPE_REG].data,
|
|
sec_desc[AW88395_DATA_TYPE_REG].len);
|
|
if (ret) {
|
|
dev_err(aw_dev->dev, "update reg failed");
|
|
return ret;
|
|
}
|
|
|
|
aw_dev->prof_cur = aw_dev->prof_index;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw88261_dev_start(struct aw88261 *aw88261)
|
|
{
|
|
struct aw_device *aw_dev = aw88261->aw_pa;
|
|
int ret;
|
|
|
|
if (aw_dev->status == AW88261_DEV_PW_ON) {
|
|
dev_dbg(aw_dev->dev, "already power on");
|
|
return 0;
|
|
}
|
|
|
|
/* power on */
|
|
aw88261_dev_pwd(aw_dev, false);
|
|
usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
|
|
|
|
ret = aw88261_dev_configure_syspll(aw88261);
|
|
if (ret) {
|
|
dev_dbg(aw_dev->dev, "pll check failed");
|
|
goto pll_check_fail;
|
|
}
|
|
|
|
/* amppd on */
|
|
aw88261_dev_amppd(aw_dev, false);
|
|
usleep_range(AW88261_1000_US, AW88261_1000_US + 50);
|
|
|
|
/* check i2s status */
|
|
ret = aw88261_dev_check_sysst(aw_dev);
|
|
if (ret) {
|
|
dev_dbg(aw_dev->dev, "sysst check failed");
|
|
goto sysst_check_fail;
|
|
}
|
|
|
|
/* enable tx feedback */
|
|
aw88261_dev_i2s_tx_enable(aw_dev, true);
|
|
|
|
if (aw88261->amppd_st)
|
|
aw88261_dev_amppd(aw_dev, true);
|
|
|
|
aw88261_reg_force_set(aw88261);
|
|
|
|
/* close uls mute */
|
|
aw88261_dev_uls_hmute(aw_dev, false);
|
|
|
|
/* close mute */
|
|
if (!aw88261->mute_st)
|
|
aw88261_dev_mute(aw_dev, false);
|
|
|
|
/* clear inturrupt */
|
|
aw88261_dev_clear_int_status(aw_dev);
|
|
aw_dev->status = AW88261_DEV_PW_ON;
|
|
|
|
return 0;
|
|
|
|
sysst_check_fail:
|
|
aw88261_dev_i2s_tx_enable(aw_dev, false);
|
|
aw88261_dev_clear_int_status(aw_dev);
|
|
aw88261_dev_amppd(aw_dev, true);
|
|
pll_check_fail:
|
|
aw88261_dev_pwd(aw_dev, true);
|
|
aw_dev->status = AW88261_DEV_PW_OFF;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw88261_dev_stop(struct aw_device *aw_dev)
|
|
{
|
|
if (aw_dev->status == AW88261_DEV_PW_OFF) {
|
|
dev_info(aw_dev->dev, "already power off");
|
|
return 0;
|
|
}
|
|
|
|
aw_dev->status = AW88261_DEV_PW_OFF;
|
|
|
|
/* clear inturrupt */
|
|
aw88261_dev_clear_int_status(aw_dev);
|
|
|
|
aw88261_dev_uls_hmute(aw_dev, true);
|
|
/* set mute */
|
|
aw88261_dev_mute(aw_dev, true);
|
|
|
|
/* close tx feedback */
|
|
aw88261_dev_i2s_tx_enable(aw_dev, false);
|
|
usleep_range(AW88261_1000_US, AW88261_1000_US + 100);
|
|
|
|
/* enable amppd */
|
|
aw88261_dev_amppd(aw_dev, true);
|
|
|
|
/* set power down */
|
|
aw88261_dev_pwd(aw_dev, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw88261_reg_update(struct aw88261 *aw88261, bool force)
|
|
{
|
|
struct aw_device *aw_dev = aw88261->aw_pa;
|
|
int ret;
|
|
|
|
if (force) {
|
|
ret = regmap_write(aw_dev->regmap,
|
|
AW88261_ID_REG, AW88261_SOFT_RESET_VALUE);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = aw88261_dev_fw_update(aw88261);
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
if (aw_dev->prof_cur != aw_dev->prof_index) {
|
|
ret = aw88261_dev_fw_update(aw88261);
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
aw_dev->prof_cur = aw_dev->prof_index;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void aw88261_start_pa(struct aw88261 *aw88261)
|
|
{
|
|
int ret, i;
|
|
|
|
for (i = 0; i < AW88261_START_RETRIES; i++) {
|
|
ret = aw88261_reg_update(aw88261, aw88261->phase_sync);
|
|
if (ret) {
|
|
dev_dbg(aw88261->aw_pa->dev,
|
|
"aw88261_reg_update failed, cnt:%d, ret:%d\n", i, ret);
|
|
continue;
|
|
}
|
|
ret = aw88261_dev_start(aw88261);
|
|
if (ret) {
|
|
dev_dbg(aw88261->aw_pa->dev,
|
|
"aw88261_dev_start failed, cnt:%d, ret:%d\n", i, ret);
|
|
continue;
|
|
} else {
|
|
dev_dbg(aw88261->aw_pa->dev, "start success\n");
|
|
break;
|
|
}
|
|
}
|
|
if (ret != 0)
|
|
dev_err(aw88261->aw_pa->dev, "start failure (%d)\n", ret);
|
|
}
|
|
|
|
static void aw88261_start(struct aw88261 *aw88261)
|
|
{
|
|
if (aw88261->aw_pa->fw_status != AW88261_DEV_FW_OK)
|
|
return;
|
|
|
|
if (aw88261->aw_pa->status == AW88261_DEV_PW_ON)
|
|
return;
|
|
|
|
aw88261_start_pa(aw88261);
|
|
}
|
|
|
|
static int aw88261_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
|
{
|
|
struct snd_soc_component *component = dai->component;
|
|
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
|
|
|
|
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
|
case SND_SOC_DAIFMT_NB_NF:
|
|
aw88261->bck_inv_value = AW88261_BCKINV_NOT_INVERT_VALUE;
|
|
break;
|
|
case SND_SOC_DAIFMT_IB_NF:
|
|
aw88261->bck_inv_value = AW88261_BCKINV_INVERTED_VALUE;
|
|
break;
|
|
default:
|
|
dev_err(aw88261->aw_pa->dev, "unsupported invert mode 0x%x\n",
|
|
fmt & SND_SOC_DAIFMT_INV_MASK);
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
|
case SND_SOC_DAIFMT_I2S:
|
|
case SND_SOC_DAIFMT_DSP_A:
|
|
aw88261->md_value = AW88261_I2SMD_PHILIPS_STANDARD_VALUE;
|
|
break;
|
|
case SND_SOC_DAIFMT_MSB:
|
|
case SND_SOC_DAIFMT_DSP_B:
|
|
aw88261->md_value = AW88261_I2SMD_MSB_JUSTIFIED_VALUE;
|
|
break;
|
|
case SND_SOC_DAIFMT_LSB:
|
|
aw88261->md_value = AW88261_I2SMD_LSB_JUSTIFIED_VALUE;
|
|
break;
|
|
default:
|
|
dev_err(aw88261->aw_pa->dev, "unsupported DAI format 0x%x\n",
|
|
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw88261_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *params,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct snd_soc_component *component = dai->component;
|
|
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
|
|
|
|
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
|
return 0;
|
|
|
|
aw88261->cco_mux_value = AW88261_CCO_MUX_BYPASS_VALUE;
|
|
switch (params_rate(params)) {
|
|
case 8000:
|
|
aw88261->sr_value = AW88261_I2SSR_8KHZ_VALUE;
|
|
aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE;
|
|
break;
|
|
case 11025:
|
|
aw88261->sr_value = AW88261_I2SSR_11P025KHZ_VALUE;
|
|
break;
|
|
case 12000:
|
|
aw88261->sr_value = AW88261_I2SSR_12KHZ_VALUE;
|
|
break;
|
|
case 16000:
|
|
aw88261->sr_value = AW88261_I2SSR_16KHZ_VALUE;
|
|
aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE;
|
|
break;
|
|
case 22050:
|
|
aw88261->sr_value = AW88261_I2SSR_22P05KHZ_VALUE;
|
|
break;
|
|
case 24000:
|
|
aw88261->sr_value = AW88261_I2SSR_24KHZ_VALUE;
|
|
break;
|
|
case 32000:
|
|
aw88261->sr_value = AW88261_I2SSR_32KHZ_VALUE;
|
|
aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE;
|
|
break;
|
|
case 44100:
|
|
aw88261->sr_value = AW88261_I2SSR_44P1KHZ_VALUE;
|
|
break;
|
|
case 48000:
|
|
aw88261->sr_value = AW88261_I2SSR_48KHZ_VALUE;
|
|
break;
|
|
case 96000:
|
|
aw88261->sr_value = AW88261_I2SSR_96KHZ_VALUE;
|
|
break;
|
|
case 192000:
|
|
aw88261->sr_value = AW88261_I2SSR_192KHZ_VALUE;
|
|
break;
|
|
default:
|
|
dev_err(aw88261->aw_pa->dev, "unsupported sample rate %d\n",
|
|
params_rate(params));
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (params_width(params)) {
|
|
case 16:
|
|
aw88261->fs_value = AW88261_I2SFS_16_BITS_VALUE;
|
|
break;
|
|
case 20:
|
|
aw88261->fs_value = AW88261_I2SFS_20_BITS_VALUE;
|
|
break;
|
|
case 24:
|
|
aw88261->fs_value = AW88261_I2SFS_24_BITS_VALUE;
|
|
break;
|
|
case 32:
|
|
aw88261->fs_value = AW88261_I2SFS_32_BITS_VALUE;
|
|
break;
|
|
default:
|
|
dev_err(aw88261->aw_pa->dev, "unsupported bit width %d\n",
|
|
params_width(params));
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (params_physical_width(params)) {
|
|
case 16:
|
|
aw88261->bck_value = AW88261_I2SBCK_32FS_VALUE;
|
|
break;
|
|
case 24:
|
|
aw88261->bck_value = AW88261_I2SBCK_48FS_VALUE;
|
|
break;
|
|
case 32:
|
|
aw88261->bck_value = AW88261_I2SBCK_64FS_VALUE;
|
|
break;
|
|
default:
|
|
dev_err(aw88261->aw_pa->dev, "unsupported physical bit width %d\n",
|
|
params_physical_width(params));
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw88261_set_tdm_slot(struct snd_soc_dai *dai,
|
|
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
|
|
{
|
|
struct snd_soc_component *component = dai->component;
|
|
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
|
|
int chan;
|
|
|
|
switch (slots) {
|
|
case 0:
|
|
/* Just reset everything TDM related to I2S values */
|
|
aw88261->slot_num_value = AW88261_SLOT_NUM_I2S_MODE_VALUE;
|
|
aw88261->tdm_bck_value = AW88261_TDM_BCK_UNSET;
|
|
aw88261->tx_slotvld_mask = 0 << AW88261_I2S_TX_SLOTVLD_START_BIT;
|
|
aw88261->rxl_slotvld_mask = 0 << AW88261_I2S_RXL_SLOTVLD_START_BIT;
|
|
aw88261->rxr_slotvld_mask = 1 << AW88261_I2S_RXR_SLOTVLD_START_BIT;
|
|
return 0;
|
|
case 1:
|
|
aw88261->slot_num_value = AW88261_SLOT_NUM_TDM1S_VALUE;
|
|
break;
|
|
case 2:
|
|
aw88261->slot_num_value = AW88261_SLOT_NUM_TDM2S_VALUE;
|
|
break;
|
|
case 4:
|
|
aw88261->slot_num_value = AW88261_SLOT_NUM_TDM4S_VALUE;
|
|
break;
|
|
case 6:
|
|
aw88261->slot_num_value = AW88261_SLOT_NUM_TDM6S_VALUE;
|
|
break;
|
|
case 8:
|
|
aw88261->slot_num_value = AW88261_SLOT_NUM_TDM8S_VALUE;
|
|
break;
|
|
case 16:
|
|
aw88261->slot_num_value = AW88261_SLOT_NUM_TDM16S_VALUE;
|
|
break;
|
|
default:
|
|
dev_err(aw88261->aw_pa->dev, "unsupported slot count %d\n", slots);
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (slot_width) {
|
|
case 16:
|
|
aw88261->tdm_bck_value = AW88261_I2SBCK_32FS_VALUE;
|
|
break;
|
|
case 20:
|
|
case 24:
|
|
aw88261->tdm_bck_value = AW88261_I2SBCK_48FS_VALUE;
|
|
break;
|
|
case 32:
|
|
aw88261->tdm_bck_value = AW88261_I2SBCK_64FS_VALUE;
|
|
break;
|
|
default:
|
|
dev_err(aw88261->aw_pa->dev, "unsupported slot width %d\n",
|
|
slot_width);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (tx_mask != 0) {
|
|
if ((chan = __ffs(tx_mask)) > 16)
|
|
return -EINVAL;
|
|
|
|
aw88261->tx_slotvld_mask = chan << AW88261_I2S_TX_SLOTVLD_START_BIT;
|
|
}
|
|
|
|
if (rx_mask != 0) {
|
|
if ((chan = __ffs(rx_mask)) > 16)
|
|
return -EINVAL;
|
|
|
|
aw88261->rxl_slotvld_mask = chan << AW88261_I2S_RXL_SLOTVLD_START_BIT;
|
|
}
|
|
|
|
if ((rx_mask & ~BIT(chan)) != 0) {
|
|
if ((chan = __ffs(rx_mask & ~BIT(chan))) > 16)
|
|
return -EINVAL;
|
|
|
|
aw88261->rxr_slotvld_mask = chan << AW88261_I2S_RXR_SLOTVLD_START_BIT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_dai_ops aw88261_dai_ops = {
|
|
.set_fmt = aw88261_set_fmt,
|
|
.hw_params = aw88261_hw_params,
|
|
.set_tdm_slot = aw88261_set_tdm_slot,
|
|
};
|
|
|
|
static struct snd_soc_dai_driver aw88261_dai[] = {
|
|
{
|
|
.name = "aw88261-aif",
|
|
.id = 1,
|
|
.playback = {
|
|
.stream_name = "Speaker_Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 2,
|
|
.rates = AW88261_RATES,
|
|
.formats = AW88261_FORMATS,
|
|
},
|
|
.capture = {
|
|
.stream_name = "Speaker_Capture",
|
|
.channels_min = 1,
|
|
.channels_max = 2,
|
|
.rates = AW88261_RATES,
|
|
.formats = AW88261_FORMATS,
|
|
},
|
|
.ops = &aw88261_dai_ops,
|
|
},
|
|
};
|
|
|
|
static int aw88261_dev_set_profile_index(struct aw_device *aw_dev, int index)
|
|
{
|
|
/* check the index whether is valid */
|
|
if ((index >= aw_dev->prof_info.count) || (index < 0))
|
|
return -EINVAL;
|
|
/* check the index whether change */
|
|
if (aw_dev->prof_index == index)
|
|
return -EPERM;
|
|
|
|
aw_dev->prof_index = index;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw88261_profile_info(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
|
|
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
|
|
char *prof_name;
|
|
int count, ret;
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
|
uinfo->count = 1;
|
|
|
|
count = aw88261->aw_pa->prof_info.count;
|
|
if (count <= 0) {
|
|
uinfo->value.enumerated.items = 0;
|
|
return 0;
|
|
}
|
|
|
|
uinfo->value.enumerated.items = count;
|
|
|
|
if (uinfo->value.enumerated.item >= count)
|
|
uinfo->value.enumerated.item = count - 1;
|
|
|
|
count = uinfo->value.enumerated.item;
|
|
|
|
ret = aw88261_dev_get_prof_name(aw88261->aw_pa, count, &prof_name);
|
|
if (ret) {
|
|
strscpy(uinfo->value.enumerated.name, "null");
|
|
return 0;
|
|
}
|
|
|
|
strscpy(uinfo->value.enumerated.name, prof_name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw88261_profile_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
|
|
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
|
|
|
|
ucontrol->value.integer.value[0] = aw88261->aw_pa->prof_index;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw88261_profile_set(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
|
|
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
|
|
int ret;
|
|
|
|
/* pa stop or stopping just set profile */
|
|
mutex_lock(&aw88261->lock);
|
|
ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]);
|
|
if (ret) {
|
|
dev_dbg(codec->dev, "profile index does not change");
|
|
mutex_unlock(&aw88261->lock);
|
|
return 0;
|
|
}
|
|
|
|
if (aw88261->aw_pa->status) {
|
|
aw88261_dev_stop(aw88261->aw_pa);
|
|
aw88261_start(aw88261);
|
|
}
|
|
|
|
mutex_unlock(&aw88261->lock);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int aw88261_volume_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
|
|
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
|
|
struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
|
|
|
|
ucontrol->value.integer.value[0] =
|
|
(AW88261_MUTE_VOL - vol_desc->ctl_volume) / 2;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw88261_volume_set(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
|
|
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
|
|
struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
|
|
struct soc_mixer_control *mc =
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
int value = ucontrol->value.integer.value[0];
|
|
|
|
if (value < mc->min || value > mc->max)
|
|
return -EINVAL;
|
|
|
|
value = AW88261_MUTE_VOL - (value * 2);
|
|
|
|
if (vol_desc->ctl_volume != value) {
|
|
vol_desc->ctl_volume = value;
|
|
aw88261_dev_set_volume(aw88261->aw_pa, vol_desc->ctl_volume);
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The field contains 4 bits in units of 6dB + 6 bits in units of 0.125dB
|
|
* which is too precise for TLV (!) so we have to multiply the scale by 2.
|
|
*
|
|
* The range is clamped at -90dB to prevent overflowing the 4-bit part.
|
|
*/
|
|
static const DECLARE_TLV_DB_SCALE(volume_tlv, -9000, 25, 0);
|
|
|
|
static const struct snd_kcontrol_new aw88261_controls[] = {
|
|
SOC_SINGLE_EXT_TLV("PCM Playback Volume", AW88261_SYSCTRL2_REG,
|
|
6, AW88261_CTL_MAX_VOL, 1,
|
|
aw88261_volume_get, aw88261_volume_set, volume_tlv),
|
|
AW88261_PROFILE_EXT("Profile Set", aw88261_profile_info,
|
|
aw88261_profile_get, aw88261_profile_set),
|
|
};
|
|
|
|
static int aw88261_playback_event(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *k, int event)
|
|
{
|
|
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
|
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
|
|
|
|
mutex_lock(&aw88261->lock);
|
|
switch (event) {
|
|
case SND_SOC_DAPM_PRE_PMU:
|
|
aw88261_start(aw88261);
|
|
break;
|
|
case SND_SOC_DAPM_POST_PMD:
|
|
aw88261_dev_stop(aw88261->aw_pa);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
mutex_unlock(&aw88261->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_dapm_widget aw88261_dapm_widgets[] = {
|
|
/* playback */
|
|
SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0,
|
|
aw88261_playback_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_OUTPUT("DAC Output"),
|
|
|
|
/* capture */
|
|
SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0),
|
|
SND_SOC_DAPM_INPUT("ADC Input"),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route aw88261_audio_map[] = {
|
|
{"DAC Output", NULL, "AIF_RX"},
|
|
{"AIF_TX", NULL, "ADC Input"},
|
|
};
|
|
|
|
static int aw88261_frcset_check(struct aw88261 *aw88261)
|
|
{
|
|
unsigned int reg_val;
|
|
u16 temh, teml, tem;
|
|
int ret;
|
|
|
|
ret = regmap_read(aw88261->regmap, AW88261_EFRH3_REG, ®_val);
|
|
if (ret)
|
|
return ret;
|
|
temh = ((u16)reg_val & (~AW88261_TEMH_MASK));
|
|
|
|
ret = regmap_read(aw88261->regmap, AW88261_EFRL3_REG, ®_val);
|
|
if (ret)
|
|
return ret;
|
|
teml = ((u16)reg_val & (~AW88261_TEML_MASK));
|
|
|
|
if (aw88261->efuse_check == AW88261_EF_OR_CHECK)
|
|
tem = (temh | teml);
|
|
else
|
|
tem = (temh & teml);
|
|
|
|
if (tem == AW88261_DEFAULT_CFG)
|
|
aw88261->frcset_en = AW88261_FRCSET_ENABLE;
|
|
else
|
|
aw88261->frcset_en = AW88261_FRCSET_DISABLE;
|
|
|
|
dev_dbg(aw88261->aw_pa->dev, "tem is 0x%04x, frcset_en is %d",
|
|
tem, aw88261->frcset_en);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg)
|
|
{
|
|
struct aw_device *aw_dev = aw88261->aw_pa;
|
|
int ret;
|
|
|
|
ret = aw88395_dev_cfg_load(aw_dev, aw_cfg);
|
|
if (ret) {
|
|
dev_err(aw_dev->dev, "aw_dev acf parse failed");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = regmap_write(aw_dev->regmap, AW88261_ID_REG, AW88261_SOFT_RESET_VALUE);
|
|
if (ret)
|
|
return ret;
|
|
|
|
aw_dev->prof_cur = AW88261_INIT_PROFILE;
|
|
aw_dev->prof_index = AW88261_INIT_PROFILE;
|
|
|
|
ret = aw88261_dev_fw_update(aw88261);
|
|
if (ret) {
|
|
dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = aw88261_frcset_check(aw88261);
|
|
if (ret) {
|
|
dev_err(aw_dev->dev, "aw88261_frcset_check ret = %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
aw88261_dev_clear_int_status(aw_dev);
|
|
|
|
aw88261_dev_uls_hmute(aw_dev, true);
|
|
|
|
aw88261_dev_mute(aw_dev, true);
|
|
|
|
aw88261_dev_i2s_tx_enable(aw_dev, false);
|
|
|
|
usleep_range(AW88261_1000_US, AW88261_1000_US + 100);
|
|
|
|
aw88261_dev_amppd(aw_dev, true);
|
|
|
|
aw88261_dev_pwd(aw_dev, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw88261_request_firmware_file(struct aw88261 *aw88261)
|
|
{
|
|
const struct firmware *cont = NULL;
|
|
struct aw_container *aw_cfg;
|
|
const char *fw_name;
|
|
int ret;
|
|
|
|
aw88261->aw_pa->fw_status = AW88261_DEV_FW_FAILED;
|
|
|
|
ret = device_property_read_string(aw88261->aw_pa->dev, "firmware-name", &fw_name);
|
|
if (ret)
|
|
fw_name = AW88261_ACF_FILE;
|
|
|
|
ret = request_firmware(&cont, fw_name, aw88261->aw_pa->dev);
|
|
if (ret)
|
|
return dev_err_probe(aw88261->aw_pa->dev, ret,
|
|
"load [%s] failed!", fw_name);
|
|
|
|
dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n",
|
|
fw_name, cont ? cont->size : 0);
|
|
|
|
aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL);
|
|
if (!aw_cfg) {
|
|
release_firmware(cont);
|
|
return -ENOMEM;
|
|
}
|
|
aw_cfg->len = (int)cont->size;
|
|
memcpy(aw_cfg->data, cont->data, cont->size);
|
|
release_firmware(cont);
|
|
|
|
aw88261->aw_cfg = aw_cfg;
|
|
|
|
ret = aw88395_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg);
|
|
if (ret) {
|
|
dev_err(aw88261->aw_pa->dev, "load [%s] failed !", fw_name);
|
|
return ret;
|
|
}
|
|
|
|
mutex_lock(&aw88261->lock);
|
|
/* aw device init */
|
|
ret = aw88261_dev_init(aw88261, aw88261->aw_cfg);
|
|
if (ret)
|
|
dev_err(aw88261->aw_pa->dev, "dev init failed");
|
|
mutex_unlock(&aw88261->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw88261_codec_probe(struct snd_soc_component *component)
|
|
{
|
|
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
|
|
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
|
|
int ret;
|
|
|
|
ret = aw88261_request_firmware_file(aw88261);
|
|
if (ret)
|
|
return dev_err_probe(aw88261->aw_pa->dev, ret,
|
|
"aw88261_request_firmware_file failed\n");
|
|
|
|
/* add widgets */
|
|
ret = snd_soc_dapm_new_controls(dapm, aw88261_dapm_widgets,
|
|
ARRAY_SIZE(aw88261_dapm_widgets));
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* add route */
|
|
ret = snd_soc_dapm_add_routes(dapm, aw88261_audio_map,
|
|
ARRAY_SIZE(aw88261_audio_map));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = snd_soc_add_component_controls(component, aw88261_controls,
|
|
ARRAY_SIZE(aw88261_controls));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct snd_soc_component_driver soc_codec_dev_aw88261 = {
|
|
.probe = aw88261_codec_probe,
|
|
};
|
|
|
|
static void aw88261_parse_channel_dt(struct aw88261 *aw88261)
|
|
{
|
|
struct aw_device *aw_dev = aw88261->aw_pa;
|
|
struct device_node *np = aw_dev->dev->of_node;
|
|
u32 channel_value = AW88261_DEV_DEFAULT_CH;
|
|
|
|
of_property_read_u32(np, "awinic,audio-channel", &channel_value);
|
|
aw88261->phase_sync = of_property_read_bool(np, "awinic,sync-flag");
|
|
|
|
aw_dev->channel = channel_value;
|
|
}
|
|
|
|
static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct regmap *regmap)
|
|
{
|
|
struct aw_device *aw_dev;
|
|
unsigned int chip_id;
|
|
int ret;
|
|
|
|
ret = devm_regulator_get_enable(&i2c->dev, "dvdd");
|
|
if (ret)
|
|
return dev_err_probe(&i2c->dev, ret, "Failed to enable dvdd supply\n");
|
|
|
|
/* read chip id */
|
|
ret = regmap_read(regmap, AW88261_ID_REG, &chip_id);
|
|
if (ret) {
|
|
dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret);
|
|
return ret;
|
|
}
|
|
if (chip_id != AW88261_CHIP_ID) {
|
|
dev_err(&i2c->dev, "unsupported device id = %x", chip_id);
|
|
return -ENXIO;
|
|
}
|
|
|
|
dev_info(&i2c->dev, "chip id = %x\n", chip_id);
|
|
|
|
aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
|
|
if (!aw_dev)
|
|
return -ENOMEM;
|
|
|
|
aw88261->aw_pa = aw_dev;
|
|
aw_dev->i2c = i2c;
|
|
aw_dev->regmap = regmap;
|
|
aw_dev->dev = &i2c->dev;
|
|
aw_dev->chip_id = AW88261_CHIP_ID;
|
|
aw_dev->acf = NULL;
|
|
aw_dev->prof_info.prof_desc = NULL;
|
|
aw_dev->prof_info.count = 0;
|
|
aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
|
|
aw_dev->channel = 0;
|
|
aw_dev->fw_status = AW88261_DEV_FW_FAILED;
|
|
aw_dev->volume_desc.ctl_volume = AW88261_CTL_DEFAULT_VOL;
|
|
aw_dev->volume_desc.mute_volume = AW88261_MUTE_VOL;
|
|
aw88261_parse_channel_dt(aw88261);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw88261_i2c_probe(struct i2c_client *i2c)
|
|
{
|
|
struct aw88261 *aw88261;
|
|
int ret;
|
|
|
|
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
|
|
return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed");
|
|
|
|
aw88261 = devm_kzalloc(&i2c->dev, sizeof(*aw88261), GFP_KERNEL);
|
|
if (!aw88261)
|
|
return -ENOMEM;
|
|
|
|
/* set defaults */
|
|
aw88261->slot_num_value = AW88261_SLOT_NUM_I2S_MODE_VALUE;
|
|
aw88261->sr_value = AW88261_I2SSR_48KHZ_VALUE;
|
|
aw88261->cco_mux_value = AW88261_CCO_MUX_BYPASS_VALUE;
|
|
aw88261->fs_value = AW88261_I2SFS_24_BITS_VALUE;
|
|
aw88261->bck_value = AW88261_I2SBCK_64FS_VALUE;
|
|
aw88261->bck_inv_value = AW88261_BCKINV_NOT_INVERT_VALUE;
|
|
aw88261->tdm_bck_value = AW88261_TDM_BCK_UNSET;
|
|
aw88261->md_value = AW88261_I2SMD_PHILIPS_STANDARD_VALUE;
|
|
aw88261->rxr_slotvld_mask = 1 << AW88261_I2S_RXR_SLOTVLD_START_BIT;
|
|
|
|
mutex_init(&aw88261->lock);
|
|
|
|
i2c_set_clientdata(i2c, aw88261);
|
|
|
|
aw88261->regmap = devm_regmap_init_i2c(i2c, &aw88261_remap_config);
|
|
if (IS_ERR(aw88261->regmap)) {
|
|
ret = PTR_ERR(aw88261->regmap);
|
|
return dev_err_probe(&i2c->dev, ret, "failed to init regmap: %d\n", ret);
|
|
}
|
|
|
|
/* aw pa init */
|
|
ret = aw88261_init(aw88261, i2c, aw88261->regmap);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = devm_snd_soc_register_component(&i2c->dev,
|
|
&soc_codec_dev_aw88261,
|
|
aw88261_dai, ARRAY_SIZE(aw88261_dai));
|
|
if (ret)
|
|
dev_err(&i2c->dev, "failed to register aw88261: %d", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct i2c_device_id aw88261_i2c_id[] = {
|
|
{ .name = "aw88261" },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id);
|
|
|
|
static const struct of_device_id aw88261_of_table[] = {
|
|
{ .compatible = "awinic,aw88261" },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, aw88261_of_table);
|
|
|
|
static struct i2c_driver aw88261_i2c_driver = {
|
|
.driver = {
|
|
.name = "aw88261",
|
|
.of_match_table = aw88261_of_table,
|
|
},
|
|
.probe = aw88261_i2c_probe,
|
|
.id_table = aw88261_i2c_id,
|
|
};
|
|
module_i2c_driver(aw88261_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("ASoC AW88261 Smart PA Driver");
|
|
MODULE_LICENSE("GPL v2");
|