Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os 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>

authored by

Sergej Sawazki and committed by
Mark Brown
c354b54c b787f68c

+137 -13
+11
Documentation/devicetree/bindings/sound/wm8741.txt
··· 10 10 - reg : the I2C address of the device for I2C, the chip select 11 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 22 Example: 14 23 15 24 codec: wm8741@1a { 16 25 compatible = "wlf,wm8741"; 17 26 reg = <0x1a>; 27 + 28 + diff-mode = <3>; 18 29 };
+116 -13
sound/soc/codecs/wm8741.c
··· 41 41 42 42 /* codec private data */ 43 43 struct wm8741_priv { 44 + struct wm8741_platform_data pdata; 44 45 struct regmap *regmap; 45 46 struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES]; 46 47 unsigned int sysclk; ··· 88 87 static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0); 89 88 static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0); 90 89 91 - static const struct snd_kcontrol_new wm8741_snd_controls[] = { 90 + static const struct snd_kcontrol_new wm8741_snd_controls_stereo[] = { 92 91 SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, 93 92 WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine), 94 93 SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, 95 94 WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv), 95 + }; 96 + 97 + static const struct snd_kcontrol_new wm8741_snd_controls_mono_left[] = { 98 + SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, 99 + 1, 255, 1, dac_tlv_fine), 100 + SOC_SINGLE_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, 101 + 0, 511, 1, dac_tlv), 102 + }; 103 + 104 + static const struct snd_kcontrol_new wm8741_snd_controls_mono_right[] = { 105 + SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACRLSB_ATTENUATION, 106 + 1, 255, 1, dac_tlv_fine), 107 + SOC_SINGLE_TLV("Playback Volume", WM8741_DACRMSB_ATTENUATION, 108 + 0, 511, 1, dac_tlv), 96 109 }; 97 110 98 111 static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = { ··· 413 398 .name = "wm8741", 414 399 .playback = { 415 400 .stream_name = "Playback", 416 - .channels_min = 2, /* Mono modes not yet supported */ 401 + .channels_min = 2, 417 402 .channels_max = 2, 418 403 .rates = WM8741_RATES, 419 404 .formats = WM8741_FORMATS, ··· 430 415 #else 431 416 #define wm8741_resume NULL 432 417 #endif 418 + 419 + static int wm8741_configure(struct snd_soc_codec *codec) 420 + { 421 + struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); 422 + 423 + /* Configure differential mode */ 424 + switch (wm8741->pdata.diff_mode) { 425 + case WM8741_DIFF_MODE_STEREO: 426 + case WM8741_DIFF_MODE_STEREO_REVERSED: 427 + case WM8741_DIFF_MODE_MONO_LEFT: 428 + case WM8741_DIFF_MODE_MONO_RIGHT: 429 + snd_soc_update_bits(codec, WM8741_MODE_CONTROL_2, 430 + WM8741_DIFF_MASK, 431 + wm8741->pdata.diff_mode << WM8741_DIFF_SHIFT); 432 + break; 433 + default: 434 + return -EINVAL; 435 + } 436 + 437 + /* Change some default settings - latch VU */ 438 + snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION, 439 + WM8741_UPDATELL, WM8741_UPDATELL); 440 + snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION, 441 + WM8741_UPDATELM, WM8741_UPDATELM); 442 + snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, 443 + WM8741_UPDATERL, WM8741_UPDATERL); 444 + snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION, 445 + WM8741_UPDATERM, WM8741_UPDATERM); 446 + 447 + return 0; 448 + } 449 + 450 + static int wm8741_add_controls(struct snd_soc_codec *codec) 451 + { 452 + struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); 453 + 454 + switch (wm8741->pdata.diff_mode) { 455 + case WM8741_DIFF_MODE_STEREO: 456 + case WM8741_DIFF_MODE_STEREO_REVERSED: 457 + snd_soc_add_codec_controls(codec, 458 + wm8741_snd_controls_stereo, 459 + ARRAY_SIZE(wm8741_snd_controls_stereo)); 460 + break; 461 + case WM8741_DIFF_MODE_MONO_LEFT: 462 + snd_soc_add_codec_controls(codec, 463 + wm8741_snd_controls_mono_left, 464 + ARRAY_SIZE(wm8741_snd_controls_mono_left)); 465 + break; 466 + case WM8741_DIFF_MODE_MONO_RIGHT: 467 + snd_soc_add_codec_controls(codec, 468 + wm8741_snd_controls_mono_right, 469 + ARRAY_SIZE(wm8741_snd_controls_mono_right)); 470 + break; 471 + default: 472 + return -EINVAL; 473 + } 474 + 475 + return 0; 476 + } 433 477 434 478 static int wm8741_probe(struct snd_soc_codec *codec) 435 479 { ··· 508 434 goto err_enable; 509 435 } 510 436 511 - /* Change some default settings - latch VU */ 512 - snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION, 513 - WM8741_UPDATELL, WM8741_UPDATELL); 514 - snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION, 515 - WM8741_UPDATELM, WM8741_UPDATELM); 516 - snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, 517 - WM8741_UPDATERL, WM8741_UPDATERL); 518 - snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION, 519 - WM8741_UPDATERM, WM8741_UPDATERM); 437 + ret = wm8741_configure(codec); 438 + if (ret < 0) { 439 + dev_err(codec->dev, "Failed to change default settings\n"); 440 + goto err_enable; 441 + } 442 + 443 + ret = wm8741_add_controls(codec); 444 + if (ret < 0) { 445 + dev_err(codec->dev, "Failed to add controls\n"); 446 + goto err_enable; 447 + } 520 448 521 449 dev_dbg(codec->dev, "Successful registration\n"); 522 450 return ret; ··· 543 467 .remove = wm8741_remove, 544 468 .resume = wm8741_resume, 545 469 546 - .controls = wm8741_snd_controls, 547 - .num_controls = ARRAY_SIZE(wm8741_snd_controls), 548 470 .dapm_widgets = wm8741_dapm_widgets, 549 471 .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets), 550 472 .dapm_routes = wm8741_dapm_routes, ··· 566 492 567 493 .readable_reg = wm8741_readable, 568 494 }; 495 + 496 + static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741) 497 + { 498 + const struct wm8741_platform_data *pdata = dev_get_platdata(dev); 499 + u32 diff_mode; 500 + 501 + if (dev->of_node) { 502 + if (of_property_read_u32(dev->of_node, "diff-mode", &diff_mode) 503 + >= 0) 504 + wm8741->pdata.diff_mode = diff_mode; 505 + } else { 506 + if (pdata != NULL) 507 + memcpy(&wm8741->pdata, pdata, sizeof(wm8741->pdata)); 508 + } 509 + 510 + return 0; 511 + } 569 512 570 513 #if IS_ENABLED(CONFIG_I2C) 571 514 static int wm8741_i2c_probe(struct i2c_client *i2c, ··· 610 519 if (IS_ERR(wm8741->regmap)) { 611 520 ret = PTR_ERR(wm8741->regmap); 612 521 dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); 522 + return ret; 523 + } 524 + 525 + wm8741_set_pdata(&i2c->dev, wm8741); 526 + if (ret != 0) { 527 + dev_err(&i2c->dev, "Failed to set pdata: %d\n", ret); 613 528 return ret; 614 529 } 615 530 ··· 676 579 if (IS_ERR(wm8741->regmap)) { 677 580 ret = PTR_ERR(wm8741->regmap); 678 581 dev_err(&spi->dev, "Failed to init regmap: %d\n", ret); 582 + return ret; 583 + } 584 + 585 + wm8741_set_pdata(&spi->dev, wm8741); 586 + if (ret != 0) { 587 + dev_err(&spi->dev, "Failed to set pdata: %d\n", ret); 679 588 return ret; 680 589 } 681 590
+10
sound/soc/codecs/wm8741.h
··· 194 194 #define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */ 195 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 204 * R32 (0x20) - ADDITONAL_CONTROL_1 199 205 */ ··· 213 207 #define WM8741_DSD_NO_NOTCH_WIDTH 1 /* DSD_NO_NOTCH */ 214 208 215 209 #define WM8741_SYSCLK 0 210 + 211 + struct wm8741_platform_data { 212 + u32 diff_mode; /* Differential Output Mode */ 213 + }; 216 214 217 215 #endif