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:
Родитель
b787f68c36
Коммит
c354b54cfd
|
@ -10,9 +10,20 @@ Required properties:
|
||||||
- reg : the I2C address of the device for I2C, the chip select
|
- reg : the I2C address of the device for I2C, the chip select
|
||||||
number for SPI.
|
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:
|
Example:
|
||||||
|
|
||||||
codec: wm8741@1a {
|
codec: wm8741@1a {
|
||||||
compatible = "wlf,wm8741";
|
compatible = "wlf,wm8741";
|
||||||
reg = <0x1a>;
|
reg = <0x1a>;
|
||||||
|
|
||||||
|
diff-mode = <3>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,6 +41,7 @@ static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = {
|
||||||
|
|
||||||
/* codec private data */
|
/* codec private data */
|
||||||
struct wm8741_priv {
|
struct wm8741_priv {
|
||||||
|
struct wm8741_platform_data pdata;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
|
struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
|
||||||
unsigned int sysclk;
|
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_fine, -12700, 13, 0);
|
||||||
static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 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,
|
SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
|
||||||
WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine),
|
WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine),
|
||||||
SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
|
SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
|
||||||
WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv),
|
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[] = {
|
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("DACL", "Playback", SND_SOC_NOPM, 0, 0),
|
||||||
SND_SOC_DAPM_DAC("DACR", "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",
|
.name = "wm8741",
|
||||||
.playback = {
|
.playback = {
|
||||||
.stream_name = "Playback",
|
.stream_name = "Playback",
|
||||||
.channels_min = 2, /* Mono modes not yet supported */
|
.channels_min = 2,
|
||||||
.channels_max = 2,
|
.channels_max = 2,
|
||||||
.rates = WM8741_RATES,
|
.rates = WM8741_RATES,
|
||||||
.formats = WM8741_FORMATS,
|
.formats = WM8741_FORMATS,
|
||||||
|
@ -416,6 +431,65 @@ static int wm8741_resume(struct snd_soc_codec *codec)
|
||||||
#define wm8741_resume NULL
|
#define wm8741_resume NULL
|
||||||
#endif
|
#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)
|
static int wm8741_probe(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(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;
|
goto err_enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Change some default settings - latch VU */
|
ret = wm8741_configure(codec);
|
||||||
snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
|
if (ret < 0) {
|
||||||
WM8741_UPDATELL, WM8741_UPDATELL);
|
dev_err(codec->dev, "Failed to change default settings\n");
|
||||||
snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
|
goto err_enable;
|
||||||
WM8741_UPDATELM, WM8741_UPDATELM);
|
}
|
||||||
snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
|
|
||||||
WM8741_UPDATERL, WM8741_UPDATERL);
|
ret = wm8741_add_controls(codec);
|
||||||
snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION,
|
if (ret < 0) {
|
||||||
WM8741_UPDATERM, WM8741_UPDATERM);
|
dev_err(codec->dev, "Failed to add controls\n");
|
||||||
|
goto err_enable;
|
||||||
|
}
|
||||||
|
|
||||||
dev_dbg(codec->dev, "Successful registration\n");
|
dev_dbg(codec->dev, "Successful registration\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -467,8 +543,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8741 = {
|
||||||
.remove = wm8741_remove,
|
.remove = wm8741_remove,
|
||||||
.resume = wm8741_resume,
|
.resume = wm8741_resume,
|
||||||
|
|
||||||
.controls = wm8741_snd_controls,
|
|
||||||
.num_controls = ARRAY_SIZE(wm8741_snd_controls),
|
|
||||||
.dapm_widgets = wm8741_dapm_widgets,
|
.dapm_widgets = wm8741_dapm_widgets,
|
||||||
.num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets),
|
.num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets),
|
||||||
.dapm_routes = wm8741_dapm_routes,
|
.dapm_routes = wm8741_dapm_routes,
|
||||||
|
@ -493,6 +567,23 @@ static const struct regmap_config wm8741_regmap = {
|
||||||
.readable_reg = wm8741_readable,
|
.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)
|
#if IS_ENABLED(CONFIG_I2C)
|
||||||
static int wm8741_i2c_probe(struct i2c_client *i2c,
|
static int wm8741_i2c_probe(struct i2c_client *i2c,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
|
@ -522,6 +613,12 @@ static int wm8741_i2c_probe(struct i2c_client *i2c,
|
||||||
return ret;
|
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);
|
i2c_set_clientdata(i2c, wm8741);
|
||||||
|
|
||||||
ret = snd_soc_register_codec(&i2c->dev,
|
ret = snd_soc_register_codec(&i2c->dev,
|
||||||
|
@ -582,6 +679,12 @@ static int wm8741_spi_probe(struct spi_device *spi)
|
||||||
return ret;
|
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);
|
spi_set_drvdata(spi, wm8741);
|
||||||
|
|
||||||
ret = snd_soc_register_codec(&spi->dev,
|
ret = snd_soc_register_codec(&spi->dev,
|
||||||
|
|
|
@ -194,6 +194,12 @@
|
||||||
#define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */
|
#define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */
|
||||||
#define WM8741_DITHER_WIDTH 2 /* 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
|
* R32 (0x20) - ADDITONAL_CONTROL_1
|
||||||
*/
|
*/
|
||||||
|
@ -208,4 +214,8 @@
|
||||||
|
|
||||||
#define WM8741_SYSCLK 0
|
#define WM8741_SYSCLK 0
|
||||||
|
|
||||||
|
struct wm8741_platform_data {
|
||||||
|
u32 diff_mode; /* Differential Output Mode */
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Загрузка…
Ссылка в новой задаче