ASoC: es8316: Porting to rockchip platform

add mclk and amp amplifier control for rockchip soc
using extcon notifier for headset detect

Change-Id: I5984a60f3e64682a669779cd99def8f423b6dd0d
Signed-off-by: Shunhua Lan <lsh@rock-chips.com>
This commit is contained in:
Shunhua Lan 2019-12-17 17:49:32 +08:00 committed by Tao Huang
parent dd9daa56a7
commit eb9a258810
1 changed files with 130 additions and 6 deletions

View File

@ -12,9 +12,13 @@
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/extcon.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/of_gpio.h>
#include <linux/regmap.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@ -23,6 +27,7 @@
#include <sound/tlv.h>
#include "es8316.h"
#define ES8316_EXTCON_ID EXTCON_JACK_HEADPHONE
/* In slave mode at single speed, the codec is documented as accepting 5
* MCLK/LRCK ratios, but we also add ratio 400, which is commonly used on
* Intel Cherry Trail platforms (19.2MHz MCLK, 48kHz LRCK).
@ -36,6 +41,13 @@ struct es8316_priv {
unsigned int sysclk;
unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS];
struct snd_pcm_hw_constraint_list sysclk_constraints;
struct clk *mclk;
struct gpio_desc *gpiod_spk_ctl;
bool muted;
bool hp_inserted;
struct notifier_block extcon_nb;
int pwr_count;
};
/*
@ -494,10 +506,75 @@ static int es8316_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
static void es8316_enable_spk(struct es8316_priv *es8316, bool enable)
{
if (!es8316 || !es8316->gpiod_spk_ctl)
return;
gpiod_set_value(es8316->gpiod_spk_ctl, enable);
}
static int es8316_extcon_notifier(struct notifier_block *self, unsigned long event, void *ptr)
{
struct es8316_priv *es8316 = container_of(self, struct es8316_priv,
extcon_nb);
if (event) {
es8316->hp_inserted = true;
es8316_enable_spk(es8316, false);
} else {
es8316->hp_inserted = false;
}
return NOTIFY_DONE;
}
static int es8316_mute(struct snd_soc_dai *dai, int mute)
{
snd_soc_component_update_bits(dai->component, ES8316_DAC_SET1, 0x20,
mute ? 0x20 : 0);
struct snd_soc_component *component = dai->component;
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
unsigned int val = mute ? 0x20 : 0;
es8316->muted = mute;
if (mute) {
es8316_enable_spk(es8316, false);
msleep(100);
snd_soc_component_update_bits(dai->component, ES8316_DAC_SET1, 0x20, val);
} else {
snd_soc_component_update_bits(dai->component, ES8316_DAC_SET1, 0x20, val);
msleep(130);
if (!es8316->hp_inserted)
es8316_enable_spk(es8316, true);
}
return 0;
}
static int es8316_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
if (IS_ERR(es8316->mclk))
break;
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
clk_disable_unprepare(es8316->mclk);
} else {
ret = clk_prepare_enable(es8316->mclk);
if (ret)
return ret;
}
break;
case SND_SOC_BIAS_STANDBY:
break;
case SND_SOC_BIAS_OFF:
break;
}
return 0;
}
@ -534,6 +611,17 @@ static struct snd_soc_dai_driver es8316_dai = {
static int es8316_probe(struct snd_soc_component *component)
{
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
int ret = 0;
es8316->mclk = devm_clk_get(component->dev, "mclk");
if (PTR_ERR(es8316->mclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
ret = clk_prepare_enable(es8316->mclk);
if (ret)
return ret;
/* Reset codec and enable current state machine */
snd_soc_component_write(component, ES8316_RESET, 0x3f);
usleep_range(5000, 5500);
@ -556,8 +644,17 @@ static int es8316_probe(struct snd_soc_component *component)
return 0;
}
static void es8316_remove(struct snd_soc_component *component)
{
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
clk_disable_unprepare(es8316->mclk);
}
static const struct snd_soc_component_driver soc_component_dev_es8316 = {
.probe = es8316_probe,
.remove = es8316_remove,
.set_bias_level = es8316_set_bias_level,
.controls = es8316_snd_controls,
.num_controls = ARRAY_SIZE(es8316_snd_controls),
.dapm_widgets = es8316_dapm_widgets,
@ -580,22 +677,49 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
struct es8316_priv *es8316;
struct extcon_dev *edev;
struct regmap *regmap;
int ret = -1;
es8316 = devm_kzalloc(&i2c_client->dev, sizeof(struct es8316_priv),
GFP_KERNEL);
if (es8316 == NULL)
return -ENOMEM;
es8316->hp_inserted = false;
es8316->muted = true;
i2c_set_clientdata(i2c_client, es8316);
regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
es8316->gpiod_spk_ctl = devm_gpiod_get_optional(&i2c_client->dev,
"spk-con",
GPIOD_OUT_LOW);
if (IS_ERR(es8316->gpiod_spk_ctl)) {
ret = IS_ERR(es8316->gpiod_spk_ctl);
es8316->gpiod_spk_ctl = NULL;
dev_warn(&i2c_client->dev, "cannot get spk-con-gpio %d\n", ret);
}
if (device_property_read_bool(&i2c_client->dev, "extcon")) {
edev = extcon_get_edev_by_phandle(&i2c_client->dev, 0);
if (IS_ERR(edev)) {
if (PTR_ERR(edev) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_err(&i2c_client->dev, "Invalid or missing extcon\n");
return PTR_ERR(edev);
}
es8316->extcon_nb.notifier_call = es8316_extcon_notifier;
ret = devm_extcon_register_notifier(&i2c_client->dev, edev,
ES8316_EXTCON_ID,
&es8316->extcon_nb);
if (ret < 0) {
dev_err(&i2c_client->dev, "register notifier fail\n");
return ret;
}
}
return devm_snd_soc_register_component(&i2c_client->dev,
&soc_component_dev_es8316,
&es8316_dai, 1);
&soc_component_dev_es8316,
&es8316_dai, 1);
}
static const struct i2c_device_id es8316_i2c_id[] = {