forked from Minki/linux
ASoC: wm8741: Add differential mono mode support
The WM8741 DAC supports several differential output modes (stereo, stereo reversed, mono left, mono right). Add platform data and DT bindings to configure it. Signed-off-by: Sergej Sawazki <ce3a@gmx.de> Acked-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
b787f68c36
commit
c354b54cfd
@ -10,9 +10,20 @@ Required properties:
|
||||
- reg : the I2C address of the device for I2C, the chip select
|
||||
number for SPI.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- diff-mode: Differential output mode configuration. Default value for field
|
||||
DIFF in register R8 (MODE_CONTROL_2). If absent, the default is 0, shall be:
|
||||
0 = stereo
|
||||
1 = mono left
|
||||
2 = stereo reversed
|
||||
3 = mono right
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8741@1a {
|
||||
compatible = "wlf,wm8741";
|
||||
reg = <0x1a>;
|
||||
|
||||
diff-mode = <3>;
|
||||
};
|
||||
|
@ -41,6 +41,7 @@ static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = {
|
||||
|
||||
/* codec private data */
|
||||
struct wm8741_priv {
|
||||
struct wm8741_platform_data pdata;
|
||||
struct regmap *regmap;
|
||||
struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
|
||||
unsigned int sysclk;
|
||||
@ -87,13 +88,27 @@ static int wm8741_reset(struct snd_soc_codec *codec)
|
||||
static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0);
|
||||
|
||||
static const struct snd_kcontrol_new wm8741_snd_controls[] = {
|
||||
static const struct snd_kcontrol_new wm8741_snd_controls_stereo[] = {
|
||||
SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
|
||||
WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine),
|
||||
SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
|
||||
WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new wm8741_snd_controls_mono_left[] = {
|
||||
SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
|
||||
1, 255, 1, dac_tlv_fine),
|
||||
SOC_SINGLE_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
|
||||
0, 511, 1, dac_tlv),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new wm8741_snd_controls_mono_right[] = {
|
||||
SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACRLSB_ATTENUATION,
|
||||
1, 255, 1, dac_tlv_fine),
|
||||
SOC_SINGLE_TLV("Playback Volume", WM8741_DACRMSB_ATTENUATION,
|
||||
0, 511, 1, dac_tlv),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0),
|
||||
@ -398,7 +413,7 @@ static struct snd_soc_dai_driver wm8741_dai = {
|
||||
.name = "wm8741",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2, /* Mono modes not yet supported */
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = WM8741_RATES,
|
||||
.formats = WM8741_FORMATS,
|
||||
@ -416,6 +431,65 @@ static int wm8741_resume(struct snd_soc_codec *codec)
|
||||
#define wm8741_resume NULL
|
||||
#endif
|
||||
|
||||
static int wm8741_configure(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
/* Configure differential mode */
|
||||
switch (wm8741->pdata.diff_mode) {
|
||||
case WM8741_DIFF_MODE_STEREO:
|
||||
case WM8741_DIFF_MODE_STEREO_REVERSED:
|
||||
case WM8741_DIFF_MODE_MONO_LEFT:
|
||||
case WM8741_DIFF_MODE_MONO_RIGHT:
|
||||
snd_soc_update_bits(codec, WM8741_MODE_CONTROL_2,
|
||||
WM8741_DIFF_MASK,
|
||||
wm8741->pdata.diff_mode << WM8741_DIFF_SHIFT);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Change some default settings - latch VU */
|
||||
snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
|
||||
WM8741_UPDATELL, WM8741_UPDATELL);
|
||||
snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
|
||||
WM8741_UPDATELM, WM8741_UPDATELM);
|
||||
snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
|
||||
WM8741_UPDATERL, WM8741_UPDATERL);
|
||||
snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION,
|
||||
WM8741_UPDATERM, WM8741_UPDATERM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8741_add_controls(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
switch (wm8741->pdata.diff_mode) {
|
||||
case WM8741_DIFF_MODE_STEREO:
|
||||
case WM8741_DIFF_MODE_STEREO_REVERSED:
|
||||
snd_soc_add_codec_controls(codec,
|
||||
wm8741_snd_controls_stereo,
|
||||
ARRAY_SIZE(wm8741_snd_controls_stereo));
|
||||
break;
|
||||
case WM8741_DIFF_MODE_MONO_LEFT:
|
||||
snd_soc_add_codec_controls(codec,
|
||||
wm8741_snd_controls_mono_left,
|
||||
ARRAY_SIZE(wm8741_snd_controls_mono_left));
|
||||
break;
|
||||
case WM8741_DIFF_MODE_MONO_RIGHT:
|
||||
snd_soc_add_codec_controls(codec,
|
||||
wm8741_snd_controls_mono_right,
|
||||
ARRAY_SIZE(wm8741_snd_controls_mono_right));
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8741_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
|
||||
@ -434,15 +508,17 @@ static int wm8741_probe(struct snd_soc_codec *codec)
|
||||
goto err_enable;
|
||||
}
|
||||
|
||||
/* Change some default settings - latch VU */
|
||||
snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
|
||||
WM8741_UPDATELL, WM8741_UPDATELL);
|
||||
snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
|
||||
WM8741_UPDATELM, WM8741_UPDATELM);
|
||||
snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
|
||||
WM8741_UPDATERL, WM8741_UPDATERL);
|
||||
snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION,
|
||||
WM8741_UPDATERM, WM8741_UPDATERM);
|
||||
ret = wm8741_configure(codec);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "Failed to change default settings\n");
|
||||
goto err_enable;
|
||||
}
|
||||
|
||||
ret = wm8741_add_controls(codec);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "Failed to add controls\n");
|
||||
goto err_enable;
|
||||
}
|
||||
|
||||
dev_dbg(codec->dev, "Successful registration\n");
|
||||
return ret;
|
||||
@ -467,8 +543,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8741 = {
|
||||
.remove = wm8741_remove,
|
||||
.resume = wm8741_resume,
|
||||
|
||||
.controls = wm8741_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(wm8741_snd_controls),
|
||||
.dapm_widgets = wm8741_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets),
|
||||
.dapm_routes = wm8741_dapm_routes,
|
||||
@ -493,6 +567,23 @@ static const struct regmap_config wm8741_regmap = {
|
||||
.readable_reg = wm8741_readable,
|
||||
};
|
||||
|
||||
static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741)
|
||||
{
|
||||
const struct wm8741_platform_data *pdata = dev_get_platdata(dev);
|
||||
u32 diff_mode;
|
||||
|
||||
if (dev->of_node) {
|
||||
if (of_property_read_u32(dev->of_node, "diff-mode", &diff_mode)
|
||||
>= 0)
|
||||
wm8741->pdata.diff_mode = diff_mode;
|
||||
} else {
|
||||
if (pdata != NULL)
|
||||
memcpy(&wm8741->pdata, pdata, sizeof(wm8741->pdata));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
static int wm8741_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
@ -522,6 +613,12 @@ static int wm8741_i2c_probe(struct i2c_client *i2c,
|
||||
return ret;
|
||||
}
|
||||
|
||||
wm8741_set_pdata(&i2c->dev, wm8741);
|
||||
if (ret != 0) {
|
||||
dev_err(&i2c->dev, "Failed to set pdata: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(i2c, wm8741);
|
||||
|
||||
ret = snd_soc_register_codec(&i2c->dev,
|
||||
@ -582,6 +679,12 @@ static int wm8741_spi_probe(struct spi_device *spi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
wm8741_set_pdata(&spi->dev, wm8741);
|
||||
if (ret != 0) {
|
||||
dev_err(&spi->dev, "Failed to set pdata: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, wm8741);
|
||||
|
||||
ret = snd_soc_register_codec(&spi->dev,
|
||||
|
@ -194,6 +194,12 @@
|
||||
#define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */
|
||||
#define WM8741_DITHER_WIDTH 2 /* DITHER - [1:0] */
|
||||
|
||||
/* DIFF field values */
|
||||
#define WM8741_DIFF_MODE_STEREO 0 /* stereo normal */
|
||||
#define WM8741_DIFF_MODE_STEREO_REVERSED 2 /* stereo reversed */
|
||||
#define WM8741_DIFF_MODE_MONO_LEFT 1 /* mono left */
|
||||
#define WM8741_DIFF_MODE_MONO_RIGHT 3 /* mono right */
|
||||
|
||||
/*
|
||||
* R32 (0x20) - ADDITONAL_CONTROL_1
|
||||
*/
|
||||
@ -208,4 +214,8 @@
|
||||
|
||||
#define WM8741_SYSCLK 0
|
||||
|
||||
struct wm8741_platform_data {
|
||||
u32 diff_mode; /* Differential Output Mode */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user