ASoC: wm8741: Add differential mono mode support
authorSergej Sawazki <ce3a@gmx.de>
Wed, 13 May 2015 09:39:01 +0000 (11:39 +0200)
committerMark Brown <broonie@kernel.org>
Wed, 13 May 2015 14:51:36 +0000 (15:51 +0100)
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>
Documentation/devicetree/bindings/sound/wm8741.txt
sound/soc/codecs/wm8741.c
sound/soc/codecs/wm8741.h

index 74bda58c1bcffb01cd1648f605cac031907a5ce3..a133154087194cb90a2056ba6f927d67b6e49ba4 100644 (file)
@@ -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>;
 };
index 9e71c768966f050e0a8279b54ec4e9eeb7f8e6d9..c065ea1668755dd615531ad89de8709faca5f67e 100644 (file)
@@ -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,
index 56c1b1d4a68100f52fcf6ec61363a9140e88e63a..c8835f65f3425c3a58b68f83de63282b89cea297 100644 (file)
 #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
  */
 
 #define  WM8741_SYSCLK 0
 
+struct wm8741_platform_data {
+       u32 diff_mode;   /* Differential Output Mode */
+};
+
 #endif