diff options
author | Sergej Sawazki <ce3a@gmx.de> | 2015-05-13 05:39:01 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2015-05-13 10:51:36 -0400 |
commit | c354b54cfdf63587154da4fa0731c1fbda44c589 (patch) | |
tree | 18ca15a19d1085e8978503c6aa45abcc560fedfc | |
parent | b787f68c36d49bb1d9236f403813641efa74a031 (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.txt | 11 | ||||
-rw-r--r-- | sound/soc/codecs/wm8741.c | 129 | ||||
-rw-r--r-- | sound/soc/codecs/wm8741.h | 10 |
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 | ||
13 | Optional 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 | |||
13 | Example: | 22 | Example: |
14 | 23 | ||
15 | codec: wm8741@1a { | 24 | codec: 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 */ |
43 | struct wm8741_priv { | 43 | struct 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) | |||
87 | static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0); | 88 | static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0); |
88 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0); | 89 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0); |
89 | 90 | ||
90 | static const struct snd_kcontrol_new wm8741_snd_controls[] = { | 91 | static const struct snd_kcontrol_new wm8741_snd_controls_stereo[] = { |
91 | SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, | 92 | SOC_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), |
93 | SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, | 94 | SOC_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 | ||
98 | static const struct snd_kcontrol_new wm8741_snd_controls_mono_left[] = { | ||
99 | SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, | ||
100 | 1, 255, 1, dac_tlv_fine), | ||
101 | SOC_SINGLE_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, | ||
102 | 0, 511, 1, dac_tlv), | ||
103 | }; | ||
104 | |||
105 | static const struct snd_kcontrol_new wm8741_snd_controls_mono_right[] = { | ||
106 | SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACRLSB_ATTENUATION, | ||
107 | 1, 255, 1, dac_tlv_fine), | ||
108 | SOC_SINGLE_TLV("Playback Volume", WM8741_DACRMSB_ATTENUATION, | ||
109 | 0, 511, 1, dac_tlv), | ||
110 | }; | ||
111 | |||
97 | static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = { | 112 | static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = { |
98 | SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0), | 113 | SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0), |
99 | SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0), | 114 | SND_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 | ||
434 | static 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 | |||
465 | static 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 | |||
419 | static int wm8741_probe(struct snd_soc_codec *codec) | 493 | static 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 | ||
570 | static 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) |
497 | static int wm8741_i2c_probe(struct i2c_client *i2c, | 588 | static 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 | ||
217 | struct wm8741_platform_data { | ||
218 | u32 diff_mode; /* Differential Output Mode */ | ||
219 | }; | ||
220 | |||
211 | #endif | 221 | #endif |