From de8d74bf521d9332f4404cd9a281f9be4f9c1728 Mon Sep 17 00:00:00 2001 From: Jose Manuel Tasende Date: Wed, 25 May 2016 17:21:10 +0200 Subject: [PATCH 1/4] Updated for Wolfson WM8742 support --- sound/soc/codecs/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b3afae990e3963..a590e6051fb403 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -159,6 +159,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8737 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8741 if SND_SOC_I2C_AND_SPI + select SND_SOC_WM8742 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8770 if SPI_MASTER @@ -869,6 +870,10 @@ config SND_SOC_WM8741 tristate "Wolfson Microelectronics WM8737 DAC" depends on SND_SOC_I2C_AND_SPI +config SND_SOC_WM8742 + tristate "Wolfson Microelectronics WM8742 DAC" + depends on SND_SOC_I2C_AND_SPI + config SND_SOC_WM8750 tristate "Wolfson Microelectronics WM8750 CODEC" depends on SND_SOC_I2C_AND_SPI From 2b60c622454bfe22c0d0a9a0f6f3705669ff460e Mon Sep 17 00:00:00 2001 From: Jose Manuel Tasende Date: Wed, 25 May 2016 17:22:34 +0200 Subject: [PATCH 2/4] Updated for Wolfson WM8742 support --- sound/soc/codecs/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b7b99416537fd9..8f6e5eb3c638f7 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -165,6 +165,7 @@ snd-soc-wm8728-objs := wm8728.o snd-soc-wm8731-objs := wm8731.o snd-soc-wm8737-objs := wm8737.o snd-soc-wm8741-objs := wm8741.o +snd-soc-wm8742-objs := wm8742.o snd-soc-wm8750-objs := wm8750.o snd-soc-wm8753-objs := wm8753.o snd-soc-wm8770-objs := wm8770.o @@ -372,6 +373,7 @@ obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o obj-$(CONFIG_SND_SOC_WM8737) += snd-soc-wm8737.o obj-$(CONFIG_SND_SOC_WM8741) += snd-soc-wm8741.o +obj-$(CONFIG_SND_SOC_WM8742) += snd-soc-wm8742.o obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o From d0b4bbb7fb5775b8d730e43cda2a3232964c0f7b Mon Sep 17 00:00:00 2001 From: Jose Manuel Tasende Date: Wed, 25 May 2016 17:26:39 +0200 Subject: [PATCH 3/4] Wolfson WM8742 DAC driver added --- sound/soc/codecs/wm8742.c | 726 ++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm8742.h | 219 ++++++++++++ 2 files changed, 945 insertions(+) create mode 100644 sound/soc/codecs/wm8742.c create mode 100644 sound/soc/codecs/wm8742.h diff --git a/sound/soc/codecs/wm8742.c b/sound/soc/codecs/wm8742.c new file mode 100644 index 00000000000000..c7a108acadec2b --- /dev/null +++ b/sound/soc/codecs/wm8742.c @@ -0,0 +1,726 @@ +/* + * wm8742.c -- WM8742 ALSA SoC Audio driver + * + * Author: José M. Tasende + * Based on code from: wm8741.c by Ian Lartey + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8742.h" + +#define WM8742_NUM_SUPPLIES 2 +static const char *wm8742_supply_names[WM8742_NUM_SUPPLIES] = { + "AVDD", + "DVDD", +}; + +#define WM8742_NUM_RATES 6 + +/* codec private data */ +struct wm8742_priv { + struct wm8742_platform_data pdata; + struct regmap *regmap; + struct regulator_bulk_data supplies[WM8742_NUM_SUPPLIES]; + unsigned int sysclk; + const struct snd_pcm_hw_constraint_list *sysclk_constraints; +}; + +static const struct reg_default wm8742_reg_defaults[] = { + { 0, 0x0000 }, /* R0 - DACLLSB Attenuation */ + { 1, 0x0000 }, /* R1 - DACLMSB Attenuation */ + { 2, 0x0000 }, /* R2 - DACRLSB Attenuation */ + { 3, 0x0000 }, /* R3 - DACRMSB Attenuation */ + { 4, 0x0000 }, /* R4 - Volume Control */ + { 5, 0x000A }, /* R5 - Format Control */ + { 6, 0x0000 }, /* R6 - Filter Control */ + { 7, 0x0000 }, /* R7 - Mode Control 1 */ + { 8, 0x0002 }, /* R8 - Mode Control 2 */ + { 32, 0x0002 }, /* R32 - ADDITONAL_CONTROL_1 */ +}; + +static int wm8742_reset(struct snd_soc_codec *codec) +{ + return snd_soc_write(codec, WM8742_RESET, 0); +} + +static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, 0, 13, 0); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12800, 400, 1); +static const char *wm8742_dither[4] = {"Off", "RPDF", "TPDF", "HPDF"}; +static const char *wm8742_filter[5] = {"Type 1", "Type 2", " Type 3", + "Type 4", "Type 5"}; +static const char *wm8742_switch[2] = {"Off", "On"}; +static const struct soc_enum wm8742_enum[5] = { + SOC_ENUM_SINGLE(WM8742_MODE_CONTROL_2, 0, 4, wm8742_dither), + SOC_ENUM_SINGLE(WM8742_FILTER_CONTROL, 0, 5, wm8742_filter), + SOC_ENUM_SINGLE(WM8742_FORMAT_CONTROL, 6, 2, + wm8742_switch),/* phase invert */ + SOC_ENUM_SINGLE(WM8742_VOLUME_CONTROL, 0, 2, + wm8742_switch),/* volume ramp */ + SOC_ENUM_SINGLE(WM8742_VOLUME_CONTROL, 3, 2, + wm8742_switch),/* soft mute */ +}; +static const struct snd_kcontrol_new wm8742_snd_controls_stereo[] = { +SOC_DOUBLE_R_TLV("DAC Fine Playback Volume", WM8742_DACLLSB_ATTENUATION, + WM8742_DACRLSB_ATTENUATION, 0, 31, 1, dac_tlv_fine), +SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8742_DACLMSB_ATTENUATION, + WM8742_DACRMSB_ATTENUATION, 0, 31, 1, dac_tlv), +SOC_ENUM("DAC Dither Control", wm8742_enum[0]), +SOC_ENUM("DAC Digital Filter", wm8742_enum[1]), +SOC_ENUM("DAC Phase Invert", wm8742_enum[2]), +SOC_ENUM("DAC Volume Ramp", wm8742_enum[3]), +SOC_ENUM("DAC Soft Mute", wm8742_enum[4]), +}; + +static const struct snd_kcontrol_new wm8742_snd_controls_mono_left[] = { +SOC_SINGLE_TLV("DAC Fine Playback Volume", WM8742_DACLLSB_ATTENUATION, + 0, 31, 0, dac_tlv_fine), +SOC_SINGLE_TLV("Digital Playback Volume", WM8742_DACLMSB_ATTENUATION, + 0, 31, 1, dac_tlv), +SOC_ENUM("DAC Dither Control", wm8742_enum[0]), +SOC_ENUM("DAC Digital Filter", wm8742_enum[1]), +SOC_ENUM("DAC Phase Invert", wm8742_enum[2]), +SOC_ENUM("DAC Volume Ramp", wm8742_enum[3]), +SOC_ENUM("DAC Soft Mute", wm8742_enum[4]), +}; + +static const struct snd_kcontrol_new wm8742_snd_controls_mono_right[] = { +SOC_SINGLE_TLV("DAC Fine Playback Volume", WM8742_DACRLSB_ATTENUATION, + 0, 31, 0, dac_tlv_fine), +SOC_SINGLE_TLV("Digital Playback Volume", WM8742_DACRMSB_ATTENUATION, + 0, 31, 1, dac_tlv), +SOC_ENUM("DAC Dither Control", wm8742_enum[0]), +SOC_ENUM("DAC Digital Filter", wm8742_enum[1]), +SOC_ENUM("DAC Phase Invert", wm8742_enum[2]), +SOC_ENUM("DAC Volume Ramp", wm8742_enum[3]), +SOC_ENUM("DAC Soft Mute", wm8742_enum[4]), +}; + +static const struct snd_soc_dapm_widget wm8742_dapm_widgets[] = { +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_OUTPUT("VOUTLP"), +SND_SOC_DAPM_OUTPUT("VOUTLN"), +SND_SOC_DAPM_OUTPUT("VOUTRP"), +SND_SOC_DAPM_OUTPUT("VOUTRN"), +}; + +static const struct snd_soc_dapm_route wm8742_dapm_routes[] = { + { "VOUTLP", NULL, "DACL" }, + { "VOUTLN", NULL, "DACL" }, + { "VOUTRP", NULL, "DACR" }, + { "VOUTRN", NULL, "DACR" }, +}; + +static const unsigned int rates_11289[] = { + 44100, 88200, +}; + +static const struct snd_pcm_hw_constraint_list constraints_11289 = { + .count = ARRAY_SIZE(rates_11289), + .list = rates_11289, +}; + +static const unsigned int rates_12288[] = { + 32000, 48000, 96000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_12288 = { + .count = ARRAY_SIZE(rates_12288), + .list = rates_12288, +}; + +static const unsigned int rates_16384[] = { + 32000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_16384 = { + .count = ARRAY_SIZE(rates_16384), + .list = rates_16384, +}; + +static const unsigned int rates_16934[] = { + 44100, 88200, +}; + +static const struct snd_pcm_hw_constraint_list constraints_16934 = { + .count = ARRAY_SIZE(rates_16934), + .list = rates_16934, +}; + +static const unsigned int rates_18432[] = { + 48000, 96000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_18432 = { + .count = ARRAY_SIZE(rates_18432), + .list = rates_18432, +}; + +static const unsigned int rates_22579[] = { + 44100, 88200, 176400 +}; + +static const struct snd_pcm_hw_constraint_list constraints_22579 = { + .count = ARRAY_SIZE(rates_22579), + .list = rates_22579, +}; + +static const unsigned int rates_24576[] = { + 32000, 48000, 96000, 192000 +}; + +static const struct snd_pcm_hw_constraint_list constraints_24576 = { + .count = ARRAY_SIZE(rates_24576), + .list = rates_24576, +}; + +static const unsigned int rates_36864[] = { + 48000, 96000, 192000 +}; + +static const struct snd_pcm_hw_constraint_list constraints_36864 = { + .count = ARRAY_SIZE(rates_36864), + .list = rates_36864, +}; + +static int wm8742_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8742_priv *wm8742 = snd_soc_codec_get_drvdata(codec); + + if (wm8742->sysclk) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + wm8742->sysclk_constraints); + + return 0; +} + +static int wm8742_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8742_priv *wm8742 = snd_soc_codec_get_drvdata(codec); + u16 iface = snd_soc_read(codec, WM8742_FORMAT_CONTROL) & 0x1FC; + int i; + + /* The set of sample rates that can be supported depends on the + * MCLK supplied to the CODEC - enforce this. + */ + if (!wm8742->sysclk) { + dev_err(codec->dev, + "No MCLK configured, call set_sysclk() on init or in hw_params\n"); + return -EINVAL; + } + + /* Find a supported LRCLK rate */ + for (i = 0; i < wm8742->sysclk_constraints->count; i++) { + if (wm8742->sysclk_constraints->list[i] == params_rate(params)) + break; + } + + if (i == wm8742->sysclk_constraints->count) { + dev_err(codec->dev, "LRCLK %d unsupported with MCLK %d\n", + params_rate(params), wm8742->sysclk); + return -EINVAL; + } + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + iface |= 0x0001; + break; + case 24: + iface |= 0x0002; + break; + case 32: + iface |= 0x0003; + break; + default: + dev_dbg(codec->dev, "wm8742_hw_params: Unsupported bit size param = %d", + params_width(params)); + return -EINVAL; + } + + dev_dbg(codec->dev, "wm8742_hw_params: bit size param = %d, rate param = %d", + params_width(params), params_rate(params)); + + snd_soc_write(codec, WM8742_FORMAT_CONTROL, iface); + return 0; +} + +static int wm8742_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8742_priv *wm8742 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "wm8742_set_dai_sysclk info: freq=%dHz\n", freq); + + switch (freq) { + case 0: + wm8742->sysclk_constraints = NULL; + break; + case 11289600: + wm8742->sysclk_constraints = &constraints_11289; + break; + case 12288000: + wm8742->sysclk_constraints = &constraints_12288; + break; + case 16384000: + wm8742->sysclk_constraints = &constraints_16384; + break; + case 16934400: + wm8742->sysclk_constraints = &constraints_16934; + break; + case 18432000: + wm8742->sysclk_constraints = &constraints_18432; + break; + case 22579200: + case 33868800: + wm8742->sysclk_constraints = &constraints_22579; + break; + case 24576000: + wm8742->sysclk_constraints = &constraints_24576; + break; + case 36864000: + wm8742->sysclk_constraints = &constraints_36864; + break; + default: + return -EINVAL; + } + + wm8742->sysclk = freq; + return 0; +} + +static int wm8742_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface = snd_soc_read(codec, WM8742_FORMAT_CONTROL) & 0x1C3; + + /* check master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0008; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0004; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x000C; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x001C; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0010; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0020; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0030; + break; + default: + return -EINVAL; + } + + + dev_dbg(codec->dev, "wm8742_set_dai_fmt: Format=%x, Clock Inv=%x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK, + ((fmt & SND_SOC_DAIFMT_INV_MASK))); + + snd_soc_write(codec, WM8742_FORMAT_CONTROL, iface); + return 0; +} + +#define WM8742_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \ + SNDRV_PCM_RATE_192000) + +#define WM8742_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8742_dai_ops = { + .startup = wm8742_startup, + .hw_params = wm8742_hw_params, + .set_sysclk = wm8742_set_dai_sysclk, + .set_fmt = wm8742_set_dai_fmt, +}; + +static struct snd_soc_dai_driver wm8742_dai = { + .name = "wm8742", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM8742_RATES, + .formats = WM8742_FORMATS, + }, + .ops = &wm8742_dai_ops, +}; + +#ifdef CONFIG_PM +static int wm8742_resume(struct snd_soc_codec *codec) +{ + snd_soc_cache_sync(codec); + return 0; +} +#else +#define wm8742_resume NULL +#endif + +static int wm8742_configure(struct snd_soc_codec *codec) +{ + struct wm8742_priv *wm8742 = snd_soc_codec_get_drvdata(codec); + + /* Configure differential mode */ + switch (wm8742->pdata.diff_mode) { + case WM8742_DIFF_MODE_STEREO: + case WM8742_DIFF_MODE_STEREO_REVERSED: + case WM8742_DIFF_MODE_MONO_LEFT: + case WM8742_DIFF_MODE_MONO_RIGHT: + snd_soc_update_bits(codec, WM8742_MODE_CONTROL_2, + WM8742_DIFF_MASK, + wm8742->pdata.diff_mode << WM8742_DIFF_SHIFT); + break; + default: + return -EINVAL; + } + + /* Change some default settings - latch VU */ + snd_soc_update_bits(codec, WM8742_DACLLSB_ATTENUATION, + WM8742_UPDATELL, WM8742_UPDATELL); + snd_soc_update_bits(codec, WM8742_DACLMSB_ATTENUATION, + WM8742_UPDATELM, WM8742_UPDATELM); + snd_soc_update_bits(codec, WM8742_DACRLSB_ATTENUATION, + WM8742_UPDATERL, WM8742_UPDATERL); + snd_soc_update_bits(codec, WM8742_DACRMSB_ATTENUATION, + WM8742_UPDATERM, WM8742_UPDATERM); + + return 0; +} + +static int wm8742_add_controls(struct snd_soc_codec *codec) +{ + struct wm8742_priv *wm8742 = snd_soc_codec_get_drvdata(codec); + + switch (wm8742->pdata.diff_mode) { + case WM8742_DIFF_MODE_STEREO: + case WM8742_DIFF_MODE_STEREO_REVERSED: + snd_soc_add_codec_controls(codec, + wm8742_snd_controls_stereo, + ARRAY_SIZE(wm8742_snd_controls_stereo)); + break; + case WM8742_DIFF_MODE_MONO_LEFT: + snd_soc_add_codec_controls(codec, + wm8742_snd_controls_mono_left, + ARRAY_SIZE(wm8742_snd_controls_mono_left)); + break; + case WM8742_DIFF_MODE_MONO_RIGHT: + snd_soc_add_codec_controls(codec, + wm8742_snd_controls_mono_right, + ARRAY_SIZE(wm8742_snd_controls_mono_right)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8742_probe(struct snd_soc_codec *codec) +{ + struct wm8742_priv *wm8742 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8742->supplies), + wm8742->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + goto err_get; + } + + ret = wm8742_reset(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to issue reset\n"); + goto err_enable; + } + + ret = wm8742_configure(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to change default settings\n"); + goto err_enable; + } + + ret = wm8742_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; + +err_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8742->supplies), wm8742->supplies); +err_get: + return ret; +} + +static int wm8742_remove(struct snd_soc_codec *codec) +{ + struct wm8742_priv *wm8742 = snd_soc_codec_get_drvdata(codec); + + regulator_bulk_disable(ARRAY_SIZE(wm8742->supplies), wm8742->supplies); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_wm8742 = { + .probe = wm8742_probe, + .remove = wm8742_remove, + .resume = wm8742_resume, + + .dapm_widgets = wm8742_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8742_dapm_widgets), + .dapm_routes = wm8742_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8742_dapm_routes), +}; + +static const struct of_device_id wm8742_of_match[] = { + { .compatible = "wlf,wm8742", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8742_of_match); + +static const struct regmap_config wm8742_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8742_MAX_REGISTER, + + .reg_defaults = wm8742_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8742_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static int wm8742_set_pdata(struct device *dev, struct wm8742_priv *wm8742) +{ + const struct wm8742_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) + wm8742->pdata.diff_mode = diff_mode; + } else { + if (pdata != NULL) + memcpy(&wm8742->pdata, pdata, sizeof(wm8742->pdata)); + } + + return 0; +} + +#if IS_ENABLED(CONFIG_I2C) +static int wm8742_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm8742_priv *wm8742; + int ret, i; + + wm8742 = devm_kzalloc(&i2c->dev, sizeof(struct wm8742_priv), + GFP_KERNEL); + if (wm8742 == NULL) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(wm8742->supplies); i++) + wm8742->supplies[i].supply = wm8742_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8742->supplies), + wm8742->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + wm8742->regmap = devm_regmap_init_i2c(i2c, &wm8742_regmap); + if (IS_ERR(wm8742->regmap)) { + ret = PTR_ERR(wm8742->regmap); + dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + ret = wm8742_set_pdata(&i2c->dev, wm8742); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to set pdata: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, wm8742); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_wm8742, &wm8742_dai, 1); + + return ret; +} + +static int wm8742_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id wm8742_i2c_id[] = { + { "wm8742", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8742_i2c_id); + +static struct i2c_driver wm8742_i2c_driver = { + .driver = { + .name = "wm8742", + .of_match_table = wm8742_of_match, + }, + .probe = wm8742_i2c_probe, + .remove = wm8742_i2c_remove, + .id_table = wm8742_i2c_id, +}; +#endif + +#if defined(CONFIG_SPI_MASTER) +static int wm8742_spi_probe(struct spi_device *spi) +{ + struct wm8742_priv *wm8742; + int ret, i; + + wm8742 = devm_kzalloc(&spi->dev, sizeof(struct wm8742_priv), + GFP_KERNEL); + if (wm8742 == NULL) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(wm8742->supplies); i++) + wm8742->supplies[i].supply = wm8742_supply_names[i]; + + ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8742->supplies), + wm8742->supplies); + if (ret != 0) { + dev_err(&spi->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + wm8742->regmap = devm_regmap_init_spi(spi, &wm8742_regmap); + if (IS_ERR(wm8742->regmap)) { + ret = PTR_ERR(wm8742->regmap); + dev_err(&spi->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + ret = wm8742_set_pdata(&spi->dev, wm8742); + if (ret != 0) { + dev_err(&spi->dev, "Failed to set pdata: %d\n", ret); + return ret; + } + + spi_set_drvdata(spi, wm8742); + + ret = snd_soc_register_codec(&spi->dev, + &soc_codec_dev_wm8742, &wm8742_dai, 1); + return ret; +} + +static int wm8742_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static struct spi_driver wm8742_spi_driver = { + .driver = { + .name = "wm8742", + .of_match_table = wm8742_of_match, + }, + .probe = wm8742_spi_probe, + .remove = wm8742_spi_remove, +}; +#endif /* CONFIG_SPI_MASTER */ + +static int __init wm8742_modinit(void) +{ + int ret = 0; + +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8742_i2c_driver); + if (ret != 0) + pr_err("Failed to register WM8742 I2C driver: %d\n", ret); +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8742_spi_driver); + if (ret != 0) + pr_err("Failed to register wm8742 SPI driver: %d\n", ret); +#endif + + return ret; +} +module_init(wm8742_modinit); + +static void __exit wm8742_exit(void) +{ +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8742_spi_driver); +#endif +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8742_i2c_driver); +#endif +} +module_exit(wm8742_exit); + +MODULE_DESCRIPTION("ASoC Wolfson WM8742 driver"); +MODULE_AUTHOR("José M. Tasende "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8742.h b/sound/soc/codecs/wm8742.h new file mode 100644 index 00000000000000..32b32a16f8a270 --- /dev/null +++ b/sound/soc/codecs/wm8742.h @@ -0,0 +1,219 @@ +/* + * wm8742.h -- Wolfson WM8742 ASoC driver + * + * + * Author: José M. Tasende + * Based on code from: wm8741.h by Ian Lartey + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM8742_H +#define _WM8742_H + +/* + * Register values. + */ +#define WM8742_DACLLSB_ATTENUATION 0x00 +#define WM8742_DACLMSB_ATTENUATION 0x01 +#define WM8742_DACRLSB_ATTENUATION 0x02 +#define WM8742_DACRMSB_ATTENUATION 0x03 +#define WM8742_VOLUME_CONTROL 0x04 +#define WM8742_FORMAT_CONTROL 0x05 +#define WM8742_FILTER_CONTROL 0x06 +#define WM8742_MODE_CONTROL_1 0x07 +#define WM8742_MODE_CONTROL_2 0x08 +#define WM8742_RESET 0x09 +#define WM8742_ADDITIONAL_CONTROL_1 0x20 + +#define WM8742_REGISTER_COUNT 11 +#define WM8742_MAX_REGISTER 0x20 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - DACLLSB_ATTENUATION + */ +#define WM8742_UPDATELL 0x0020 /* UPDATELL */ +#define WM8742_UPDATELL_MASK 0x0020 /* UPDATELL */ +#define WM8742_UPDATELL_SHIFT 5 /* UPDATELL */ +#define WM8742_UPDATELL_WIDTH 1 /* UPDATELL */ +#define WM8742_LAT_4_0_MASK 0x001F /* LAT[4:0] - [4:0] */ +#define WM8742_LAT_4_0_SHIFT 0 /* LAT[4:0] - [4:0] */ +#define WM8742_LAT_4_0_WIDTH 5 /* LAT[4:0] - [4:0] */ + +/* + * R1 (0x01) - DACLMSB_ATTENUATION + */ +#define WM8742_UPDATELM 0x0020 /* UPDATELM */ +#define WM8742_UPDATELM_MASK 0x0020 /* UPDATELM */ +#define WM8742_UPDATELM_SHIFT 5 /* UPDATELM */ +#define WM8742_UPDATELM_WIDTH 1 /* UPDATELM */ +#define WM8742_LAT_9_5_0_MASK 0x001F /* LAT[9:5] - [4:0] */ +#define WM8742_LAT_9_5_0_SHIFT 0 /* LAT[9:5] - [4:0] */ +#define WM8742_LAT_9_5_0_WIDTH 5 /* LAT[9:5] - [4:0] */ + +/* + * R2 (0x02) - DACRLSB_ATTENUATION + */ +#define WM8742_UPDATERL 0x0020 /* UPDATERL */ +#define WM8742_UPDATERL_MASK 0x0020 /* UPDATERL */ +#define WM8742_UPDATERL_SHIFT 5 /* UPDATERL */ +#define WM8742_UPDATERL_WIDTH 1 /* UPDATERL */ +#define WM8742_RAT_4_0_MASK 0x001F /* RAT[4:0] - [4:0] */ +#define WM8742_RAT_4_0_SHIFT 0 /* RAT[4:0] - [4:0] */ +#define WM8742_RAT_4_0_WIDTH 5 /* RAT[4:0] - [4:0] */ + +/* + * R3 (0x03) - DACRMSB_ATTENUATION + */ +#define WM8742_UPDATERM 0x0020 /* UPDATERM */ +#define WM8742_UPDATERM_MASK 0x0020 /* UPDATERM */ +#define WM8742_UPDATERM_SHIFT 5 /* UPDATERM */ +#define WM8742_UPDATERM_WIDTH 1 /* UPDATERM */ +#define WM8742_RAT_9_5_0_MASK 0x001F /* RAT[9:5] - [4:0] */ +#define WM8742_RAT_9_5_0_SHIFT 0 /* RAT[9:5] - [4:0] */ +#define WM8742_RAT_9_5_0_WIDTH 5 /* RAT[9:5] - [4:0] */ + +/* + * R4 (0x04) - VOLUME_CONTROL + */ +#define WM8742_AMUTE 0x0080 /* AMUTE */ +#define WM8742_AMUTE_MASK 0x0080 /* AMUTE */ +#define WM8742_AMUTE_SHIFT 7 /* AMUTE */ +#define WM8742_AMUTE_WIDTH 1 /* AMUTE */ +#define WM8742_ZFLAG_MASK 0x0060 /* ZFLAG - [6:5] */ +#define WM8742_ZFLAG_SHIFT 5 /* ZFLAG - [6:5] */ +#define WM8742_ZFLAG_WIDTH 2 /* ZFLAG - [6:5] */ +#define WM8742_IZD 0x0010 /* IZD */ +#define WM8742_IZD_MASK 0x0010 /* IZD */ +#define WM8742_IZD_SHIFT 4 /* IZD */ +#define WM8742_IZD_WIDTH 1 /* IZD */ +#define WM8742_SOFT 0x0008 /* SOFT MUTE */ +#define WM8742_SOFT_MASK 0x0008 /* SOFT MUTE */ +#define WM8742_SOFT_SHIFT 3 /* SOFT MUTE */ +#define WM8742_SOFT_WIDTH 1 /* SOFT MUTE */ +#define WM8742_ATC 0x0004 /* ATC */ +#define WM8742_ATC_MASK 0x0004 /* ATC */ +#define WM8742_ATC_SHIFT 2 /* ATC */ +#define WM8742_ATC_WIDTH 1 /* ATC */ +#define WM8742_ATT2DB 0x0002 /* ATT2DB */ +#define WM8742_ATT2DB_MASK 0x0002 /* ATT2DB */ +#define WM8742_ATT2DB_SHIFT 1 /* ATT2DB */ +#define WM8742_ATT2DB_WIDTH 1 /* ATT2DB */ +#define WM8742_VOL_RAMP 0x0001 /* VOL_RAMP */ +#define WM8742_VOL_RAMP_MASK 0x0001 /* VOL_RAMP */ +#define WM8742_VOL_RAMP_SHIFT 0 /* VOL_RAMP */ +#define WM8742_VOL_RAMP_WIDTH 1 /* VOL_RAMP */ + +/* + * R5 (0x05) - FORMAT_CONTROL + */ +#define WM8742_PWDN 0x0080 /* PWDN */ +#define WM8742_PWDN_MASK 0x0080 /* PWDN */ +#define WM8742_PWDN_SHIFT 7 /* PWDN */ +#define WM8742_PWDN_WIDTH 1 /* PWDN */ +#define WM8742_REV 0x0040 /* REV */ +#define WM8742_REV_MASK 0x0040 /* REV */ +#define WM8742_REV_SHIFT 6 /* REV */ +#define WM8742_REV_WIDTH 1 /* REV */ +#define WM8742_BCP 0x0020 /* BCP */ +#define WM8742_BCP_MASK 0x0020 /* BCP */ +#define WM8742_BCP_SHIFT 5 /* BCP */ +#define WM8742_BCP_WIDTH 1 /* BCP */ +#define WM8742_LRP 0x0010 /* LRP */ +#define WM8742_LRP_MASK 0x0010 /* LRP */ +#define WM8742_LRP_SHIFT 4 /* LRP */ +#define WM8742_LRP_WIDTH 1 /* LRP */ +#define WM8742_FMT_MASK 0x000C /* FMT - [3:2] */ +#define WM8742_FMT_SHIFT 2 /* FMT - [3:2] */ +#define WM8742_FMT_WIDTH 2 /* FMT - [3:2] */ +#define WM8742_IWL_MASK 0x0003 /* IWL - [1:0] */ +#define WM8742_IWL_SHIFT 0 /* IWL - [1:0] */ +#define WM8742_IWL_WIDTH 2 /* IWL - [1:0] */ + +/* + * R6 (0x06) - FILTER_CONTROL + */ +#define WM8742_ZFLAG_HI 0x0080 /* ZFLAG_HI */ +#define WM8742_ZFLAG_HI_MASK 0x0080 /* ZFLAG_HI */ +#define WM8742_ZFLAG_HI_SHIFT 7 /* ZFLAG_HI */ +#define WM8742_ZFLAG_HI_WIDTH 1 /* ZFLAG_HI */ +#define WM8742_DEEMPH_MASK 0x0060 /* DEEMPH - [6:5] */ +#define WM8742_DEEMPH_SHIFT 5 /* DEEMPH - [6:5] */ +#define WM8742_DEEMPH_WIDTH 2 /* DEEMPH - [6:5] */ +#define WM8742_DSDFILT_MASK 0x0018 /* DSDFILT - [4:3] */ +#define WM8742_DSDFILT_SHIFT 3 /* DSDFILT - [4:3] */ +#define WM8742_DSDFILT_WIDTH 2 /* DSDFILT - [4:3] */ +#define WM8742_FIRSEL_MASK 0x0007 /* FIRSEL - [2:0] */ +#define WM8742_FIRSEL_SHIFT 0 /* FIRSEL - [2:0] */ +#define WM8742_FIRSEL_WIDTH 3 /* FIRSEL - [2:0] */ + +/* + * R7 (0x07) - MODE_CONTROL_1 + */ +#define WM8742_MODE8X 0x0080 /* MODE8X */ +#define WM8742_MODE8X_MASK 0x0080 /* MODE8X */ +#define WM8742_MODE8X_SHIFT 7 /* MODE8X */ +#define WM8742_MODE8X_WIDTH 1 /* MODE8X */ +#define WM8742_OSR_MASK 0x0060 /* OSR - [6:5] */ +#define WM8742_OSR_SHIFT 5 /* OSR - [6:5] */ +#define WM8742_OSR_WIDTH 2 /* OSR - [6:5] */ +#define WM8742_SR_MASK 0x001C /* SR - [4:2] */ +#define WM8742_SR_SHIFT 2 /* SR - [4:2] */ +#define WM8742_SR_WIDTH 3 /* SR - [4:2] */ +#define WM8742_MODESEL_MASK 0x0003 /* MODESEL - [1:0] */ +#define WM8742_MODESEL_SHIFT 0 /* MODESEL - [1:0] */ +#define WM8742_MODESEL_WIDTH 2 /* MODESEL - [1:0] */ + +/* + * R8 (0x08) - MODE_CONTROL_2 + */ +#define WM8742_DSD_GAIN 0x0040 /* DSD_GAIN */ +#define WM8742_DSD_GAIN_MASK 0x0040 /* DSD_GAIN */ +#define WM8742_DSD_GAIN_SHIFT 6 /* DSD_GAIN */ +#define WM8742_DSD_GAIN_WIDTH 1 /* DSD_GAIN */ +#define WM8742_SDOUT 0x0020 /* SDOUT */ +#define WM8742_SDOUT_MASK 0x0020 /* SDOUT */ +#define WM8742_SDOUT_SHIFT 5 /* SDOUT */ +#define WM8742_SDOUT_WIDTH 1 /* SDOUT */ +#define WM8742_DOUT 0x0010 /* DOUT */ +#define WM8742_DOUT_MASK 0x0010 /* DOUT */ +#define WM8742_DOUT_SHIFT 4 /* DOUT */ +#define WM8742_DOUT_WIDTH 1 /* DOUT */ +#define WM8742_DIFF_MASK 0x000C /* DIFF - [3:2] */ +#define WM8742_DIFF_SHIFT 2 /* DIFF - [3:2] */ +#define WM8742_DIFF_WIDTH 2 /* DIFF - [3:2] */ +#define WM8742_DITHER_MASK 0x0003 /* DITHER - [1:0] */ +#define WM8742_DITHER_SHIFT 0 /* DITHER - [1:0] */ +#define WM8742_DITHER_WIDTH 2 /* DITHER - [1:0] */ + +/* DIFF field values */ +#define WM8742_DIFF_MODE_STEREO 0 /* stereo normal */ +#define WM8742_DIFF_MODE_STEREO_REVERSED 2 /* stereo reversed */ +#define WM8742_DIFF_MODE_MONO_LEFT 1 /* mono left */ +#define WM8742_DIFF_MODE_MONO_RIGHT 3 /* mono right */ + +/* + * R32 (0x20) - ADDITONAL_CONTROL_1 + */ +#define WM8742_DSD_LEVEL 0x0002 /* DSD_LEVEL */ +#define WM8742_DSD_LEVEL_MASK 0x0002 /* DSD_LEVEL */ +#define WM8742_DSD_LEVEL_SHIFT 1 /* DSD_LEVEL */ +#define WM8742_DSD_LEVEL_WIDTH 1 /* DSD_LEVEL */ +#define WM8742_DSD_NO_NOTCH 0x0001 /* DSD_NO_NOTCH */ +#define WM8742_DSD_NO_NOTCH_MASK 0x0001 /* DSD_NO_NOTCH */ +#define WM8742_DSD_NO_NOTCH_SHIFT 0 /* DSD_NO_NOTCH */ +#define WM8742_DSD_NO_NOTCH_WIDTH 1 /* DSD_NO_NOTCH */ + +#define WM8742_SYSCLK 0 + +struct wm8742_platform_data { + u32 diff_mode; /* Differential Output Mode */ +}; + +#endif From da6843394289dcb5268f0ba7b30dab9d62db639c Mon Sep 17 00:00:00 2001 From: Jose Manuel Tasende Date: Wed, 25 May 2016 17:30:01 +0200 Subject: [PATCH 4/4] Wolfson WM8742 DAC driver