blob: df9031cd5d0297997bab213096f349b88c6d2bb9 [file] [log] [blame]
/*
* Copyright (C) 2019 MediaTek Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See http://www.gnu.org/licenses/gpl-2.0.html for more details.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
/* alsa sound header */
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/pcm_params.h>
#include "rt5509.h"
struct reg_config {
uint8_t reg_addr;
uint32_t reg_data;
};
static const char * const prop_str[RT5509_CFG_MAX] = {
"general",
"boost_converter",
"speaker_protect",
"safe_guard",
"eq",
"bwe",
"dcr",
"mbdrc",
"alc",
};
static const struct reg_config battmode_config[] = {
};
static const struct reg_config adaptive_config[] = {
};
static const struct reg_config general_config[] = {
{ 0x1e, 0x0b},
{ 0x8a, 0x02},
{ 0x94, 0x12},
{ 0x15, 0x00},
{ 0x81, 0x99},
{ 0x50, 0x00},
{ 0x2c, 0x00},
{ 0x5b, 0x00},
{ 0x82, 0x42},
{ 0x83, 0xc0},
{ 0x84, 0xb5},
{ 0x85, 0xd5},
{ 0xb3, 0x00},
{ 0x88, 0x90},
{ 0x96, 0xfd},
{ 0x93, 0x55},
{ 0x26, 0x00},
{ 0x8d, 0xe0},
{ 0x14, 0x7fff},
{ 0xea, 0x81},
{ 0xeb, 0xa1},
{ 0xc3, 0x10},
{ 0xfd, 0xf0},
{ 0xee, 0x06},
{ 0x86, 0x18},
{ 0x13, 0x3bbb},
{ 0x14, 0x6c00},
{ 0x16, 0x4c},
{ 0x18, 0x4a},
{ 0x19, 0x016a},
{ 0x1a, 0x0200},
{ 0x1b, 0x77},
{ 0x1c, 0x70},
{ 0x1d, 0x80},
{ 0x1f, 0x05d3},
{ 0x20, 0x03cd},
{ 0x21, 0x0209},
{ 0x22, 0x07},
{ 0x23, 0x47},
{ 0x24, 0xc1},
{ 0x25, 0x4f},
{ 0x43, 0x44},
{ 0x44, 0xb3},
{ 0x45, 0x1a},
{ 0x46, 0x800000},
{ 0x81, 0xb9},
{ 0x84, 0x35},
{ 0x86, 0x14},
{ 0x87, 0x04},
{ 0x92, 0x57},
{ 0x93, 0x54},
{ 0xc0, 0x20},
{ 0xc2, 0x000097},
{ 0xeb, 0xa2},
{ 0xec, 0x7474},
{ 0xef, 0x001201},
{ 0xf8, 0x55},
{ 0xf9, 0x01},
{ 0xfa, 0x0711},
};
static const struct reg_config revc_general_config[] = {
{ 0x14, 0x6000},
{ 0x15, 0xB8},
{ 0x16, 0x5d},
{ 0x18, 0x0a},
{ 0x19, 0x016a},
{ 0x1a, 0x0000},
{ 0x1c, 0x70},
{ 0x1d, 0x80},
{ 0x1e, 0x0b},
{ 0x1f, 0x085b},
{ 0x20, 0x05cf},
{ 0x21, 0x02c7},
{ 0x22, 0x0d},
{ 0x23, 0x6f},
{ 0x24, 0xb1},
{ 0x25, 0x08},
{ 0x2c, 0x00},
{ 0x2d, 0x0100},
{ 0x2f, 0x0559},
{ 0x45, 0x0b},
{ 0x5a, 0x07dc},
{ 0x81, 0xbb},
{ 0x82, 0x43},
{ 0x83, 0x54},
{ 0x86, 0x3f},
{ 0x87, 0x1f},
{ 0x8a, 0x02},
{ 0x8d, 0xc0},
{ 0x93, 0xd4},
{ 0x94, 0x12},
{ 0x96, 0x0d},
{ 0x97, 0xa5},
{ 0xb8, 0x80},
{ 0xea, 0x91},
{ 0xeb, 0xa4},
{ 0xec, 0x7474},
{ 0xee, 0x06},
{ 0xf8, 0xd7},
{ 0xfd, 0xd0},
{ 0xef, 0x001201},
{ 0xb4, 0x81},
};
static const struct reg_config revd_general_config[] = {
{ 0x14, 0x6800},
{ 0x16, 0x95},
{ 0x18, 0xaa},
{ 0x19, 0x016a},
{ 0x1a, 0x0000},
{ 0x1c, 0x50},
{ 0x1d, 0x00},
{ 0x1e, 0x0b},
{ 0x1f, 0x085b},
{ 0x20, 0x05cf},
{ 0x21, 0x02c7},
{ 0x22, 0x05},
{ 0x24, 0xb1},
{ 0x25, 0x04},
{ 0x2c, 0x00},
{ 0x2f, 0x0559},
{ 0x45, 0x08},
{ 0x5b, 0x00},
{ 0x81, 0xbb},
{ 0x82, 0x43},
{ 0x83, 0x54},
{ 0x86, 0x38},
{ 0x87, 0x1f},
{ 0x92, 0x54},
{ 0x93, 0xd4},
{ 0x94, 0x12},
{ 0x9a, 0xdc},
{ 0xb3, 0x00},
{ 0xea, 0x80},
{ 0xf9, 0x01},
{ 0xfd, 0xd0},
{ 0xef, 0x001201},
{ 0xee, 0x05},
{ 0xb4, 0x81},
{ 0xba, 0x80},
{ 0x2b, 0x67},
};
static int rt5509_block_read(
void *client, u32 reg, int bytes, void *dest)
{
#if RT5509_SIMULATE_DEVICE
struct rt5509_chip *chip = i2c_get_clientdata(client);
int offset = 0, ret = 0;
offset = rt5509_calculate_offset(reg);
if (offset < 0) {
dev_err(chip->dev, "%s: unknown register 0x%02x\n", __func__,
ret);
ret = -EINVAL;
} else
memcpy(dest, chip->sim + offset, bytes);
return ret;
#else
return i2c_smbus_read_i2c_block_data(client, reg, bytes, dest);
#endif /* #if RT5509_SIMULATE_DEVICE */
}
static int rt5509_block_write(void *client, u32 reg,
int bytes, const void *src)
{
#if RT5509_SIMULATE_DEVICE
struct rt5509_chip *chip = i2c_get_clientdata(client);
int offset = 0, ret = 0;
offset = rt5509_calculate_offset(reg);
if (offset < 0) {
dev_err(chip->dev, "%s: unknown register 0x%02x\n", __func__,
ret);
ret = -EINVAL;
} else
memcpy(chip->sim + offset, src, bytes);
return ret;
#else
return i2c_smbus_write_i2c_block_data(client, reg, bytes, src);
#endif /* #if RT5509_SIMULATE_DEVICE */
}
static struct rt_regmap_fops rt5509_regmap_ops = {
.read_device = rt5509_block_read,
.write_device = rt5509_block_write,
};
/* Global read/write function */
static int rt5509_update_bits(struct i2c_client *i2c, u32 reg,
u32 mask, u32 data, int bytes)
{
struct rt5509_chip *chip = i2c_get_clientdata(i2c);
#ifdef CONFIG_RT_REGMAP
struct rt_reg_data rrd;
return rt_regmap_update_bits(chip->rd, &rrd, reg, mask, data);
#else
u32 read_data = 0;
u8 *p_data = (u8 *)&read_data;
int i = 0, j = 0, ret = 0;
down(&chip->io_semaphore);
ret = rt5509_block_read(chip->i2c, reg, bytes, &read_data);
if (ret < 0)
goto err_bits;
j = (bytes / 2);
for (i = 0; i < j; i++)
swap(p_data[i], p_data[bytes - i]);
ret = rt5509_block_write(chip->i2c, reg, bytes, &read_data);
if (ret < 0)
goto err_bits;
err_bits:
up(&chip->io_semaphore);
return ret;
#endif /* #ifdef CONFIG_RT_REGMAP */
}
static int rt5509_set_bits(struct i2c_client *i2c, u32 reg, u8 mask)
{
return rt5509_update_bits(i2c, reg, mask, mask, 1);
}
static int rt5509_clr_bits(struct i2c_client *i2c, u32 reg, u8 mask)
{
return rt5509_update_bits(i2c, reg, mask, 0, 1);
}
static unsigned int rt5509_io_read(struct snd_soc_codec *codec,
unsigned int reg)
{
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
int ret = 0;
#ifdef CONFIG_RT_REGMAP
struct rt_reg_data rrd = {0};
dev_dbg(codec->dev, "%s: reg %02x\n", __func__, reg);
ret = rt_regmap_reg_read(chip->rd, &rrd, reg);
return (ret < 0 ? ret : rrd.rt_data.data_u32);
#else
u8 data = 0;
down(&chip->io_semaphore);
ret = rt5509_block_read(chip->i2c, reg, 1, &data);
up(&chip->io_semaphore);
return (ret < 0 ? ret : data);
#endif /* #ifdef CONFIG_RT_REGMAP */
}
static int rt5509_io_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int data)
{
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
#ifdef CONFIG_RT_REGMAP
struct rt_reg_data rrd = {0};
dev_dbg(codec->dev, "%s: reg %02x data %02x\n", __func__, reg, data);
return rt_regmap_reg_write(chip->rd, &rrd, reg, data);
#else
int ret = 0;
down(&chip->io_semaphore);
ret = rt5509_block_write(chip->i2c, reg, 1, &data);
up(&chip->io_semaphore);
return ret;
#endif /* #ifdef CONFIG_RT_REGMAP */
}
static inline int rt5509_power_on(struct rt5509_chip *chip, bool en)
{
dev_dbg(chip->dev, "%s: en %d\n", __func__, en);
mutex_lock(&chip->var_lock);
if (en) {
if (chip->power_count++ == 0) {
rt5509_clr_bits(chip->i2c, RT5509_REG_CHIPEN,
RT5509_CHIPPD_ENMASK);
mdelay(1);
}
} else {
if (--chip->power_count == 0) {
rt5509_set_bits(chip->i2c, RT5509_REG_CHIPEN,
RT5509_CHIPPD_ENMASK);
}
/* just use to prevent unpaired on off */
if (chip->power_count < 0) {
dev_err(chip->dev, "no paired on off floww\n");
chip->power_count = 0;
}
}
dev_info(chip->dev, "power count = %d\n", chip->power_count);
mutex_unlock(&chip->var_lock);
return 0;
}
static int rt5509_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
int ret = 0;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
dapm->bias_level = level;
break;
case SND_SOC_BIAS_STANDBY:
if (dapm->bias_level != SND_SOC_BIAS_OFF) {
dapm->bias_level = level;
break;
}
ret = rt5509_power_on(chip, true);
if (ret < 0)
goto out_set_bias;
ret = snd_soc_update_bits(codec, RT5509_REG_DSPKCONF5,
RT5509_VBG_ENMASK, RT5509_VBG_ENMASK);
if (ret < 0)
goto out_set_bias;
ret = snd_soc_update_bits(codec, RT5509_REG_DSPKEN1,
RT5509_BIAS_ENMASK, RT5509_BIAS_ENMASK);
if (ret < 0)
goto out_set_bias;
/* initial change to adaptive mode */
ret = snd_soc_update_bits(codec, RT5509_REG_BST_MODE,
0x03, 0x03);
if (ret < 0)
goto out_set_bias;
dapm->bias_level = level;
ret = 0;
break;
case SND_SOC_BIAS_OFF:
/* change to disable mode for power consumption */
ret = snd_soc_update_bits(codec, RT5509_REG_BST_MODE,
0x03, 0x00);
if (ret < 0)
goto out_set_bias;
ret = snd_soc_update_bits(codec, RT5509_REG_DSPKEN1,
RT5509_BIAS_ENMASK, ~RT5509_BIAS_ENMASK);
if (ret < 0)
goto out_set_bias;
ret = snd_soc_update_bits(codec, RT5509_REG_DSPKCONF5,
RT5509_VBG_ENMASK, ~RT5509_VBG_ENMASK);
if (ret < 0)
goto out_set_bias;
ret = snd_soc_read(codec, RT5509_REG_INTERRUPT);
if (ret < 0)
goto out_set_bias;
ret = rt5509_power_on(chip, false);
if (ret < 0)
goto out_set_bias;
dapm->bias_level = level;
ret = 0;
break;
default:
ret = -EINVAL;
}
out_set_bias:
dev_info(codec->dev, "%s: bias_level %d\n", __func__, level);
return ret;
}
static int rt5509_init_battmode_setting(struct snd_soc_codec *codec)
{
int i = 0, ret = 0;
dev_dbg(codec->dev, "%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(battmode_config); i++) {
ret = snd_soc_write(codec, battmode_config[i].reg_addr,
battmode_config[i].reg_data);
if (ret < 0)
break;
}
return ret;
}
static int rt5509_init_adaptive_setting(struct snd_soc_codec *codec)
{
int i = 0, ret = 0;
dev_dbg(codec->dev, "%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(adaptive_config); i++) {
ret = snd_soc_write(codec, adaptive_config[i].reg_addr,
adaptive_config[i].reg_data);
if (ret < 0)
break;
}
return ret;
}
static int rt5509_init_general_setting(struct snd_soc_codec *codec)
{
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
const struct reg_config *reg_cfg = NULL;
int reg_cfg_size = 0;
int i = 0, ret = 0;
dev_dbg(codec->dev, "%s\n", __func__);
if (chip->chip_rev >= RT5509_CHIP_REVD) {
reg_cfg = revd_general_config;
reg_cfg_size = ARRAY_SIZE(revd_general_config);
} else if (chip->chip_rev >= RT5509_CHIP_REVC) {
reg_cfg = revc_general_config;
reg_cfg_size = ARRAY_SIZE(revc_general_config);
} else {
reg_cfg = general_config;
reg_cfg_size = ARRAY_SIZE(general_config);
}
for (i = 0; i < reg_cfg_size; i++) {
ret = snd_soc_write(codec, reg_cfg[i].reg_addr,
reg_cfg[i].reg_data);
if (ret < 0)
break;
}
return ret;
}
static int rt5509_do_tcsense_fix(struct snd_soc_codec *codec)
{
uint32_t tc_sense = 0, vtemp = 0;
int ret = 0;
dev_dbg(codec->dev, "%s\n", __func__);
ret = snd_soc_update_bits(codec, RT5509_REG_MTPFLOWB, 0x40, 0x40);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_OTPCONF, 0x82);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_OTPCONF, 0x00);
if (ret < 0)
return ret;
ret = snd_soc_update_bits(codec, RT5509_REG_MTPFLOWB, 0x40, 0x00);
if (ret < 0)
return ret;
ret = snd_soc_read(codec, RT5509_REG_VTEMP_TRIM);
if (ret < 0)
return ret;
vtemp = ret & 0xffff;
if (vtemp < 0x4250) {
tc_sense = 0xffff;
goto bypass_tcsense_overflow;
}
tc_sense = (1073741824U / vtemp * (273 - 40) / (265 - 40)) << 1;
if (tc_sense & 0x10000)
tc_sense = (tc_sense & 0xffff) + 1;
tc_sense &= 0xffff;
bypass_tcsense_overflow:
dev_dbg(codec->dev, "tc_sense %04x\n", tc_sense);
return snd_soc_write(codec, RT5509_REG_TCOEFF, tc_sense);
}
static int rt5509_adap_coefficent_fix(struct snd_soc_codec *codec)
{
int i = 0, ret = 0;
int64_t x = 0, y = 0, z = 0, w = 0;
dev_dbg(codec->dev, "%s\n", __func__);
ret = snd_soc_read(codec, RT5509_REG_ISENSEGAIN);
ret &= 0xffffff;
dev_info(codec->dev, "gsense otp -> 0x%08x\n", ret);
/* gsense otp value */
x = ret;
ret = snd_soc_read(codec, RT5509_REG_CALIB_DCR);
ret &= 0xffffff;
dev_info(codec->dev, "dcr otp -> 0x%08x\n", ret);
if (ret == 0xffffff)
ret = 0x800000;
/* rspk otp value */
w = ret;
for (i = 0; i < 6; i++) {
ret = snd_soc_read(codec, RT5509_REG_ADAPTB0 + i);
ret &= 0xffffff;
dev_info(codec->dev, "b factor before 0x%08x\n", ret);
/* y = phi factor */
y = ret;
if (ret < 0x800000) {
z = div64_s64(x * 1000000, w) * y;
z = div_s64(z, 1000000);
} else {
y = ((int64_t)0xffffff - y) * div64_s64(x * 1000000, w);
z = (int64_t)0xffffff * (int64_t)1000000;
z = z - y;
z = div_s64(z, 1000000);
}
ret = z & 0xffffff;
dev_info(codec->dev, "b factor after 0x%08x\n", ret);
ret = snd_soc_write(codec, RT5509_REG_ADAPTB0 + i, ret);
if (ret < 0) {
dev_err(codec->dev, "fix b factor fail %d\n", i);
return ret;
}
}
return 0;
}
static int rt5509_init_impedance_ctrl_fix(struct snd_soc_codec *codec)
{
u32 gsense_otp = 0, rspk_otp = 0, result = 0;
int ret = 0;
dev_dbg(codec->dev, "%s\n", __func__);
ret = snd_soc_read(codec, RT5509_REG_ISENSEGAIN);
if (ret == 0)
ret = 0x800000;
gsense_otp = ret & 0xffffff;
dev_dbg(codec->dev, "gsense otp 0x%08x\n", gsense_otp);
ret = snd_soc_read(codec, RT5509_REG_CALIB_DCR);
if (ret == 0)
ret = 0x800000;
rspk_otp = ret & 0xffffff;
dev_dbg(codec->dev, "rspk otp 0x%08x\n", rspk_otp);
result = ((rspk_otp << 7) / gsense_otp) << 16;
result &= 0xffffff;
dev_dbg(codec->dev, "final result 0x%08x\n", result);
return snd_soc_write(codec, RT5509_REG_DELAYRES, result);
}
static int rt5509_init_proprietary_setting(struct snd_soc_codec *codec)
{
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
struct rt5509_proprietary_param *p_param = chip->pdata->p_param;
const u8 *cfg = NULL;
u32 cfg_size = 0;
int i = 0, j = 0;
int ret = 0;
dev_dbg(codec->dev, "%s\n", __func__);
if (!p_param)
goto out_init_proprietary;
for (i = 0; i < RT5509_CFG_MAX; i++) {
cfg = p_param->cfg[i];
cfg_size = p_param->cfg_size[i];
if (!cfg)
continue;
dev_dbg(chip->dev, "%s start\n", prop_str[i]);
for (j = 0; j < cfg_size;) {
#ifdef CONFIG_RT_REGMAP
ret = rt_regmap_block_write(chip->rd, cfg[0], cfg[1],
cfg + 2);
#else
ret = rt5509_block_write(chip->i2c, cfg[0], cfg[1],
cfg + 2);
#endif /* #ifdef CONFIG_RT_REGMAP */
if (ret < 0)
dev_err(chip->dev, "set %02x fail\n", cfg[0]);
j += (2 + cfg[1]);
cfg += (2 + cfg[1]);
}
dev_dbg(chip->dev, "%s end\n", prop_str[i]);
}
ret = rt5509_adap_coefficent_fix(codec);
if (ret < 0)
dev_err(chip->dev, "fix adap coefficient fail\n");
ret = rt5509_init_impedance_ctrl_fix(codec);
if (ret < 0)
dev_err(chip->dev, "init impedance ctrl fix fail\n");
if (p_param->cfg_size[RT5509_CFG_SPEAKERPROT]) {
ret = snd_soc_update_bits(codec, RT5509_REG_CHIPEN,
RT5509_SPKPROT_ENMASK, RT5509_SPKPROT_ENMASK);
}
out_init_proprietary:
return ret;
}
static ssize_t rt5509_proprietary_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct rt5509_chip *chip = dev_get_drvdata(dev);
struct rt5509_proprietary_param *param = chip->pdata->p_param;
int i = 0, j = 0;
const u8 *cfg = NULL;
u32 cfg_size = 0;
dev_dbg(chip->dev, "%s\n", __func__);
if (!param) {
i += scnprintf(buf + i, PAGE_SIZE - i, "no proprietary parm\n");
goto out_show;
}
for (j = 0; j < RT5509_CFG_MAX; j++) {
cfg = param->cfg[j];
cfg_size = param->cfg_size[j];
if (!cfg) {
i += scnprintf(buf + i, PAGE_SIZE - i, "no %s cfg\n",
prop_str[j]);
continue;
}
i += scnprintf(buf + i, PAGE_SIZE - i, "%s size %d\n",
prop_str[j], cfg_size);
}
out_show:
return i;
}
static ssize_t rt5509_proprietary_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t cnt)
{
struct rt5509_chip *chip = dev_get_drvdata(dev);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(chip->codec);
struct rt5509_proprietary_param *param = NULL;
int i = 0, size = 0;
const u8 *bin_offset = NULL;
u32 *sptr;
dev_dbg(chip->dev, "%s, size %d\n", __func__, (int)cnt);
/* do mark check */
if (cnt < 7) {
dev_err(chip->dev, "data is invalid\n");
goto out_param_write;
}
if (strncmp("richtek", buf, 7)) {
dev_err(chip->dev, "data is invalid\n");
goto out_param_write;
}
/* do size check */
sptr = (u32 *)(buf + cnt - 4);
size = *sptr;
dev_dbg(chip->dev, "size %d\n", size);
if (cnt < 47 || (size + 47) != cnt) {
dev_err(chip->dev, "sorry bin size is wrong\n");
goto out_param_write;
}
for (i = 0; i < RT5509_CFG_MAX; i++) {
sptr = (u32 *)(buf + cnt - 40 + i * 4);
size -= *sptr;
}
if (size != 0) {
dev_err(chip->dev, "sorry, bin format is wrong\n");
goto out_param_write;
}
/* if previous one is existed, release it */
param = chip->pdata->p_param;
if (param) {
dev_dbg(chip->dev, "previous existed\n");
for (i = 0; i < RT5509_CFG_MAX; i++)
devm_kfree(chip->dev, param->cfg[i]);
devm_kfree(chip->dev, param);
chip->pdata->p_param = NULL;
}
/* start to copy */
param = devm_kzalloc(chip->dev, sizeof(*param), GFP_KERNEL);
if (!param)
goto out_param_write;
bin_offset = buf + 7;
for (i = 0; i < RT5509_CFG_MAX; i++) {
sptr = (u32 *)(buf + cnt - 40 + i * 4);
param->cfg_size[i] = *sptr;
param->cfg[i] = devm_kzalloc(chip->dev,
sizeof(u8) * param->cfg_size[i],
GFP_KERNEL);
memcpy(param->cfg[i], bin_offset, param->cfg_size[i]);
bin_offset += param->cfg_size[i];
}
chip->pdata->p_param = param;
if (dapm->bias_level != SND_SOC_BIAS_OFF)
goto out_param_write;
rt5509_power_on(chip, true);
rt5509_do_tcsense_fix(chip->codec);
rt5509_init_proprietary_setting(chip->codec);
rt5509_power_on(chip, false);
return cnt;
out_param_write:
return -EINVAL;
}
static struct device_attribute rt5509_proprietary_attr = {
.attr = {
.name = "prop_param",
.mode = 0644,
},
.show = rt5509_proprietary_show,
.store = rt5509_proprietary_store,
};
struct rt5509_param_platform_data {
struct rt5509_chip *chip;
};
static int rt5509_param_probe(struct platform_device *pdev)
{
struct rt5509_param_platform_data *pdata = dev_get_platdata(&pdev->dev);
int ret = 0;
ret = device_create_file(&pdev->dev, &rt5509_proprietary_attr);
if (ret < 0) {
dev_err(&pdev->dev, "create file error\n");
return ret;
}
platform_set_drvdata(pdev, pdata->chip);
return 0;
}
static int rt5509_param_remove(struct platform_device *pdev)
{
device_remove_file(&pdev->dev, &rt5509_proprietary_attr);
return 0;
}
static struct platform_driver rt5509_param_driver = {
.driver = {
.name = "rt5509_param",
.owner = THIS_MODULE,
},
.probe = rt5509_param_probe,
.remove = rt5509_param_remove,
};
static int rt5509_param_create(struct rt5509_chip *chip)
{
struct rt5509_param_platform_data param_data;
param_data.chip = chip;
chip->pdev = platform_device_register_data(NULL, "rt5509_param",
chip->dev_cnt, &param_data,
sizeof(param_data));
if (!chip->pdev)
return -EFAULT;
return 0;
}
static void rt5509_param_destroy(struct rt5509_chip *chip)
{
platform_device_unregister(chip->pdev);
}
static int rt5509_codec_probe(struct snd_soc_codec *codec)
{
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
int ret = 0;
dev_dbg(codec->dev, "%s\n", __func__);
/* CHIP Enable */
rt5509_power_on(chip, true);
ret = rt5509_init_general_setting(codec);
if (ret < 0)
goto err_out_probe;
ret = rt5509_init_adaptive_setting(codec);
if (ret < 0)
goto err_out_probe;
ret = rt5509_init_battmode_setting(codec);
if (ret < 0)
goto err_out_probe;
ret = rt5509_do_tcsense_fix(codec);
if (ret < 0)
goto err_out_probe;
ret = rt5509_init_proprietary_setting(codec);
if (ret < 0)
goto err_out_probe;
ret = rt5509_param_create(chip);
if (ret < 0)
goto err_out_probe;
chip->codec = codec;
ret = rt5509_calib_create(chip);
if (ret < 0)
goto err_out_probe;
dev_info(codec->dev, "%s\n", __func__);
return rt5509_set_bias_level(codec, SND_SOC_BIAS_OFF);
err_out_probe:
dev_info(codec->dev, "chip io error\n");
/* Chip Disable */
ret = snd_soc_update_bits(codec, RT5509_REG_CHIPEN,
RT5509_CHIPPD_ENMASK, RT5509_CHIPPD_ENMASK);
return ret < 0 ? ret : -EINVAL;
}
static int rt5509_codec_remove(struct snd_soc_codec *codec)
{
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
rt5509_calib_destroy(chip);
rt5509_param_destroy(chip);
return rt5509_set_bias_level(codec, SND_SOC_BIAS_OFF);
}
#ifdef CONFIG_PM
static int rt5509_codec_suspend(struct snd_soc_codec *codec)
{
return 0;
}
static int rt5509_codec_resume(struct snd_soc_codec *codec)
{
return 0;
}
#else
#define rt5509_codec_suspend NULL
#define rt5509_codec_resume NULL
#endif /* #ifdef CONFIG_PM */
static int rt5509_clk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
int ret = 0;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
ret = snd_soc_read(codec, RT5509_REG_OTPCONF);
if (ret < 0)
return ret;
/* check bit 6 and 5 */
ret &= 0x60;
if (ret) {
dev_err(codec->dev, "MTP check fail, val 0x%x\n", ret);
return -EINVAL;
}
ret = snd_soc_update_bits(codec, RT5509_REG_CLKEN1,
0x40, 0x00);
if (ret < 0)
return ret;
break;
default:
break;
}
return 0;
}
static int rt5509_boost_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
int ret = 0;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
ret = snd_soc_update_bits(codec, RT5509_REG_DSPKVMID,
RT5509_VMID_ENMASK, RT5509_VMID_ENMASK);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_CHIPEN,
RT5509_TRIWAVE_ENMASK, RT5509_TRIWAVE_ENMASK);
if (ret < 0)
goto out_boost_event;
mdelay(1);
ret = snd_soc_update_bits(codec, RT5509_REG_MSKFLAG,
0x3F, 0x00);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_BSTTM,
0x40, 0x40);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_OCPOTPEN,
0x03, 0x03);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_write(codec, RT5509_REG_OCPMAX, 0x7f);
if (ret < 0)
goto out_boost_event;
if (chip->chip_rev >= RT5509_CHIP_REVD) {
ret = snd_soc_update_bits(codec, RT5509_REG_OVPUVPCTRL,
0xe0, 0xe0);
if (ret < 0)
goto out_boost_event;
} else {
ret = snd_soc_update_bits(codec, RT5509_REG_OVPUVPCTRL,
0x60, 0x60);
if (ret < 0)
goto out_boost_event;
}
ret = snd_soc_update_bits(codec, RT5509_REG_CLKEN1,
0x40, 0x40);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_write(codec, RT5509_REG_DSPKCONF4, 0xd9);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_MSKFLAG,
0x3F, 0x3F);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_BLOCKREF1,
0x0300, 0x0100);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_MTPFLOWA,
0x10, 0x10);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_DSPKEN1,
RT5509_BUF_ENMASK, RT5509_BUF_ENMASK);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_AMPCONF,
0x80, 0x80);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_VBATSENSE,
0x20, 0x20);
if (ret < 0)
goto out_boost_event;
if (chip->chip_rev >= RT5509_CHIP_REVD) {
ret = snd_soc_update_bits(codec,
RT5509_REG_BIASRESISTOR, 0x0001, 0x0000);
if (ret < 0)
goto out_boost_event;
}
mdelay(6);
ret = snd_soc_update_bits(codec, RT5509_REG_AMPCONF,
0x38, 0x38);
if (ret < 0)
goto out_boost_event;
/* change to battery mode to prevent pop */
ret = snd_soc_update_bits(codec, RT5509_REG_BST_MODE,
0x03, 0x01);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_write(codec, RT5509_REG_ISENSE_CTRL, 0x97);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_MTPFLOWA,
0x04, 0x04);
if (ret < 0)
goto out_boost_event;
mdelay(1);
dev_info(chip->dev, "amp turn on\n");
break;
case SND_SOC_DAPM_POST_PMU:
mdelay(11);
ret = snd_soc_update_bits(codec, RT5509_REG_PILOTNISENSE,
0x07, 0x03);
if (ret < 0)
goto out_boost_event;
if (chip->chip_rev >= RT5509_CHIP_REVD) {
ret = snd_soc_update_bits(codec, RT5509_REG_MTPFLOWA,
0x01, 0x01);
if (ret < 0)
goto out_boost_event;
}
/* change to adaptive mode after analog part ready */
ret = snd_soc_update_bits(codec, RT5509_REG_BST_MODE,
0x03, 0x03);
if (ret < 0)
goto out_boost_event;
if (!chip->recv_spec_set) {
ret = snd_soc_update_bits(codec, RT5509_REG_PILOTEN,
0x01, 0x01);
if (ret < 0)
goto out_boost_event;
}
break;
case SND_SOC_DAPM_PRE_PMD:
ret = snd_soc_read(codec, RT5509_REG_CHIPEN);
if (ret < 0)
goto out_boost_event;
if (ret & RT5509_SPKPROT_ENMASK) {
ret = snd_soc_update_bits(codec, RT5509_REG_CHIPEN,
RT5509_SPKPROT_ENMASK, ~RT5509_SPKPROT_ENMASK);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_CHIPEN,
RT5509_SPKPROT_ENMASK, RT5509_SPKPROT_ENMASK);
if (ret < 0)
goto out_boost_event;
}
ret = snd_soc_update_bits(codec, RT5509_REG_PILOTEN,
0x01, 0x00);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_PILOTNISENSE,
0x07, 0x00);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_DSPKCONF1,
0x20, 0x20);
if (ret < 0)
goto out_boost_event;
mdelay(1);
break;
case SND_SOC_DAPM_POST_PMD:
dev_info(chip->dev, "amp turn off\n");
mdelay(1);
ret = snd_soc_update_bits(codec, RT5509_REG_DSPKCONF1,
0x20, 0x00);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_CHIPEN,
RT5509_SPKAMP_ENMASK, RT5509_SPKAMP_ENMASK);
if (ret < 0)
goto out_boost_event;
mdelay(11);
ret = snd_soc_update_bits(codec, RT5509_REG_DSPKCONF1,
0x20, 0x20);
if (ret < 0)
goto out_boost_event;
mdelay(1);
ret = snd_soc_update_bits(codec, RT5509_REG_CHIPEN,
RT5509_SPKAMP_ENMASK, ~RT5509_SPKAMP_ENMASK);
if (ret < 0)
goto out_boost_event;
mdelay(1);
ret = snd_soc_update_bits(codec, RT5509_REG_DSPKCONF1,
0x20, 0x00);
if (ret < 0)
goto out_boost_event;
mdelay(1);
ret = snd_soc_update_bits(codec, RT5509_REG_MTPFLOWA,
0x05, 0x00);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_write(codec, RT5509_REG_ISENSE_CTRL, 0x03);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_AMPCONF,
0x38, 0x00);
if (ret < 0)
goto out_boost_event;
if (chip->chip_rev >= RT5509_CHIP_REVD) {
ret = snd_soc_update_bits(codec,
RT5509_REG_BIASRESISTOR, 0x0001, 0x0001);
if (ret < 0)
goto out_boost_event;
}
ret = snd_soc_update_bits(codec, RT5509_REG_VBATSENSE,
0x20, 0x00);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_AMPCONF,
0x80, 0x00);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_DSPKEN1,
RT5509_BUF_ENMASK, ~RT5509_BUF_ENMASK);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_MTPFLOWA,
0x10, 0x00);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_BLOCKREF1,
0x0300, 0x0000);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_write(codec, RT5509_REG_DSPKCONF4, 0x15);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_OVPUVPCTRL,
0xe0, 0x00);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_write(codec, RT5509_REG_OCPMAX, 0x00);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_OCPOTPEN,
0x03, 0x00);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_BSTTM,
0x40, 0x00);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_CHIPEN,
RT5509_TRIWAVE_ENMASK, ~RT5509_TRIWAVE_ENMASK);
if (ret < 0)
goto out_boost_event;
ret = snd_soc_update_bits(codec, RT5509_REG_DSPKVMID,
RT5509_VMID_ENMASK, ~RT5509_VMID_ENMASK);
if (ret < 0)
goto out_boost_event;
break;
default:
break;
}
out_boost_event:
return ret;
}
static const char * const rt5509_i2smux_text[] = { "I2S1", "I2S2"};
static const char * const rt5509_i2sdomux_text[] = { "I2SDOR/L", "DATAI3"};
static SOC_ENUM_SINGLE_DECL(rt5509_i2s_muxsel,
SND_SOC_NOPM, 0, rt5509_i2smux_text);
static SOC_ENUM_SINGLE_DECL(rt5509_i2s_dosel,
RT5509_REG_I2SDOSEL, 1, rt5509_i2sdomux_text);
static const struct snd_kcontrol_new rt5509_i2smux_ctrl =
SOC_DAPM_ENUM("Switch", rt5509_i2s_muxsel);
static const struct snd_kcontrol_new rt5509_i2sdo_ctrl =
SOC_DAPM_ENUM("Switch", rt5509_i2s_dosel);
static const struct snd_soc_dapm_widget rt5509_dapm_widgets[] = {
SND_SOC_DAPM_MUX("I2S Mux", SND_SOC_NOPM, 0, 0, &rt5509_i2smux_ctrl),
SND_SOC_DAPM_MUX("I2SDO Mux", RT5509_REG_I2SDOSEL, 0, 0,
&rt5509_i2sdo_ctrl),
SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_PGA("PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_OUT_DRV_E("BOOST", RT5509_REG_CHIPEN, RT5509_SPKAMP_ENSHFT,
0, NULL, 0, rt5509_boost_event, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("CLK", RT5509_REG_PLLCONF1, 0, 1, rt5509_clk_event,
SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_SPK("Speaker", NULL),
};
static const struct snd_soc_dapm_route rt5509_dapm_routes[] = {
{ "I2S Mux", "I2S1", "AIF1 Playback"},
{ "I2S Mux", "I2S2", "AIF2 Playback"},
/* DATAO path start */
{ "I2SDO Mux", "I2SDOR/L", "I2S Mux"},
{ "I2SDO Mux", "DATAI3", "I2S Mux"},
{ "AIF1 Capture", NULL, "I2SDO Mux"},
/* DATAO path end */
{ "DAC", NULL, "I2S Mux"},
{ "DAC", NULL, "CLK"},
{ "PGA", NULL, "DAC"},
{ "BOOST", NULL, "PGA"},
{ "Speaker", NULL, "BOOST"},
};
static int rt5509_alcfixed_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
int ret = 0;
if (!chip->rlr_func)
return -EINVAL;
ret = snd_soc_read(codec, RT5509_REG_ALCGAIN);
if (ret < 0)
return ret;
ucontrol->value.integer.value[0] = ret & 0x0f;
return 0;
}
static int rt5509_alcfixed_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
struct soc_enum *se = (struct soc_enum *)kcontrol->private_value;
int val = 0, ret = 0;
if (ucontrol->value.enumerated.item[0] >= se->items)
return -EINVAL;
if (!chip->rlr_func)
return -EINVAL;
rt5509_power_on(chip, true);
val = ucontrol->value.enumerated.item[0];
ret = snd_soc_write(codec, RT5509_REG_ALCMINGAIN, val);
if (ret < 0)
return ret;
val += (val << 4);
ret = snd_soc_write(codec, RT5509_REG_ALCGAIN, val);
if (ret < 0)
return ret;
rt5509_power_on(chip, false);
return 0;
}
static int rt5509_rlrfunc_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = chip->rlr_func;
return 0;
}
static int rt5509_rlrfunc_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
struct soc_enum *se = (struct soc_enum *)kcontrol->private_value;
int orig_proton = 0, ret = 0;
if (ucontrol->value.enumerated.item[0] >= se->items)
return -EINVAL;
if (ucontrol->value.enumerated.item[0] == chip->rlr_func)
return 0;
ret = snd_soc_read(codec, RT5509_REG_CHIPEN);
if (ret < 0)
return ret;
orig_proton = (ret & RT5509_SPKPROT_ENMASK) ? 1 : 0;
rt5509_power_on(chip, true);
ret = snd_soc_update_bits(codec, RT5509_REG_CHIPEN,
RT5509_SPKPROT_ENMASK, 0);
if (ret < 0)
return ret;
if (ucontrol->value.enumerated.item[0]) {
ret = snd_soc_update_bits(codec, RT5509_REG_FUNCEN,
0x1f, 0x12);
if (ret < 0)
return ret;
ret = snd_soc_read(codec, RT5509_REG_FUNCEN);
if (ret < 0)
return ret;
if (!(ret & 0x80)) {
ret = snd_soc_read(codec, RT5509_REG_NDELAY);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_NDELAY,
ret + 0x128f5c);
if (ret < 0)
return ret;
}
ret = snd_soc_read(codec, RT5509_REG_ALCGAIN);
if (ret < 0)
return ret;
chip->alc_gain = (u8)ret;
ret = snd_soc_read(codec, RT5509_REG_ALCMINGAIN);
if (ret < 0)
return ret;
chip->alc_min_gain = (u8)ret;
ret = snd_soc_write(codec, RT5509_REG_ALCGAIN, 0x00);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_ALCMINGAIN, 0x00);
if (ret < 0)
return ret;
} else {
ret = snd_soc_update_bits(codec, RT5509_REG_FUNCEN,
0x1f, 0x1f);
if (ret < 0)
return ret;
ret = snd_soc_read(codec, RT5509_REG_FUNCEN);
if (ret < 0)
return ret;
if (!(ret & 0x80)) {
ret = snd_soc_read(codec, RT5509_REG_NDELAY);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_NDELAY,
ret - 0x128f5c);
if (ret < 0)
return ret;
}
ret = snd_soc_write(codec, RT5509_REG_ALCGAIN, chip->alc_gain);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_ALCMINGAIN,
chip->alc_min_gain);
if (ret < 0)
return ret;
}
if (orig_proton) {
ret = snd_soc_update_bits(codec, RT5509_REG_CHIPEN,
RT5509_SPKPROT_ENMASK, 0xff);
if (ret < 0)
return ret;
}
rt5509_power_on(chip, false);
chip->rlr_func = ucontrol->value.enumerated.item[0];
return 0;
}
static int rt5509_recv_config_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = chip->recv_spec_set;
return 0;
}
static int rt5509_recv_config_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
struct soc_enum *se = (struct soc_enum *)kcontrol->private_value;
int orig_pwron = 0, ret = 0;
if (ucontrol->value.enumerated.item[0] >= se->items)
return -EINVAL;
if (ucontrol->value.enumerated.item[0] == chip->recv_spec_set)
return 0;
ret = snd_soc_read(codec, RT5509_REG_CHIPEN);
if (ret < 0)
return ret;
orig_pwron = (ret & RT5509_CHIPPD_ENMASK) ? 0 : 1;
rt5509_power_on(chip, true);
if (ucontrol->value.enumerated.item[0]) {
/* backup gain ++ */
ret = snd_soc_read(codec, RT5509_REG_SPKGAIN);
if (ret < 0)
return ret;
chip->classd_gain_store = ret;
ret = snd_soc_read(codec, RT5509_REG_DSPKCONF1);
if (ret < 0)
return ret;
chip->pgain_gain_store = ret;
ret = snd_soc_read(codec, RT5509_REG_BST_SIG_GAIN);
if (ret < 0)
return ret;
chip->sig_gain_store = ret;
ret = snd_soc_read(codec, RT5509_REG_CLIP_SIGMAX);
if (ret < 0)
return ret;
chip->sig_max_store = ret;
/* backup gain -- */
/* default set to model 1 */
ret = snd_soc_update_bits(codec, RT5509_REG_SPKGAIN,
0xe0, 0x00);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_BST_SIG_GAIN, 0x12);
if (ret < 0)
return ret;
ret = snd_soc_update_bits(codec, RT5509_REG_DSPKCONF1,
0x03, 0x02);
if (ret < 0)
return ret;
ret = snd_soc_update_bits(codec, RT5509_REG_CLIP_CTRL,
0x80, 0x00);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_CLIP_SIGMAX, 0x7fff);
if (ret < 0)
return ret;
if (orig_pwron) {
ret = snd_soc_update_bits(codec, RT5509_REG_PILOTEN,
0x01, 0x00);
if (ret < 0)
return ret;
}
ret = snd_soc_update_bits(codec, RT5509_REG_TDEN, 0x20, 0x00);
if (ret < 0)
return ret;
} else {
ret = snd_soc_update_bits(codec, RT5509_REG_TDEN, 0x20, 0x20);
if (ret < 0)
return ret;
if (orig_pwron) {
ret = snd_soc_update_bits(codec, RT5509_REG_PILOTEN,
0x01, 0x00);
if (ret < 0)
return ret;
}
/* restore gain ++ */
ret = snd_soc_write(codec, RT5509_REG_CLIP_SIGMAX,
chip->sig_max_store);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_BST_SIG_GAIN,
chip->sig_gain_store);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_DSPKCONF1,
chip->pgain_gain_store);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_SPKGAIN,
chip->classd_gain_store);
if (ret < 0)
return ret;
/* restore gain -- */
}
rt5509_power_on(chip, false);
chip->recv_spec_set = ucontrol->value.enumerated.item[0];
return 0;
}
static int rt5509_bypassdsp_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = chip->bypass_dsp;
return 0;
}
static int rt5509_bypassdsp_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
struct soc_enum *se = (struct soc_enum *)kcontrol->private_value;
int ret = 0;
if (ucontrol->value.enumerated.item[0] >= se->items)
return -EINVAL;
if (ucontrol->value.enumerated.item[0] == chip->bypass_dsp)
return 0;
rt5509_power_on(chip, true);
if (ucontrol->value.enumerated.item[0]) {
ret = snd_soc_read(codec, RT5509_REG_FUNCEN);
if (ret < 0)
return ret;
chip->func_en = ret;
ret = snd_soc_read(codec, RT5509_REG_CHIPEN);
if (ret < 0)
return ret;
chip->spk_prot_en = ret & RT5509_SPKPROT_ENMASK;
ret = snd_soc_write(codec, RT5509_REG_FUNCEN, 0);
if (ret < 0)
return ret;
ret = snd_soc_update_bits(codec, RT5509_REG_CHIPEN,
RT5509_SPKPROT_ENMASK, 0);
if (ret < 0)
return ret;
} else {
ret = snd_soc_write(codec, RT5509_REG_FUNCEN, chip->func_en);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_FUNCEN, 0);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_FUNCEN, chip->func_en);
if (ret < 0)
return ret;
ret = snd_soc_update_bits(codec, RT5509_REG_CHIPEN,
RT5509_SPKPROT_ENMASK,
chip->spk_prot_en);
if (ret < 0)
return ret;
}
rt5509_power_on(chip, false);
chip->bypass_dsp = ucontrol->value.enumerated.item[0];
return 0;
}
static int rt5509_put_spk_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
int ret = 0;
rt5509_power_on(chip, true);
ret = snd_soc_put_volsw(kcontrol, ucontrol);
if (ret < 0)
return ret;
rt5509_power_on(chip, false);
return 0;
}
static int rt5509_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
int ret = 0;
rt5509_power_on(chip, true);
ret = snd_soc_put_enum_double(kcontrol, ucontrol);
if (ret < 0)
return ret;
rt5509_power_on(chip, false);
return 0;
}
static int rt5509_recv_model_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
int ret = 0;
if (!chip->recv_spec_set)
return -EINVAL;
ret = snd_soc_read(codec, RT5509_REG_SPKGAIN);
if (ret < 0)
return ret;
ucontrol->value.integer.value[0] = (ret & 0xe0) >> 5;
return 0;
}
static int rt5509_recv_model_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
struct soc_enum *se = (struct soc_enum *)kcontrol->private_value;
int ret = 0;
if (ucontrol->value.enumerated.item[0] >= se->items)
return -EINVAL;
if (!chip->recv_spec_set)
return -EINVAL;
rt5509_power_on(chip, true);
if (ucontrol->value.enumerated.item[0]) {
ret = snd_soc_update_bits(codec, RT5509_REG_SPKGAIN,
0xe0, 0x20);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_BST_SIG_GAIN, 0x1a);
if (ret < 0)
return ret;
} else {
ret = snd_soc_update_bits(codec, RT5509_REG_SPKGAIN,
0xe0, 0x00);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_BST_SIG_GAIN, 0x12);
if (ret < 0)
return ret;
}
rt5509_power_on(chip, false);
return 0;
}
static int rt5509_put_calib_start(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
struct rt5509_calib_classdev *cdev = &chip->calib_dev;
int ret = 0;
if (ucontrol->value.integer.value[0] != 5526799)
return -EINVAL;
ret = cdev->trigger_read(cdev);
if (ret < 0) {
dev_err(cdev->dev, "failed to trigger_read action\n");
return ret;
}
ret = cdev->trigger_calculation(cdev);
if (ret < 0) {
dev_err(cdev->dev, "failed to trigger_calculation action\n");
return ret;
}
ret = cdev->trigger_write(cdev);
if (ret < 0) {
dev_err(cdev->dev, "failed to trigger_write action\n");
return ret;
}
return 0;
}
static int rt5509_get_calib_flag(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = chip->calibrated;
return 0;
}
static const DECLARE_TLV_DB_SCALE(dacvol_tlv, -1275, 5, 0);
static const char * const rt5509_enable_text[] = { "Disable", "Enable"};
static const char * const rt5509_slots_text[] = { "Slot 0", "Slot 1"};
static const char * const rt5509_alcgain_text[] = {
"0dB", "3dB", "6dB", "9dB", "12dB", "15dB", "18dB", "21dB" };
static const DECLARE_TLV_DB_SCALE(predspvol_tlv, 0, 6, 0);
static const char * const rt5509_recvmodel_text[] = { "model1", "model2"};
static const struct soc_enum rt5509_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rt5509_enable_text), rt5509_enable_text),
SOC_ENUM_SINGLE(RT5509_REG_TDM_CTRL, 2, ARRAY_SIZE(rt5509_slots_text),
rt5509_slots_text),
SOC_ENUM_SINGLE(RT5509_REG_TDM_CTRL, 1, ARRAY_SIZE(rt5509_slots_text),
rt5509_slots_text),
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rt5509_alcgain_text),
rt5509_alcgain_text),
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rt5509_recvmodel_text),
rt5509_recvmodel_text),
};
static const struct snd_kcontrol_new rt5509_controls[] = {
SOC_SINGLE_EXT_TLV("DAC Volume", RT5509_REG_VOLUME, 0, 255, 1,
snd_soc_get_volsw, rt5509_put_spk_volsw, dacvol_tlv),
SOC_SINGLE_EXT("Speaker Protection", RT5509_REG_CHIPEN,
RT5509_SPKPROT_ENSHFT,
1, 0, snd_soc_get_volsw, rt5509_put_spk_volsw),
SOC_SINGLE_EXT("Limiter Func", RT5509_REG_FUNCEN,
RT5509_LMTEN_SHFT, 1, 0,
snd_soc_get_volsw, rt5509_put_spk_volsw),
SOC_SINGLE_EXT("ALC Func", RT5509_REG_FUNCEN, RT5509_ALCEN_SHFT, 1, 0,
snd_soc_get_volsw, rt5509_put_spk_volsw),
SOC_SINGLE_EXT("CLIP Func", RT5509_REG_CLIP_CTRL,
RT5509_CLIPEN_SHFT, 1, 0,
snd_soc_get_volsw, rt5509_put_spk_volsw),
SOC_SINGLE_EXT("BoostMode", RT5509_REG_BST_MODE,
RT5509_BSTMODE_SHFT, 3, 0,
snd_soc_get_volsw, rt5509_put_spk_volsw),
SOC_SINGLE_EXT("I2S_Channel", RT5509_REG_I2SSEL,
RT5509_I2SLRSEL_SHFT, 3, 0,
snd_soc_get_volsw, rt5509_put_spk_volsw),
SOC_SINGLE_EXT("Ext_DO_Enable", RT5509_REG_I2SDOSEL, 0, 1, 0,
snd_soc_get_volsw, rt5509_put_spk_volsw),
SOC_SINGLE_EXT("I2SDOL Mux", RT5509_REG_I2SDOLRSEL, 0, 15, 0,
snd_soc_get_volsw, rt5509_put_spk_volsw),
SOC_SINGLE_EXT("I2SDOR Mux", RT5509_REG_I2SDOLRSEL, 4, 15, 0,
snd_soc_get_volsw, rt5509_put_spk_volsw),
SOC_ENUM_EXT("BypassDSP", rt5509_enum[0], rt5509_bypassdsp_get,
rt5509_bypassdsp_put),
SOC_ENUM_EXT("Recv_Special_Set", rt5509_enum[0], rt5509_recv_config_get,
rt5509_recv_config_put),
SOC_ENUM_EXT("RLR Func", rt5509_enum[0], rt5509_rlrfunc_get,
rt5509_rlrfunc_put),
SOC_ENUM_EXT("TDM_ADC_SEL", rt5509_enum[1], snd_soc_get_enum_double,
rt5509_put_enum_double),
SOC_ENUM_EXT("TDM_DAC_SEL", rt5509_enum[2], snd_soc_get_enum_double,
rt5509_put_enum_double),
SOC_ENUM_EXT("ALC Fixed Gain", rt5509_enum[3], rt5509_alcfixed_gain_get,
rt5509_alcfixed_gain_put),
SOC_SINGLE_EXT_TLV("PreDSP Volume", RT5509_REG_ALCMINGAIN, 4, 2, 0,
snd_soc_get_volsw, rt5509_put_spk_volsw, predspvol_tlv),
SOC_ENUM_EXT("Recv_Model_Set", rt5509_enum[4], rt5509_recv_model_get,
rt5509_recv_model_put),
SOC_SINGLE_EXT("Calib_Start", SND_SOC_NOPM, 0, 0, 0,
rt5509_get_calib_flag, rt5509_put_calib_start),
};
static const struct snd_soc_codec_driver rt5509_codec_drv = {
.probe = rt5509_codec_probe,
.remove = rt5509_codec_remove,
.suspend = rt5509_codec_suspend,
.resume = rt5509_codec_resume,
.component_driver = {
.controls = rt5509_controls,
.num_controls = ARRAY_SIZE(rt5509_controls),
.dapm_widgets = rt5509_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rt5509_dapm_widgets),
.dapm_routes = rt5509_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt5509_dapm_routes),
},
.set_bias_level = rt5509_set_bias_level,
.idle_bias_off = true,
/* codec io */
.read = rt5509_io_read,
.write = rt5509_io_write,
};
static int rt5509_aif_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
u8 regval = 0;
int ret = 0;
dev_dbg(dai->dev, "%s: fmt:%d\n", __func__, fmt);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
regval |= (RT5509_AUDFMT_I2S << RT5509_AUDFMT_SHFT);
break;
case SND_SOC_DAIFMT_RIGHT_J:
regval |= (RT5509_AUDFMT_RIGHTJ << RT5509_AUDFMT_SHFT);
break;
case SND_SOC_DAIFMT_LEFT_J:
regval |= (RT5509_AUDFMT_LEFTJ << RT5509_AUDFMT_SHFT);
break;
case SND_SOC_DAIFMT_DSP_A:
regval |= (RT5509_DSP_MODEA << RT5509_DSPMODE_SHFT);
break;
case SND_SOC_DAIFMT_DSP_B:
regval |= (RT5509_DSP_MODEB << RT5509_DSPMODE_SHFT);
break;
default:
break;
}
ret = snd_soc_update_bits(dai->codec, RT5509_REG_AUDFMT,
RT5509_DSPMODE_MASK | RT5509_AUDFMT_MASK, regval);
if (ret < 0)
dev_err(dai->dev, "config dac audfmt error\n");
return ret;
}
static int rt5509_aif_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
{
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(dai->codec);
unsigned int rate = params_rate(hw_params);
snd_pcm_format_t format = params_format(hw_params);
/* 0 for sr and bckfs, 1 for audbits */
u8 regval[2] = {0};
u32 pll_divider = 0;
u8 word_len = 0;
int ret = 0;
dev_info(dai->dev, "%s(), format %d, rate %u\n",
__func__, format, rate);
switch (format) {
case SNDRV_PCM_FORMAT_S16:
case SNDRV_PCM_FORMAT_U16:
regval[0] |= (RT5509_BCKMODE_32FS << RT5509_BCKMODE_SHFT);
regval[1] |= (RT5509_AUDBIT_16 << RT5509_AUDBIT_SHFT);
pll_divider = 0x00100000;
word_len = 16 * 4;
break;
case SNDRV_PCM_FORMAT_S18_3LE:
case SNDRV_PCM_FORMAT_U18_3LE:
case SNDRV_PCM_FORMAT_S18_3BE:
case SNDRV_PCM_FORMAT_U18_3BE:
regval[0] |= (RT5509_BCKMODE_48FS << RT5509_BCKMODE_SHFT);
regval[1] |= (RT5509_AUDBIT_18 << RT5509_AUDBIT_SHFT);
pll_divider = 0x000c0000;
word_len = 18 * 4;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
case SNDRV_PCM_FORMAT_U20_3LE:
case SNDRV_PCM_FORMAT_S20_3BE:
case SNDRV_PCM_FORMAT_U20_3BE:
regval[0] |= (RT5509_BCKMODE_48FS << RT5509_BCKMODE_SHFT);
regval[1] |= (RT5509_AUDBIT_20 << RT5509_AUDBIT_SHFT);
pll_divider = 0x000c0000;
word_len = 20 * 4;
break;
case SNDRV_PCM_FORMAT_S24_3LE:
case SNDRV_PCM_FORMAT_S24_3BE:
case SNDRV_PCM_FORMAT_U24_3LE:
case SNDRV_PCM_FORMAT_U24_3BE:
regval[0] |= (RT5509_BCKMODE_48FS << RT5509_BCKMODE_SHFT);
regval[1] |= (RT5509_AUDBIT_24 << RT5509_AUDBIT_SHFT);
pll_divider = 0x000c0000;
word_len = 24 * 4;
break;
case SNDRV_PCM_FORMAT_S32:
case SNDRV_PCM_FORMAT_U32:
regval[0] |= (RT5509_BCKMODE_64FS << RT5509_BCKMODE_SHFT);
regval[1] |= (RT5509_AUDBIT_24 << RT5509_AUDBIT_SHFT);
pll_divider = 0x00080000;
word_len = 24 * 4;
break;
default:
ret = -EINVAL;
goto out_hw_params;
}
switch (rate) {
case 8000:
regval[0] |= (RT5509_SRMODE_8K << RT5509_SRMODE_SHFT);
pll_divider *= 6;
break;
case 11025:
case 12000:
regval[0] |= (RT5509_SRMODE_12K << RT5509_SRMODE_SHFT);
pll_divider *= 4;
break;
case 16000:
regval[0] |= (RT5509_SRMODE_16K << RT5509_SRMODE_SHFT);
pll_divider *= 3;
break;
case 22050:
case 24000:
regval[0] |= (RT5509_SRMODE_24K << RT5509_SRMODE_SHFT);
pll_divider *= 2;
break;
case 32000:
regval[0] |= (RT5509_SRMODE_32K << RT5509_SRMODE_SHFT);
pll_divider = (pll_divider * 3) >> 1;
break;
case 44100:
case 48000:
regval[0] |= (RT5509_SRMODE_48K << RT5509_SRMODE_SHFT);
break;
case 88200:
case 96000:
regval[0] |= (RT5509_SRMODE_96K << RT5509_SRMODE_SHFT);
pll_divider >>= 1;
break;
case 176400:
case 192000:
regval[0] |= (RT5509_SRMODE_192K << RT5509_SRMODE_SHFT);
pll_divider >>= 2;
break;
default:
ret = -EINVAL;
goto out_hw_params;
}
if (chip->tdm_mode)
pll_divider >>= 1;
ret = snd_soc_update_bits(dai->codec, RT5509_REG_AUDSR,
RT5509_BCKMODE_MASK | RT5509_SRMODE_MASK, regval[0]);
if (ret < 0) {
dev_err(dai->dev, "configure bck and sr fail\n");
goto out_hw_params;
}
ret = snd_soc_update_bits(dai->codec, RT5509_REG_AUDFMT,
RT5509_AUDBIT_MASK, regval[1]);
if (ret < 0) {
dev_err(dai->dev, "configure audbit fail\n");
goto out_hw_params;
}
ret = snd_soc_write(dai->codec, RT5509_REG_PLLDIVISOR, pll_divider);
if (ret < 0) {
dev_err(dai->dev, "configure pll divider fail\n");
goto out_hw_params;
}
ret = snd_soc_write(dai->codec, RT5509_REG_DMGFLAG, word_len);
if (ret < 0)
dev_err(dai->dev, "configure word len fail\n");
out_hw_params:
return ret;
}
static int rt5509_aif_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
return 0;
}
static int rt5509_aif_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
dev_dbg(dai->dev, "%s\n", __func__);
return rt5509_set_bias_level(dai->codec, SND_SOC_BIAS_STANDBY);
}
static void rt5509_aif_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
dev_dbg(dai->dev, "%s\n", __func__);
}
static int rt5509_aif_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
dev_dbg(dai->dev, "%s: cmd=%d\n", __func__, cmd);
dev_dbg(dai->dev, "%s: %c\n", __func__, capture ? 'c' : 'p');
return 0;
}
static int rt5509_aif_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
{
struct rt5509_chip *chip = snd_soc_codec_get_drvdata(dai->codec);
dev_dbg(dai->dev, "%s: slots %d\n", __func__, slots);
if (!slots) {
dev_dbg(dai->dev, "disable TDM\n");
chip->tdm_mode = 0;
} else if (slots == 4) {
dev_dbg(dai->dev, "enable TDM\n");
chip->tdm_mode = 1;
} else
return -EINVAL;
return snd_soc_update_bits(dai->codec, RT5509_REG_TDM_CTRL,
RT5509_TDM_ENMASK,
chip->tdm_mode ? 0xff : 0);
}
static const struct snd_soc_dai_ops rt5509_dai_ops = {
.set_fmt = rt5509_aif_set_fmt,
.hw_params = rt5509_aif_hw_params,
.startup = rt5509_aif_startup,
.shutdown = rt5509_aif_shutdown,
.trigger = rt5509_aif_trigger,
.prepare = rt5509_aif_prepare,
.set_tdm_slot = rt5509_aif_set_tdm_slot,
};
#define RT5509_RATES SNDRV_PCM_RATE_8000_192000
#define RT5509_FORMATS (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S18_3LE |\
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE |\
SNDRV_PCM_FMTBIT_S32)
static struct snd_soc_dai_driver rt5509_i2s_dais[] = {
{
.name = "rt5509-aif1",
.playback = {
.stream_name = "AIF1 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = RT5509_RATES,
.formats = RT5509_FORMATS,
},
.capture = {
.stream_name = "AIF1 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = RT5509_RATES,
.formats = RT5509_FORMATS,
},
.ops = &rt5509_dai_ops,
},
{
.name = "rt5509-aif2",
.playback = {
.stream_name = "AIF2 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = RT5509_RATES,
.formats = RT5509_FORMATS,
},
.ops = &rt5509_dai_ops,
},
};
static inline int rt5509_codec_register(struct rt5509_chip *chip)
{
return snd_soc_register_codec(chip->dev, &rt5509_codec_drv,
rt5509_i2s_dais, ARRAY_SIZE(rt5509_i2s_dais));
}
static inline int rt5509_codec_unregister(struct rt5509_chip *chip)
{
snd_soc_unregister_codec(chip->dev);
return 0;
}
static int rt5509_handle_pdata(struct rt5509_chip *chip)
{
return 0;
}
static int rt5509_i2c_initreg(struct rt5509_chip *chip)
{
return rt5509_clr_bits(chip->i2c, RT5509_REG_CHIPEN,
RT5509_TRIWAVE_ENMASK);
}
static int rt5509_get_chip_rev(struct rt5509_chip *chip)
{
int ret = 0;
u8 data = 0;
ret = rt5509_block_read(chip->i2c, RT5509_REG_CHIPREV, 1, &data);
if (ret < 0)
return ret;
if ((data & RT5509_CHIPID_MASK) != RT5509_CHIP_ID)
return -ENODEV;
chip->chip_rev = (data & RT5509_CHIPREV_MASK) >> RT5509_CHIPREV_SHFT;
dev_dbg(chip->dev, "chip revision %d\n", chip->chip_rev);
return 0;
}
static int rt5509_sw_reset(struct rt5509_chip *chip)
{
int ret = 0;
u8 data = 0;
dev_dbg(chip->dev, "%s\n", __func__);
ret = rt5509_block_read(chip->i2c, RT5509_REG_SWRESET, 1, &data);
if (ret < 0)
return ret;
data |= RT5509_SWRST_MASK;
ret = rt5509_block_write(chip->i2c, RT5509_REG_SWRESET, 1, &data);
mdelay(30);
return ret;
}
static inline int _rt5509_power_on(struct rt5509_chip *chip, bool en)
{
int ret = 0;
u8 data = 0;
dev_dbg(chip->dev, "%s: en %d\n", __func__, en);
ret = rt5509_block_read(chip->i2c, RT5509_REG_CHIPEN, 1, &data);
if (ret < 0)
return ret;
data = (en ? (data & ~0x01) : (data | 0x01));
return rt5509_block_write(chip->i2c, RT5509_REG_CHIPEN, 1, &data);
}
#ifdef CONFIG_OF
static inline int rt5509_parse_dt(struct device *dev,
struct rt5509_pdata *pdata)
{
struct device_node *param_np = NULL;
struct property *prop = NULL;
struct rt5509_proprietary_param *p_param = NULL;
u32 len = 0;
int i = 0;
param_np = of_find_node_by_name(dev->of_node, "proprietary_param");
if (!param_np)
goto OUT_PARSE_DT;
p_param = devm_kzalloc(dev, sizeof(*p_param), GFP_KERNEL);
if (!p_param)
return -ENOMEM;
for (i = 0; i < RT5509_CFG_MAX; i++) {
prop = of_find_property(param_np, prop_str[i], &len);
if (!prop)
dev_warn(dev, "no %s setting\n", prop_str[i]);
else if (!len)
dev_warn(dev, "%s cfg size is zero\n", prop_str[i]);
else {
p_param->cfg[i] = devm_kzalloc(dev, len * sizeof(u8),
GFP_KERNEL);
if (!p_param->cfg[i])
return -ENOMEM;
memcpy(p_param->cfg[i], prop->value, len);
p_param->cfg_size[i] = len;
}
}
pdata->p_param = p_param;
OUT_PARSE_DT:
return 0;
}
#else
static inline int rt5509_parse_dt(struct device *dev,
struct rt5509_pdata *pdata)
{
return 0;
}
#endif /* #ifdef CONFIG_OF */
int rt5509_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct rt5509_pdata *pdata = client->dev.platform_data;
struct rt5509_chip *chip = NULL;
static int dev_cnt;
int ret = 0;
pr_info("+%s\n", __func__);
if (client->dev.of_node) {
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
ret = rt5509_parse_dt(&client->dev, pdata);
if (ret < 0)
goto err_parse_dt;
client->dev.platform_data = pdata;
} else {
if (!pdata) {
dev_err(&client->dev, "Failed, no pdata specified\n");
return -EINVAL;
}
}
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
goto err_parse_dt;
chip->i2c = client;
chip->dev = &client->dev;
chip->pdata = pdata;
chip->dev_cnt = dev_cnt;
i2c_set_clientdata(client, chip);
#if RT5509_SIMULATE_DEVICE
ret = rt5509_calculate_total_size();
chip->sim = devm_kzalloc(&client->dev, ret, GFP_KERNEL);
if (!chip->sim) {
ret = -ENOMEM;
goto err_simulate;
}
#endif /* #if RT5509_SIMULATE_DEVICE */
sema_init(&chip->io_semaphore, 1);
mutex_init(&chip->var_lock);
chip->power_count = 1;
/* before sw_reset, set CHIP_PD = 0 */
ret = _rt5509_power_on(chip, true);
if (ret < 0) {
dev_err(chip->dev, "power on fail 1\n");
goto err_sw_reset;
}
/* do software reset at default */
ret = rt5509_sw_reset(chip);
if (ret < 0) {
dev_err(chip->dev, "sw_reset fail\n");
goto err_sw_reset;
}
ret = _rt5509_power_on(chip, true);
if (ret < 0) {
dev_err(chip->dev, "power on fail 2\n");
goto err_pm_init;
}
/* get chip revisioin first */
ret = rt5509_get_chip_rev(chip);
if (ret < 0) {
dev_err(chip->dev, "get chip rev fail\n");
goto err_sw_reset;
}
/* register RegMAP */
chip->rd = rt5509_regmap_register(
&rt5509_regmap_ops, &client->dev, (void *)client, chip);
if (!chip->rd) {
dev_err(chip->dev, "create regmap device fail\n");
ret = -EINVAL;
goto err_regmap;
}
ret = rt5509_i2c_initreg(chip);
if (ret < 0) {
dev_err(chip->dev, "init_reg fail\n");
goto err_initreg;
}
ret = rt5509_handle_pdata(chip);
if (ret < 0) {
dev_err(chip->dev, "init_pdata fail\n");
goto err_pdata;
}
ret = rt5509_power_on(chip, false);
if (ret < 0) {
dev_err(chip->dev, "power off fail\n");
goto err_put_sync;
}
dev_set_name(chip->dev, "RT5509_MT_%d", chip->dev_cnt);
ret = rt5509_codec_register(chip);
if (ret < 0) {
dev_err(chip->dev, "codec register fail\n");
goto err_put_sync;
}
dev_info(&client->dev, "RT5509_MT_%d driver probed\n",
dev_cnt);
dev_cnt++;
return 0;
err_put_sync:
err_pdata:
err_initreg:
#ifdef CONFIG_RT_REGMAP
rt_regmap_device_unregister(chip->rd);
#endif /* #ifdef CONFIG_RT_REGMAP */
err_regmap:
err_pm_init:
_rt5509_power_on(chip, false);
err_sw_reset:
#if RT5509_SIMULATE_DEVICE
devm_kfree(chip->dev, chip->sim);
err_simulate:
#endif /* #if RT5509_SIMULATE_DEVICE */
devm_kfree(&client->dev, chip);
err_parse_dt:
if (client->dev.of_node)
devm_kfree(&client->dev, pdata);
dev_err(&client->dev, "error %d\n", ret);
i2c_set_clientdata(client, NULL);
return ret;
}
EXPORT_SYMBOL(rt5509_i2c_probe);
int rt5509_i2c_remove(struct i2c_client *client)
{
struct rt5509_chip *chip = i2c_get_clientdata(client);
rt5509_codec_unregister(chip);
#ifdef CONFIG_RT_REGMAP
rt_regmap_device_unregister(chip->rd);
#endif /* #ifdef CONFIG_RT_REGMAP */
_rt5509_power_on(chip, false);
#if RT5509_SIMULATE_DEVICE
devm_kfree(chip->dev, chip->sim);
#endif /* #if RT5509_SIMULATE_DEVICE */
devm_kfree(chip->dev, chip->pdata);
chip->pdata = client->dev.platform_data = NULL;
dev_dbg(&client->dev, "driver removed\n");
return 0;
}
EXPORT_SYMBOL(rt5509_i2c_remove);
void rt5509_i2c_shutdown(struct i2c_client *client)
{
struct rt5509_chip *chip = i2c_get_clientdata(client);
struct snd_soc_dapm_context *dapm = NULL;
dev_dbg(&client->dev, "%s\n", __func__);
if (chip && chip->codec) {
dapm = snd_soc_codec_get_dapm(chip->codec);
snd_soc_dapm_disable_pin(dapm, "Speaker");
snd_soc_dapm_sync(dapm);
}
}
EXPORT_SYMBOL(rt5509_i2c_shutdown);
static int __init rt5509_driver_init(void)
{
pr_info("%s\n", __func__);
platform_driver_register(&rt5509_param_driver);
return 0;
}
module_init(rt5509_driver_init);
static void __exit rt5509_driver_exit(void)
{
pr_info("%s\n", __func__);
platform_driver_unregister(&rt5509_param_driver);
}
module_exit(rt5509_driver_exit);
MODULE_AUTHOR("CY_Huang <cy_huang@richtek.com>");
MODULE_DESCRIPTION("RT5509 SPKAMP Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(RT5509_DRV_VER);