aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergej Sawazki <ce3a@gmx.de>2015-05-13 05:39:01 -0400
committerMark Brown <broonie@kernel.org>2015-05-13 10:51:36 -0400
commitc354b54cfdf63587154da4fa0731c1fbda44c589 (patch)
tree18ca15a19d1085e8978503c6aa45abcc560fedfc
parentb787f68c36d49bb1d9236f403813641efa74a031 (diff)
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>
-rw-r--r--Documentation/devicetree/bindings/sound/wm8741.txt11
-rw-r--r--sound/soc/codecs/wm8741.c129
-rw-r--r--sound/soc/codecs/wm8741.h10
3 files changed, 137 insertions, 13 deletions
diff --git a/Documentation/devicetree/bindings/sound/wm8741.txt b/Documentation/devicetree/bindings/sound/wm8741.txt
index 74bda58c1bcf..a13315408719 100644
--- a/Documentation/devicetree/bindings/sound/wm8741.txt
+++ b/Documentation/devicetree/bindings/sound/wm8741.txt
@@ -10,9 +10,20 @@ Required properties:
10 - reg : the I2C address of the device for I2C, the chip select 10 - reg : the I2C address of the device for I2C, the chip select
11 number for SPI. 11 number for SPI.
12 12
13Optional properties:
14
15 - diff-mode: Differential output mode configuration. Default value for field
16 DIFF in register R8 (MODE_CONTROL_2). If absent, the default is 0, shall be:
17 0 = stereo
18 1 = mono left
19 2 = stereo reversed
20 3 = mono right
21
13Example: 22Example:
14 23
15codec: wm8741@1a { 24codec: wm8741@1a {
16 compatible = "wlf,wm8741"; 25 compatible = "wlf,wm8741";
17 reg = <0x1a>; 26 reg = <0x1a>;
27
28 diff-mode = <3>;
18}; 29};
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index 9e71c768966f..c065ea166875 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -41,6 +41,7 @@ static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = {
41 41
42/* codec private data */ 42/* codec private data */
43struct wm8741_priv { 43struct wm8741_priv {
44 struct wm8741_platform_data pdata;
44 struct regmap *regmap; 45 struct regmap *regmap;
45 struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES]; 46 struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
46 unsigned int sysclk; 47 unsigned int sysclk;
@@ -87,13 +88,27 @@ static int wm8741_reset(struct snd_soc_codec *codec)
87static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0); 88static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0);
88static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0); 89static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0);
89 90
90static const struct snd_kcontrol_new wm8741_snd_controls[] = { 91static const struct snd_kcontrol_new wm8741_snd_controls_stereo[] = {
91SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, 92SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
92 WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine), 93 WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine),
93SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, 94SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
94 WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv), 95 WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv),
95}; 96};
96 97
98static const struct snd_kcontrol_new wm8741_snd_controls_mono_left[] = {
99SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
100 1, 255, 1, dac_tlv_fine),
101SOC_SINGLE_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
102 0, 511, 1, dac_tlv),
103};
104
105static const struct snd_kcontrol_new wm8741_snd_controls_mono_right[] = {
106SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACRLSB_ATTENUATION,
107 1, 255, 1, dac_tlv_fine),
108SOC_SINGLE_TLV("Playback Volume", WM8741_DACRMSB_ATTENUATION,
109 0, 511, 1, dac_tlv),
110};
111
97static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = { 112static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = {
98SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0), 113SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0),
99SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0), 114SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0),
@@ -398,7 +413,7 @@ static struct snd_soc_dai_driver wm8741_dai = {
398 .name = "wm8741", 413 .name = "wm8741",
399 .playback = { 414 .playback = {
400 .stream_name = "Playback", 415 .stream_name = "Playback",
401 .channels_min = 2, /* Mono modes not yet supported */ 416 .channels_min = 2,
402 .channels_max = 2, 417 .channels_max = 2,
403 .rates = WM8741_RATES, 418 .rates = WM8741_RATES,
404 .formats = WM8741_FORMATS, 419 .formats = WM8741_FORMATS,
@@ -416,6 +431,65 @@ static int wm8741_resume(struct snd_soc_codec *codec)
416#define wm8741_resume NULL 431#define wm8741_resume NULL
417#endif 432#endif
418 433
434static int wm8741_configure(struct snd_soc_codec *codec)
435{
436 struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
437
438 /* Configure differential mode */
439 switch (wm8741->pdata.diff_mode) {
440 case WM8741_DIFF_MODE_STEREO:
441 case WM8741_DIFF_MODE_STEREO_REVERSED:
442 case WM8741_DIFF_MODE_MONO_LEFT:
443 case WM8741_DIFF_MODE_MONO_RIGHT:
444 snd_soc_update_bits(codec, WM8741_MODE_CONTROL_2,
445 WM8741_DIFF_MASK,
446 wm8741->pdata.diff_mode << WM8741_DIFF_SHIFT);
447 break;
448 default:
449 return -EINVAL;
450 }
451
452 /* Change some default settings - latch VU */
453 snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
454 WM8741_UPDATELL, WM8741_UPDATELL);
455 snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
456 WM8741_UPDATELM, WM8741_UPDATELM);
457 snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
458 WM8741_UPDATERL, WM8741_UPDATERL);
459 snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION,
460 WM8741_UPDATERM, WM8741_UPDATERM);
461
462 return 0;
463}
464
465static int wm8741_add_controls(struct snd_soc_codec *codec)
466{
467 struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
468
469 switch (wm8741->pdata.diff_mode) {
470 case WM8741_DIFF_MODE_STEREO:
471 case WM8741_DIFF_MODE_STEREO_REVERSED:
472 snd_soc_add_codec_controls(codec,
473 wm8741_snd_controls_stereo,
474 ARRAY_SIZE(wm8741_snd_controls_stereo));
475 break;
476 case WM8741_DIFF_MODE_MONO_LEFT:
477 snd_soc_add_codec_controls(codec,
478 wm8741_snd_controls_mono_left,
479 ARRAY_SIZE(wm8741_snd_controls_mono_left));
480 break;
481 case WM8741_DIFF_MODE_MONO_RIGHT:
482 snd_soc_add_codec_controls(codec,
483 wm8741_snd_controls_mono_right,
484 ARRAY_SIZE(wm8741_snd_controls_mono_right));
485 break;
486 default:
487 return -EINVAL;
488 }
489
490 return 0;
491}
492
419static int wm8741_probe(struct snd_soc_codec *codec) 493static int wm8741_probe(struct snd_soc_codec *codec)
420{ 494{
421 struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); 495 struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
@@ -434,15 +508,17 @@ static int wm8741_probe(struct snd_soc_codec *codec)
434 goto err_enable; 508 goto err_enable;
435 } 509 }
436 510
437 /* Change some default settings - latch VU */ 511 ret = wm8741_configure(codec);
438 snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION, 512 if (ret < 0) {
439 WM8741_UPDATELL, WM8741_UPDATELL); 513 dev_err(codec->dev, "Failed to change default settings\n");
440 snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION, 514 goto err_enable;
441 WM8741_UPDATELM, WM8741_UPDATELM); 515 }
442 snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, 516
443 WM8741_UPDATERL, WM8741_UPDATERL); 517 ret = wm8741_add_controls(codec);
444 snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION, 518 if (ret < 0) {
445 WM8741_UPDATERM, WM8741_UPDATERM); 519 dev_err(codec->dev, "Failed to add controls\n");
520 goto err_enable;
521 }
446 522
447 dev_dbg(codec->dev, "Successful registration\n"); 523 dev_dbg(codec->dev, "Successful registration\n");
448 return ret; 524 return ret;
@@ -467,8 +543,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8741 = {
467 .remove = wm8741_remove, 543 .remove = wm8741_remove,
468 .resume = wm8741_resume, 544 .resume = wm8741_resume,
469 545
470 .controls = wm8741_snd_controls,
471 .num_controls = ARRAY_SIZE(wm8741_snd_controls),
472 .dapm_widgets = wm8741_dapm_widgets, 546 .dapm_widgets = wm8741_dapm_widgets,
473 .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets), 547 .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets),
474 .dapm_routes = wm8741_dapm_routes, 548 .dapm_routes = wm8741_dapm_routes,
@@ -493,6 +567,23 @@ static const struct regmap_config wm8741_regmap = {
493 .readable_reg = wm8741_readable, 567 .readable_reg = wm8741_readable,
494}; 568};
495 569
570static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741)
571{
572 const struct wm8741_platform_data *pdata = dev_get_platdata(dev);
573 u32 diff_mode;
574
575 if (dev->of_node) {
576 if (of_property_read_u32(dev->of_node, "diff-mode", &diff_mode)
577 >= 0)
578 wm8741->pdata.diff_mode = diff_mode;
579 } else {
580 if (pdata != NULL)
581 memcpy(&wm8741->pdata, pdata, sizeof(wm8741->pdata));
582 }
583
584 return 0;
585}
586
496#if IS_ENABLED(CONFIG_I2C) 587#if IS_ENABLED(CONFIG_I2C)
497static int wm8741_i2c_probe(struct i2c_client *i2c, 588static int wm8741_i2c_probe(struct i2c_client *i2c,
498 const struct i2c_device_id *id) 589 const struct i2c_device_id *id)
@@ -522,6 +613,12 @@ static int wm8741_i2c_probe(struct i2c_client *i2c,
522 return ret; 613 return ret;
523 } 614 }
524 615
616 wm8741_set_pdata(&i2c->dev, wm8741);
617 if (ret != 0) {
618 dev_err(&i2c->dev, "Failed to set pdata: %d\n", ret);
619 return ret;
620 }
621
525 i2c_set_clientdata(i2c, wm8741); 622 i2c_set_clientdata(i2c, wm8741);
526 623
527 ret = snd_soc_register_codec(&i2c->dev, 624 ret = snd_soc_register_codec(&i2c->dev,
@@ -582,6 +679,12 @@ static int wm8741_spi_probe(struct spi_device *spi)
582 return ret; 679 return ret;
583 } 680 }
584 681
682 wm8741_set_pdata(&spi->dev, wm8741);
683 if (ret != 0) {
684 dev_err(&spi->dev, "Failed to set pdata: %d\n", ret);
685 return ret;
686 }
687
585 spi_set_drvdata(spi, wm8741); 688 spi_set_drvdata(spi, wm8741);
586 689
587 ret = snd_soc_register_codec(&spi->dev, 690 ret = snd_soc_register_codec(&spi->dev,
diff --git a/sound/soc/codecs/wm8741.h b/sound/soc/codecs/wm8741.h
index 56c1b1d4a681..c8835f65f342 100644
--- a/sound/soc/codecs/wm8741.h
+++ b/sound/soc/codecs/wm8741.h
@@ -194,6 +194,12 @@
194#define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */ 194#define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */
195#define WM8741_DITHER_WIDTH 2 /* DITHER - [1:0] */ 195#define WM8741_DITHER_WIDTH 2 /* DITHER - [1:0] */
196 196
197/* DIFF field values */
198#define WM8741_DIFF_MODE_STEREO 0 /* stereo normal */
199#define WM8741_DIFF_MODE_STEREO_REVERSED 2 /* stereo reversed */
200#define WM8741_DIFF_MODE_MONO_LEFT 1 /* mono left */
201#define WM8741_DIFF_MODE_MONO_RIGHT 3 /* mono right */
202
197/* 203/*
198 * R32 (0x20) - ADDITONAL_CONTROL_1 204 * R32 (0x20) - ADDITONAL_CONTROL_1
199 */ 205 */
@@ -208,4 +214,8 @@
208 214
209#define WM8741_SYSCLK 0 215#define WM8741_SYSCLK 0
210 216
217struct wm8741_platform_data {
218 u32 diff_mode; /* Differential Output Mode */
219};
220
211#endif 221#endif