forked from Minki/linux
Merge remote-tracking branch 'asoc/topic/twl6040' into asoc-next
This commit is contained in:
commit
e78e4c49e7
@ -125,8 +125,15 @@
|
||||
|
||||
#define TWL6040_HSDACENA (1 << 0)
|
||||
#define TWL6040_HSDACMODE (1 << 1)
|
||||
#define TWL6040_HSDRVENA (1 << 2)
|
||||
#define TWL6040_HSDRVMODE (1 << 3)
|
||||
|
||||
/* HFLCTL/R (0x14/0x16) fields */
|
||||
|
||||
#define TWL6040_HFDACENA (1 << 0)
|
||||
#define TWL6040_HFPGAENA (1 << 1)
|
||||
#define TWL6040_HFDRVENA (1 << 4)
|
||||
|
||||
/* VIBCTLL/R (0x18/0x1A) fields */
|
||||
|
||||
#define TWL6040_VIBENA (1 << 0)
|
||||
|
@ -38,6 +38,14 @@
|
||||
|
||||
#include "twl6040.h"
|
||||
|
||||
enum twl6040_dai_id {
|
||||
TWL6040_DAI_LEGACY = 0,
|
||||
TWL6040_DAI_UL,
|
||||
TWL6040_DAI_DL1,
|
||||
TWL6040_DAI_DL2,
|
||||
TWL6040_DAI_VIB,
|
||||
};
|
||||
|
||||
#define TWL6040_RATES SNDRV_PCM_RATE_8000_96000
|
||||
#define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
@ -67,6 +75,8 @@ struct twl6040_data {
|
||||
int pll_power_mode;
|
||||
int hs_power_mode;
|
||||
int hs_power_mode_locked;
|
||||
bool dl1_unmuted;
|
||||
bool dl2_unmuted;
|
||||
unsigned int clk_in;
|
||||
unsigned int sysclk;
|
||||
struct twl6040_jack_data hs_jack;
|
||||
@ -220,6 +230,25 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec,
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool twl6040_is_path_unmuted(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
switch (reg) {
|
||||
case TWL6040_REG_HSLCTL:
|
||||
case TWL6040_REG_HSRCTL:
|
||||
case TWL6040_REG_EARCTL:
|
||||
/* DL1 path */
|
||||
return priv->dl1_unmuted;
|
||||
case TWL6040_REG_HFLCTL:
|
||||
case TWL6040_REG_HFRCTL:
|
||||
return priv->dl2_unmuted;
|
||||
default:
|
||||
return 1;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* write to the twl6040 register space
|
||||
*/
|
||||
@ -232,7 +261,8 @@ static int twl6040_write(struct snd_soc_codec *codec,
|
||||
return -EIO;
|
||||
|
||||
twl6040_write_reg_cache(codec, reg, value);
|
||||
if (likely(reg < TWL6040_REG_SW_SHADOW))
|
||||
if (likely(reg < TWL6040_REG_SW_SHADOW) &&
|
||||
twl6040_is_path_unmuted(codec, reg))
|
||||
return twl6040_reg_write(twl6040, reg, value);
|
||||
else
|
||||
return 0;
|
||||
@ -1026,16 +1056,84 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id id,
|
||||
int mute)
|
||||
{
|
||||
struct twl6040 *twl6040 = codec->control_data;
|
||||
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
|
||||
int hslctl, hsrctl, earctl;
|
||||
int hflctl, hfrctl;
|
||||
|
||||
switch (id) {
|
||||
case TWL6040_DAI_DL1:
|
||||
hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL);
|
||||
hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL);
|
||||
earctl = twl6040_read_reg_cache(codec, TWL6040_REG_EARCTL);
|
||||
|
||||
if (mute) {
|
||||
/* Power down drivers and DACs */
|
||||
earctl &= ~0x01;
|
||||
hslctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);
|
||||
hsrctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);
|
||||
|
||||
}
|
||||
|
||||
twl6040_reg_write(twl6040, TWL6040_REG_EARCTL, earctl);
|
||||
twl6040_reg_write(twl6040, TWL6040_REG_HSLCTL, hslctl);
|
||||
twl6040_reg_write(twl6040, TWL6040_REG_HSRCTL, hsrctl);
|
||||
priv->dl1_unmuted = !mute;
|
||||
break;
|
||||
case TWL6040_DAI_DL2:
|
||||
hflctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFLCTL);
|
||||
hfrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFRCTL);
|
||||
|
||||
if (mute) {
|
||||
/* Power down drivers and DACs */
|
||||
hflctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
|
||||
TWL6040_HFDRVENA);
|
||||
hfrctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
|
||||
TWL6040_HFDRVENA);
|
||||
}
|
||||
|
||||
twl6040_reg_write(twl6040, TWL6040_REG_HFLCTL, hflctl);
|
||||
twl6040_reg_write(twl6040, TWL6040_REG_HFRCTL, hfrctl);
|
||||
priv->dl2_unmuted = !mute;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static int twl6040_digital_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
switch (dai->id) {
|
||||
case TWL6040_DAI_LEGACY:
|
||||
twl6040_mute_path(dai->codec, TWL6040_DAI_DL1, mute);
|
||||
twl6040_mute_path(dai->codec, TWL6040_DAI_DL2, mute);
|
||||
break;
|
||||
case TWL6040_DAI_DL1:
|
||||
case TWL6040_DAI_DL2:
|
||||
twl6040_mute_path(dai->codec, dai->id, mute);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops twl6040_dai_ops = {
|
||||
.startup = twl6040_startup,
|
||||
.hw_params = twl6040_hw_params,
|
||||
.prepare = twl6040_prepare,
|
||||
.set_sysclk = twl6040_set_dai_sysclk,
|
||||
.digital_mute = twl6040_digital_mute,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver twl6040_dai[] = {
|
||||
{
|
||||
.name = "twl6040-legacy",
|
||||
.id = TWL6040_DAI_LEGACY,
|
||||
.playback = {
|
||||
.stream_name = "Legacy Playback",
|
||||
.channels_min = 1,
|
||||
@ -1054,6 +1152,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
|
||||
},
|
||||
{
|
||||
.name = "twl6040-ul",
|
||||
.id = TWL6040_DAI_UL,
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 1,
|
||||
@ -1065,6 +1164,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
|
||||
},
|
||||
{
|
||||
.name = "twl6040-dl1",
|
||||
.id = TWL6040_DAI_DL1,
|
||||
.playback = {
|
||||
.stream_name = "Headset Playback",
|
||||
.channels_min = 1,
|
||||
@ -1076,6 +1176,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
|
||||
},
|
||||
{
|
||||
.name = "twl6040-dl2",
|
||||
.id = TWL6040_DAI_DL2,
|
||||
.playback = {
|
||||
.stream_name = "Handsfree Playback",
|
||||
.channels_min = 1,
|
||||
@ -1087,6 +1188,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
|
||||
},
|
||||
{
|
||||
.name = "twl6040-vib",
|
||||
.id = TWL6040_DAI_VIB,
|
||||
.playback = {
|
||||
.stream_name = "Vibra Playback",
|
||||
.channels_min = 1,
|
||||
@ -1143,7 +1245,7 @@ static int twl6040_probe(struct snd_soc_codec *codec)
|
||||
|
||||
mutex_init(&priv->mutex);
|
||||
|
||||
ret = devm_request_threaded_irq(codec->dev, priv->plug_irq, NULL,
|
||||
ret = request_threaded_irq(priv->plug_irq, NULL,
|
||||
twl6040_audio_handler, IRQF_NO_SUSPEND,
|
||||
"twl6040_irq_plug", codec);
|
||||
if (ret) {
|
||||
@ -1159,6 +1261,9 @@ static int twl6040_probe(struct snd_soc_codec *codec)
|
||||
|
||||
static int twl6040_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
free_irq(priv->plug_irq, codec);
|
||||
twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user