From bd6d417743d941c3e5eabb21abbcac9737f11061 Mon Sep 17 00:00:00 2001 From: Mike Arthur Date: Tue, 18 Aug 2009 20:37:49 +0100 Subject: ASoC: Add WM8711 CODEC driver The WM8711 or WM8711L (WM8711/L) is a low power stereo DAC with an integrated headphone driver. The WM8711/L is designed specifically for portable MP3 audio and speech players. The WM8711/L is also ideal for MD, CD machines and DAT players. Signed-off-by: Mike Arthur Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/wm8711.c | 685 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm8711.h | 42 +++ 4 files changed, 733 insertions(+) create mode 100644 sound/soc/codecs/wm8711.c create mode 100644 sound/soc/codecs/wm8711.h (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b6c7f7a01cb..663840e6776 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -29,6 +29,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8400 if MFD_WM8400 select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8580 if I2C + select SND_SOC_WM8711 if I2C select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI @@ -120,6 +121,9 @@ config SND_SOC_WM8510 config SND_SOC_WM8580 tristate +config SND_SOC_WM8711 + tristate + config SND_SOC_WM8728 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f2653803ede..19950e998b6 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -17,6 +17,7 @@ snd-soc-wm8350-objs := wm8350.o snd-soc-wm8400-objs := wm8400.o snd-soc-wm8510-objs := wm8510.o snd-soc-wm8580-objs := wm8580.o +snd-soc-wm8711-objs := wm8711.o snd-soc-wm8728-objs := wm8728.o snd-soc-wm8731-objs := wm8731.o snd-soc-wm8750-objs := wm8750.o @@ -48,6 +49,7 @@ obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o +obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c new file mode 100644 index 00000000000..84ead3f9293 --- /dev/null +++ b/sound/soc/codecs/wm8711.c @@ -0,0 +1,685 @@ +/* + * wm8711.c -- WM8711 ALSA SoC Audio driver + * + * Copyright 2006 Wolfson Microelectronics + * + * Author: Mike Arthur + * + * Based on wm8731.c by Richard Purdie + * + * 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 "wm8711.h" + +#define AUDIO_NAME "wm8711" +#define WM8711_VERSION "0.3" + +/* codec private data */ +struct wm8711_priv { + unsigned int sysclk; +}; + +/* + * wm8711 register cache + * We can't read the WM8711 register space when we are + * using 2 wire for device control, so we cache them instead. + * There is no point in caching the reset register + */ +static const u16 wm8711_reg[WM8711_CACHEREGNUM] = { + 0x0079, 0x0079, 0x000a, 0x0008, + 0x009f, 0x000a, 0x0000, 0x0000 +}; + +/* + * read wm8711 register cache + */ +static inline unsigned int wm8711_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + if (reg == WM8711_RESET) + return 0; + if (reg >= WM8711_CACHEREGNUM) + return -1; + return cache[reg]; +} + +/* + * write wm8711 register cache + */ +static inline void wm8711_write_reg_cache(struct snd_soc_codec *codec, + u16 reg, unsigned int value) +{ + u16 *cache = codec->reg_cache; + if (reg >= WM8711_CACHEREGNUM) + return; + cache[reg] = value; +} + +/* + * write to the WM8711 register space + */ +static int wm8711_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[2]; + + /* data is + * D15..D9 WM8753 register offset + * D8...D0 register data + */ + data[0] = (reg << 1) | ((value >> 8) & 0x0001); + data[1] = value & 0x00ff; + + wm8711_write_reg_cache(codec, reg, value); + if (codec->hw_write(codec->control_data, data, 2) == 2) + return 0; + else + return -EIO; +} + +#define wm8711_reset(c) wm8711_write(c, WM8711_RESET, 0) + +static const struct snd_kcontrol_new wm8711_snd_controls[] = { + +SOC_DOUBLE_R("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V, + 0, 127, 0), +SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V, + 7, 1, 0), + +}; + +/* add non dapm controls */ +static int wm8711_add_controls(struct snd_soc_codec *codec) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(wm8711_snd_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&wm8711_snd_controls[i], codec, + NULL)); + if (err < 0) + return err; + } + + return 0; +} + +/* Output Mixer */ +static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = { +SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0), +SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = { +SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1, + &wm8711_output_mixer_controls[0], + ARRAY_SIZE(wm8711_output_mixer_controls)), +SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1), +SND_SOC_DAPM_OUTPUT("LOUT"), +SND_SOC_DAPM_OUTPUT("LHPOUT"), +SND_SOC_DAPM_OUTPUT("ROUT"), +SND_SOC_DAPM_OUTPUT("RHPOUT"), +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* output mixer */ + {"Output Mixer", "Line Bypass Switch", "Line Input"}, + {"Output Mixer", "HiFi Playback Switch", "DAC"}, + + /* outputs */ + {"RHPOUT", NULL, "Output Mixer"}, + {"ROUT", NULL, "Output Mixer"}, + {"LHPOUT", NULL, "Output Mixer"}, + {"LOUT", NULL, "Output Mixer"}, +}; + +static int wm8711_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, wm8711_dapm_widgets, + ARRAY_SIZE(wm8711_dapm_widgets)); + + snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +struct _coeff_div { + u32 mclk; + u32 rate; + u16 fs; + u8 sr:4; + u8 bosr:1; + u8 usb:1; +}; + +/* codec mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 48k */ + {12288000, 48000, 256, 0x0, 0x0, 0x0}, + {18432000, 48000, 384, 0x0, 0x1, 0x0}, + {12000000, 48000, 250, 0x0, 0x0, 0x1}, + + /* 32k */ + {12288000, 32000, 384, 0x6, 0x0, 0x0}, + {18432000, 32000, 576, 0x6, 0x1, 0x0}, + {12000000, 32000, 375, 0x6, 0x0, 0x1}, + + /* 8k */ + {12288000, 8000, 1536, 0x3, 0x0, 0x0}, + {18432000, 8000, 2304, 0x3, 0x1, 0x0}, + {11289600, 8000, 1408, 0xb, 0x0, 0x0}, + {16934400, 8000, 2112, 0xb, 0x1, 0x0}, + {12000000, 8000, 1500, 0x3, 0x0, 0x1}, + + /* 96k */ + {12288000, 96000, 128, 0x7, 0x0, 0x0}, + {18432000, 96000, 192, 0x7, 0x1, 0x0}, + {12000000, 96000, 125, 0x7, 0x0, 0x1}, + + /* 44.1k */ + {11289600, 44100, 256, 0x8, 0x0, 0x0}, + {16934400, 44100, 384, 0x8, 0x1, 0x0}, + {12000000, 44100, 272, 0x8, 0x1, 0x1}, + + /* 88.2k */ + {11289600, 88200, 128, 0xf, 0x0, 0x0}, + {16934400, 88200, 192, 0xf, 0x1, 0x0}, + {12000000, 88200, 136, 0xf, 0x1, 0x1}, +}; + +static inline int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return 0; +} + +static int wm8711_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 wm8711_priv *wm8711 = codec->private_data; + u16 iface = wm8711_read_reg_cache(codec, WM8711_IFACE) & 0xfffc; + int i = get_coeff(wm8711->sysclk, params_rate(params)); + u16 srate = (coeff_div[i].sr << 2) | + (coeff_div[i].bosr << 1) | coeff_div[i].usb; + + wm8711_write(codec, WM8711_SRATE, srate); + + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x0004; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= 0x0008; + break; + } + + wm8711_write(codec, WM8711_IFACE, iface); + return 0; +} + +static int wm8711_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + + /* set active */ + wm8711_write(codec, WM8711_ACTIVE, 0x0001); + + return 0; +} + +static void wm8711_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + + /* deactivate */ + if (!codec->active) { + udelay(50); + wm8711_write(codec, WM8711_ACTIVE, 0x0); + } +} + +static int wm8711_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u16 mute_reg = wm8711_read_reg_cache(codec, WM8711_APDIGI) & 0xfff7; + + if (mute) + wm8711_write(codec, WM8711_APDIGI, mute_reg | 0x8); + else + wm8711_write(codec, WM8711_APDIGI, mute_reg); + + return 0; +} + +static int wm8711_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 wm8711_priv *wm8711 = codec->private_data; + + switch (freq) { + case 11289600: + case 12000000: + case 12288000: + case 16934400: + case 18432000: + wm8711->sysclk = freq; + return 0; + } + return -EINVAL; +} + +static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface |= 0x0040; + break; + 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 |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0013; + 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 |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0010; + break; + default: + return -EINVAL; + } + + /* set iface */ + wm8711_write(codec, WM8711_IFACE, iface); + return 0; +} + + +static int wm8711_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u16 reg = wm8711_read_reg_cache(codec, WM8711_PWR) & 0xff7f; + + switch (level) { + case SND_SOC_BIAS_ON: + wm8711_write(codec, WM8711_PWR, reg); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + wm8711_write(codec, WM8711_PWR, reg | 0x0040); + break; + case SND_SOC_BIAS_OFF: + wm8711_write(codec, WM8711_ACTIVE, 0x0); + wm8711_write(codec, WM8711_PWR, 0xffff); + break; + } + codec->bias_level = level; + return 0; +} + +#define WM8711_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000) + +#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops wm8711_ops = { + .prepare = wm8711_pcm_prepare, + .hw_params = wm8711_hw_params, + .shutdown = wm8711_shutdown, + .digital_mute = wm8711_mute, + .set_sysclk = wm8711_set_dai_sysclk, + .set_fmt = wm8711_set_dai_fmt, +}; + +struct snd_soc_dai wm8711_dai = { + .name = "WM8711", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8711_RATES, + .formats = WM8711_FORMATS,}, + .ops = &wm8711_ops, +}; +EXPORT_SYMBOL_GPL(wm8711_dai); + +static int wm8711_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + wm8711_write(codec, WM8711_ACTIVE, 0x0); + wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int wm8711_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + int i; + u8 data[2]; + u16 *cache = codec->reg_cache; + + /* Sync reg_cache with the hardware */ + for (i = 0; i < ARRAY_SIZE(wm8711_reg); i++) { + data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); + data[1] = cache[i] & 0x00ff; + codec->hw_write(codec->control_data, data, 2); + } + wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + wm8711_set_bias_level(codec, codec->suspend_bias_level); + return 0; +} + +/* + * initialise the WM8711 driver + * register the mixer and dsp interfaces with the kernel + */ +static int wm8711_init(struct snd_soc_device *socdev) +{ + struct snd_soc_codec *codec = socdev->card->codec; + int reg, ret = 0; + + codec->name = "WM8711"; + codec->owner = THIS_MODULE; + codec->read = wm8711_read_reg_cache; + codec->write = wm8711_write; + codec->set_bias_level = wm8711_set_bias_level; + codec->dai = &wm8711_dai; + codec->num_dai = 1; + codec->reg_cache_size = ARRAY_SIZE(wm8711_reg); + codec->reg_cache = kmemdup(wm8711_reg, sizeof(wm8711_reg), GFP_KERNEL); + + if (codec->reg_cache == NULL) + return -ENOMEM; + + wm8711_reset(codec); + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "wm8711: failed to create pcms\n"); + goto pcm_err; + } + + /* power on device */ + wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* set the update bits */ + reg = wm8711_read_reg_cache(codec, WM8711_LOUT1V); + wm8711_write(codec, WM8711_LOUT1V, reg | 0x0100); + reg = wm8711_read_reg_cache(codec, WM8711_ROUT1V); + wm8711_write(codec, WM8711_ROUT1V, reg | 0x0100); + + wm8711_add_controls(codec); + wm8711_add_widgets(codec); + ret = snd_soc_init_card(socdev); + if (ret < 0) { + printk(KERN_ERR "wm8711: failed to register card\n"); + goto card_err; + } + return ret; + +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + kfree(codec->reg_cache); + return ret; +} + +static struct snd_soc_device *wm8711_socdev; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + +/* + * WM8711 2 wire address is determined by GPIO5 + * state during powerup. + * low = 0x1a + * high = 0x1b + */ +#define I2C_DRIVERID_WM8711 0xfefe /* liam - need a proper id */ + +static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; + +/* Magic definition of all other variables and things */ +I2C_CLIENT_INSMOD; + +static struct i2c_driver wm8711_i2c_driver; +static struct i2c_client client_template; + +/* If the i2c layer weren't so broken, we could pass this kind of data + around */ + +static int wm8711_codec_probe(struct i2c_adapter *adap, int addr, int kind) +{ + struct snd_soc_device *socdev = wm8711_socdev; + struct wm8711_setup_data *setup = socdev->codec_data; + struct snd_soc_codec *codec = socdev->card->codec; + struct i2c_client *i2c; + int ret; + + if (addr != setup->i2c_address) + return -ENODEV; + + client_template.adapter = adap; + client_template.addr = addr; + + i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); + if (i2c == NULL) { + kfree(codec); + return -ENOMEM; + } + + i2c_set_clientdata(i2c, codec); + + codec->control_data = i2c; + + ret = i2c_attach_client(i2c); + if (ret < 0) { + pr_err("failed to attach codec at addr %x\n", addr); + goto err; + } + + ret = wm8711_init(socdev); + if (ret < 0) { + pr_err("failed to initialise WM8711\n"); + goto err; + } + return ret; + +err: + kfree(codec); + kfree(i2c); + return ret; +} + +static int wm8711_i2c_detach(struct i2c_client *client) +{ + struct snd_soc_codec *codec = i2c_get_clientdata(client); + + i2c_detach_client(client); + kfree(codec->reg_cache); + kfree(client); + return 0; +} + +static int wm8711_i2c_attach(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &addr_data, wm8711_codec_probe); +} + +/* corgi i2c codec control layer */ +static struct i2c_driver wm8711_i2c_driver = { + .driver = { + .name = "WM8711 I2C Codec", + .owner = THIS_MODULE, + }, + .id = I2C_DRIVERID_WM8711, + .attach_adapter = wm8711_i2c_attach, + .detach_client = wm8711_i2c_detach, + .command = NULL, +}; + +static struct i2c_client client_template = { + .name = "WM8711", + .driver = &wm8711_i2c_driver, +}; +#endif + +static int wm8711_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct wm8711_setup_data *setup; + struct snd_soc_codec *codec; + struct wm8711_priv *wm8711; + int ret = 0; + + pr_info("WM8711 Audio Codec %s", WM8711_VERSION); + + setup = socdev->codec_data; + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + + wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL); + if (wm8711 == NULL) { + kfree(codec); + return -ENOMEM; + } + + codec->private_data = wm8711; + socdev->card->codec = codec; + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + wm8711_socdev = socdev; +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + if (setup->i2c_address) { + normal_i2c[0] = setup->i2c_address; + codec->hw_write = (hw_write_t)i2c_master_send; + ret = i2c_add_driver(&wm8711_i2c_driver); + if (ret != 0) + printk(KERN_ERR "can't add i2c driver"); + } +#else + /* Add other interfaces here */ +#endif + return ret; +} + +/* power down chip */ +static int wm8711_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + if (codec->control_data) + wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&wm8711_i2c_driver); +#endif + kfree(codec->private_data); + kfree(codec); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm8711 = { + .probe = wm8711_probe, + .remove = wm8711_remove, + .suspend = wm8711_suspend, + .resume = wm8711_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8711); + +static int __init wm8711_modinit(void) +{ + return snd_soc_register_dai(&wm8711_dai); +} +module_init(wm8711_modinit); + +static void __exit wm8711_exit(void) +{ + snd_soc_unregister_dai(&wm8711_dai); +} +module_exit(wm8711_exit); + +MODULE_DESCRIPTION("ASoC WM8711 driver"); +MODULE_AUTHOR("Mike Arthur"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8711.h b/sound/soc/codecs/wm8711.h new file mode 100644 index 00000000000..381e84a4381 --- /dev/null +++ b/sound/soc/codecs/wm8711.h @@ -0,0 +1,42 @@ +/* + * wm8711.h -- WM8711 Soc Audio driver + * + * Copyright 2006 Wolfson Microelectronics + * + * Author: Mike Arthur + * + * Based on wm8731.h + * + * 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 _WM8711_H +#define _WM8711_H + +/* WM8711 register space */ + +#define WM8711_LOUT1V 0x02 +#define WM8711_ROUT1V 0x03 +#define WM8711_APANA 0x04 +#define WM8711_APDIGI 0x05 +#define WM8711_PWR 0x06 +#define WM8711_IFACE 0x07 +#define WM8711_SRATE 0x08 +#define WM8711_ACTIVE 0x09 +#define WM8711_RESET 0x0f + +#define WM8711_CACHEREGNUM 8 + +#define WM8711_SYSCLK 0 +#define WM8711_DAI 0 + +struct wm8711_setup_data { + unsigned short i2c_address; +}; + +extern struct snd_soc_dai wm8711_dai; +extern struct snd_soc_codec_device soc_codec_dev_wm8711; + +#endif -- cgit v1.2.3 From 318b0b8d90326aee6a66c994432eee95c0a9aaea Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 18 Aug 2009 20:57:33 +0100 Subject: ASoC: Update WM8711 to driver model registration method Signed-off-by: Mark Brown --- sound/soc/codecs/wm8711.c | 297 ++++++++++++++++++++-------------------------- 1 file changed, 129 insertions(+), 168 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 84ead3f9293..812283e2760 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -28,11 +28,12 @@ #include "wm8711.h" -#define AUDIO_NAME "wm8711" -#define WM8711_VERSION "0.3" +static struct snd_soc_codec *wm8711_codec; /* codec private data */ struct wm8711_priv { + struct snd_soc_codec codec; + u16 reg_cache[WM8711_CACHEREGNUM]; unsigned int sysclk; }; @@ -442,241 +443,201 @@ static int wm8711_resume(struct platform_device *pdev) return 0; } -/* - * initialise the WM8711 driver - * register the mixer and dsp interfaces with the kernel - */ -static int wm8711_init(struct snd_soc_device *socdev) +static int wm8711_probe(struct platform_device *pdev) { - struct snd_soc_codec *codec = socdev->card->codec; - int reg, ret = 0; - - codec->name = "WM8711"; - codec->owner = THIS_MODULE; - codec->read = wm8711_read_reg_cache; - codec->write = wm8711_write; - codec->set_bias_level = wm8711_set_bias_level; - codec->dai = &wm8711_dai; - codec->num_dai = 1; - codec->reg_cache_size = ARRAY_SIZE(wm8711_reg); - codec->reg_cache = kmemdup(wm8711_reg, sizeof(wm8711_reg), GFP_KERNEL); + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; - if (codec->reg_cache == NULL) - return -ENOMEM; + if (wm8711_codec == NULL) { + dev_err(&pdev->dev, "Codec device not registered\n"); + return -ENODEV; + } - wm8711_reset(codec); + socdev->card->codec = wm8711_codec; + codec = wm8711_codec; /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) { - printk(KERN_ERR "wm8711: failed to create pcms\n"); + dev_err(codec->dev, "failed to create pcms: %d\n", ret); goto pcm_err; } - /* power on device */ - wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - /* set the update bits */ - reg = wm8711_read_reg_cache(codec, WM8711_LOUT1V); - wm8711_write(codec, WM8711_LOUT1V, reg | 0x0100); - reg = wm8711_read_reg_cache(codec, WM8711_ROUT1V); - wm8711_write(codec, WM8711_ROUT1V, reg | 0x0100); - - wm8711_add_controls(codec); + snd_soc_add_controls(codec, wm8711_snd_controls, + ARRAY_SIZE(wm8711_snd_controls)); wm8711_add_widgets(codec); ret = snd_soc_init_card(socdev); if (ret < 0) { - printk(KERN_ERR "wm8711: failed to register card\n"); + dev_err(codec->dev, "failed to register card: %d\n", ret); goto card_err; } + return ret; card_err: snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); pcm_err: - kfree(codec->reg_cache); return ret; } -static struct snd_soc_device *wm8711_socdev; - -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - -/* - * WM8711 2 wire address is determined by GPIO5 - * state during powerup. - * low = 0x1a - * high = 0x1b - */ -#define I2C_DRIVERID_WM8711 0xfefe /* liam - need a proper id */ - -static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; +/* power down chip */ +static int wm8711_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); -/* Magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); -static struct i2c_driver wm8711_i2c_driver; -static struct i2c_client client_template; + return 0; +} -/* If the i2c layer weren't so broken, we could pass this kind of data - around */ +struct snd_soc_codec_device soc_codec_dev_wm8711 = { + .probe = wm8711_probe, + .remove = wm8711_remove, + .suspend = wm8711_suspend, + .resume = wm8711_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8711); -static int wm8711_codec_probe(struct i2c_adapter *adap, int addr, int kind) +static int wm8711_register(struct wm8711_priv *wm8711) { - struct snd_soc_device *socdev = wm8711_socdev; - struct wm8711_setup_data *setup = socdev->codec_data; - struct snd_soc_codec *codec = socdev->card->codec; - struct i2c_client *i2c; int ret; + struct snd_soc_codec *codec = &wm8711->codec; + u16 reg; - if (addr != setup->i2c_address) - return -ENODEV; - - client_template.adapter = adap; - client_template.addr = addr; - - i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); - if (i2c == NULL) { - kfree(codec); - return -ENOMEM; + if (wm8711_codec) { + dev_err(codec->dev, "Another WM8711 is registered\n"); + return -EINVAL; } - i2c_set_clientdata(i2c, codec); + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); - codec->control_data = i2c; + codec->private_data = wm8711; + codec->name = "WM8711"; + codec->owner = THIS_MODULE; + codec->read = wm8711_read_reg_cache; + codec->write = wm8711_write; + codec->bias_level = SND_SOC_BIAS_OFF; + codec->set_bias_level = wm8711_set_bias_level; + codec->dai = &wm8711_dai; + codec->num_dai = 1; + codec->reg_cache_size = WM8711_CACHEREGNUM; + codec->reg_cache = &wm8711->reg_cache; - ret = i2c_attach_client(i2c); - if (ret < 0) { - pr_err("failed to attach codec at addr %x\n", addr); - goto err; - } + memcpy(codec->reg_cache, wm8711_reg, sizeof(wm8711_reg)); - ret = wm8711_init(socdev); + ret = wm8711_reset(codec); if (ret < 0) { - pr_err("failed to initialise WM8711\n"); - goto err; + dev_err(codec->dev, "Failed to issue reset\n"); + return ret; } - return ret; -err: - kfree(codec); - kfree(i2c); - return ret; -} + wm8711_dai.dev = codec->dev; -static int wm8711_i2c_detach(struct i2c_client *client) -{ - struct snd_soc_codec *codec = i2c_get_clientdata(client); + wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* Latch the update bits */ + reg = wm8711_read_reg_cache(codec, WM8711_LOUT1V); + wm8711_write(codec, WM8711_LOUT1V, reg | 0x0100); + reg = wm8711_read_reg_cache(codec, WM8711_ROUT1V); + wm8711_write(codec, WM8711_ROUT1V, reg | 0x0100); + + wm8711_codec = codec; + + ret = snd_soc_register_codec(codec); + if (ret != 0) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + return ret; + } + + ret = snd_soc_register_dai(&wm8711_dai); + if (ret != 0) { + dev_err(codec->dev, "Failed to register DAI: %d\n", ret); + snd_soc_unregister_codec(codec); + return ret; + } - i2c_detach_client(client); - kfree(codec->reg_cache); - kfree(client); return 0; } -static int wm8711_i2c_attach(struct i2c_adapter *adap) +static void wm8711_unregister(struct wm8711_priv *wm8711) { - return i2c_probe(adap, &addr_data, wm8711_codec_probe); + wm8711_set_bias_level(&wm8711->codec, SND_SOC_BIAS_OFF); + snd_soc_unregister_dai(&wm8711_dai); + snd_soc_unregister_codec(&wm8711->codec); + kfree(wm8711); + wm8711_codec = NULL; } -/* corgi i2c codec control layer */ -static struct i2c_driver wm8711_i2c_driver = { - .driver = { - .name = "WM8711 I2C Codec", - .owner = THIS_MODULE, - }, - .id = I2C_DRIVERID_WM8711, - .attach_adapter = wm8711_i2c_attach, - .detach_client = wm8711_i2c_detach, - .command = NULL, -}; - -static struct i2c_client client_template = { - .name = "WM8711", - .driver = &wm8711_i2c_driver, -}; -#endif - -static int wm8711_probe(struct platform_device *pdev) +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static __devinit int wm8711_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct wm8711_setup_data *setup; - struct snd_soc_codec *codec; struct wm8711_priv *wm8711; - int ret = 0; - - pr_info("WM8711 Audio Codec %s", WM8711_VERSION); - - setup = socdev->codec_data; - codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); - if (codec == NULL) - return -ENOMEM; + struct snd_soc_codec *codec; wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL); - if (wm8711 == NULL) { - kfree(codec); + if (wm8711 == NULL) return -ENOMEM; - } - codec->private_data = wm8711; - socdev->card->codec = codec; - mutex_init(&codec->mutex); - INIT_LIST_HEAD(&codec->dapm_widgets); - INIT_LIST_HEAD(&codec->dapm_paths); - - wm8711_socdev = socdev; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - if (setup->i2c_address) { - normal_i2c[0] = setup->i2c_address; - codec->hw_write = (hw_write_t)i2c_master_send; - ret = i2c_add_driver(&wm8711_i2c_driver); - if (ret != 0) - printk(KERN_ERR "can't add i2c driver"); - } -#else - /* Add other interfaces here */ -#endif - return ret; -} + codec = &wm8711->codec; + codec->hw_write = (hw_write_t)i2c_master_send; -/* power down chip */ -static int wm8711_remove(struct platform_device *pdev) -{ - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->card->codec; + i2c_set_clientdata(i2c, wm8711); + codec->control_data = i2c; - if (codec->control_data) - wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF); + codec->dev = &i2c->dev; - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_del_driver(&wm8711_i2c_driver); -#endif - kfree(codec->private_data); - kfree(codec); + return wm8711_register(wm8711); +} +static __devexit int wm8711_i2c_remove(struct i2c_client *client) +{ + struct wm8711_priv *wm8711 = i2c_get_clientdata(client); + wm8711_unregister(wm8711); return 0; } -struct snd_soc_codec_device soc_codec_dev_wm8711 = { - .probe = wm8711_probe, - .remove = wm8711_remove, - .suspend = wm8711_suspend, - .resume = wm8711_resume, +static const struct i2c_device_id wm8711_i2c_id[] = { + { "wm8711", 0 }, + { } }; -EXPORT_SYMBOL_GPL(soc_codec_dev_wm8711); +MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id); + +static struct i2c_driver wm8711_i2c_driver = { + .driver = { + .name = "WM8711 I2C Codec", + .owner = THIS_MODULE, + }, + .probe = wm8711_i2c_probe, + .remove = __devexit_p(wm8711_i2c_remove), + .id_table = wm8711_i2c_id, +}; +#endif static int __init wm8711_modinit(void) { - return snd_soc_register_dai(&wm8711_dai); + int ret; +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&wm8711_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n", + ret); + } +#endif + return 0; } module_init(wm8711_modinit); static void __exit wm8711_exit(void) { - snd_soc_unregister_dai(&wm8711_dai); +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&wm8711_i2c_driver); +#endif } module_exit(wm8711_exit); -- cgit v1.2.3 From d97d2e35b903b11dc6f7f8fcbe9a82fd8929e234 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 18 Aug 2009 21:12:30 +0100 Subject: ASoC: Factor out WM8711 cache I/O Signed-off-by: Mark Brown --- sound/soc/codecs/wm8711.c | 114 ++++++++++++++++------------------------------ 1 file changed, 38 insertions(+), 76 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 812283e2760..c7b1af89297 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -48,55 +48,7 @@ static const u16 wm8711_reg[WM8711_CACHEREGNUM] = { 0x009f, 0x000a, 0x0000, 0x0000 }; -/* - * read wm8711 register cache - */ -static inline unsigned int wm8711_read_reg_cache(struct snd_soc_codec *codec, - unsigned int reg) -{ - u16 *cache = codec->reg_cache; - if (reg == WM8711_RESET) - return 0; - if (reg >= WM8711_CACHEREGNUM) - return -1; - return cache[reg]; -} - -/* - * write wm8711 register cache - */ -static inline void wm8711_write_reg_cache(struct snd_soc_codec *codec, - u16 reg, unsigned int value) -{ - u16 *cache = codec->reg_cache; - if (reg >= WM8711_CACHEREGNUM) - return; - cache[reg] = value; -} - -/* - * write to the WM8711 register space - */ -static int wm8711_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 data[2]; - - /* data is - * D15..D9 WM8753 register offset - * D8...D0 register data - */ - data[0] = (reg << 1) | ((value >> 8) & 0x0001); - data[1] = value & 0x00ff; - - wm8711_write_reg_cache(codec, reg, value); - if (codec->hw_write(codec->control_data, data, 2) == 2) - return 0; - else - return -EIO; -} - -#define wm8711_reset(c) wm8711_write(c, WM8711_RESET, 0) +#define wm8711_reset(c) snd_soc_write(c, WM8711_RESET, 0) static const struct snd_kcontrol_new wm8711_snd_controls[] = { @@ -224,12 +176,12 @@ static int wm8711_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct wm8711_priv *wm8711 = codec->private_data; - u16 iface = wm8711_read_reg_cache(codec, WM8711_IFACE) & 0xfffc; + u16 iface = snd_soc_read(codec, WM8711_IFACE) & 0xfffc; int i = get_coeff(wm8711->sysclk, params_rate(params)); u16 srate = (coeff_div[i].sr << 2) | (coeff_div[i].bosr << 1) | coeff_div[i].usb; - wm8711_write(codec, WM8711_SRATE, srate); + snd_soc_write(codec, WM8711_SRATE, srate); /* bit size */ switch (params_format(params)) { @@ -243,7 +195,7 @@ static int wm8711_hw_params(struct snd_pcm_substream *substream, break; } - wm8711_write(codec, WM8711_IFACE, iface); + snd_soc_write(codec, WM8711_IFACE, iface); return 0; } @@ -253,7 +205,7 @@ static int wm8711_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; /* set active */ - wm8711_write(codec, WM8711_ACTIVE, 0x0001); + snd_soc_write(codec, WM8711_ACTIVE, 0x0001); return 0; } @@ -266,19 +218,19 @@ static void wm8711_shutdown(struct snd_pcm_substream *substream, /* deactivate */ if (!codec->active) { udelay(50); - wm8711_write(codec, WM8711_ACTIVE, 0x0); + snd_soc_write(codec, WM8711_ACTIVE, 0x0); } } static int wm8711_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - u16 mute_reg = wm8711_read_reg_cache(codec, WM8711_APDIGI) & 0xfff7; + u16 mute_reg = snd_soc_read(codec, WM8711_APDIGI) & 0xfff7; if (mute) - wm8711_write(codec, WM8711_APDIGI, mute_reg | 0x8); + snd_soc_write(codec, WM8711_APDIGI, mute_reg | 0x8); else - wm8711_write(codec, WM8711_APDIGI, mute_reg); + snd_soc_write(codec, WM8711_APDIGI, mute_reg); return 0; } @@ -356,7 +308,7 @@ static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai, } /* set iface */ - wm8711_write(codec, WM8711_IFACE, iface); + snd_soc_write(codec, WM8711_IFACE, iface); return 0; } @@ -364,20 +316,20 @@ static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai, static int wm8711_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - u16 reg = wm8711_read_reg_cache(codec, WM8711_PWR) & 0xff7f; + u16 reg = snd_soc_read(codec, WM8711_PWR) & 0xff7f; switch (level) { case SND_SOC_BIAS_ON: - wm8711_write(codec, WM8711_PWR, reg); + snd_soc_write(codec, WM8711_PWR, reg); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - wm8711_write(codec, WM8711_PWR, reg | 0x0040); + snd_soc_write(codec, WM8711_PWR, reg | 0x0040); break; case SND_SOC_BIAS_OFF: - wm8711_write(codec, WM8711_ACTIVE, 0x0); - wm8711_write(codec, WM8711_PWR, 0xffff); + snd_soc_write(codec, WM8711_ACTIVE, 0x0); + snd_soc_write(codec, WM8711_PWR, 0xffff); break; } codec->bias_level = level; @@ -419,7 +371,7 @@ static int wm8711_suspend(struct platform_device *pdev, pm_message_t state) struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->card->codec; - wm8711_write(codec, WM8711_ACTIVE, 0x0); + snd_soc_write(codec, WM8711_ACTIVE, 0x0); wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -501,7 +453,8 @@ struct snd_soc_codec_device soc_codec_dev_wm8711 = { }; EXPORT_SYMBOL_GPL(soc_codec_dev_wm8711); -static int wm8711_register(struct wm8711_priv *wm8711) +static int wm8711_register(struct wm8711_priv *wm8711, + enum snd_soc_control_type control) { int ret; struct snd_soc_codec *codec = &wm8711->codec; @@ -519,8 +472,6 @@ static int wm8711_register(struct wm8711_priv *wm8711) codec->private_data = wm8711; codec->name = "WM8711"; codec->owner = THIS_MODULE; - codec->read = wm8711_read_reg_cache; - codec->write = wm8711_write; codec->bias_level = SND_SOC_BIAS_OFF; codec->set_bias_level = wm8711_set_bias_level; codec->dai = &wm8711_dai; @@ -530,10 +481,16 @@ static int wm8711_register(struct wm8711_priv *wm8711) memcpy(codec->reg_cache, wm8711_reg, sizeof(wm8711_reg)); + ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + goto err; + } + ret = wm8711_reset(codec); if (ret < 0) { dev_err(codec->dev, "Failed to issue reset\n"); - return ret; + goto err; } wm8711_dai.dev = codec->dev; @@ -541,27 +498,32 @@ static int wm8711_register(struct wm8711_priv *wm8711) wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Latch the update bits */ - reg = wm8711_read_reg_cache(codec, WM8711_LOUT1V); - wm8711_write(codec, WM8711_LOUT1V, reg | 0x0100); - reg = wm8711_read_reg_cache(codec, WM8711_ROUT1V); - wm8711_write(codec, WM8711_ROUT1V, reg | 0x0100); + reg = snd_soc_read(codec, WM8711_LOUT1V); + snd_soc_write(codec, WM8711_LOUT1V, reg | 0x0100); + reg = snd_soc_read(codec, WM8711_ROUT1V); + snd_soc_write(codec, WM8711_ROUT1V, reg | 0x0100); wm8711_codec = codec; ret = snd_soc_register_codec(codec); if (ret != 0) { dev_err(codec->dev, "Failed to register codec: %d\n", ret); - return ret; + goto err; } ret = snd_soc_register_dai(&wm8711_dai); if (ret != 0) { dev_err(codec->dev, "Failed to register DAI: %d\n", ret); - snd_soc_unregister_codec(codec); - return ret; + goto err_codec; } return 0; + +err_codec: + snd_soc_unregister_codec(codec); +err: + kfree(wm8711); + return ret; } static void wm8711_unregister(struct wm8711_priv *wm8711) @@ -592,7 +554,7 @@ static __devinit int wm8711_i2c_probe(struct i2c_client *i2c, codec->dev = &i2c->dev; - return wm8711_register(wm8711); + return wm8711_register(wm8711, SND_SOC_I2C); } static __devexit int wm8711_i2c_remove(struct i2c_client *client) -- cgit v1.2.3 From 08aff8cd7a8568588d460c4bf8875a492d430314 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 18 Aug 2009 21:15:14 +0100 Subject: ASoC: Add SPI support to WM8711 Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 2 +- sound/soc/codecs/wm8711.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a5cfa78eb16..20ebf7437f9 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -35,7 +35,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8523 if I2C select SND_SOC_WM8580 if I2C - select SND_SOC_WM8711 if I2C + select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index c7b1af89297..1a7fca7d1ef 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -535,6 +535,62 @@ static void wm8711_unregister(struct wm8711_priv *wm8711) wm8711_codec = NULL; } +#if defined(CONFIG_SPI_MASTER) +static int __devinit wm8711_spi_probe(struct spi_device *spi) +{ + struct snd_soc_codec *codec; + struct wm8711_priv *wm8711; + + wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL); + if (wm8711 == NULL) + return -ENOMEM; + + codec = &wm8711->codec; + codec->control_data = spi; + codec->dev = &spi->dev; + + dev_set_drvdata(&spi->dev, wm8711); + + return wm8711_register(wm8711, SND_SOC_SPI); +} + +static int __devexit wm8711_spi_remove(struct spi_device *spi) +{ + struct wm8711_priv *wm8711 = dev_get_drvdata(&spi->dev); + + wm8711_unregister(wm8711); + + return 0; +} + +#ifdef CONFIG_PM +static int wm8711_spi_suspend(struct spi_device *spi, pm_message_t msg) +{ + return snd_soc_suspend_device(&spi->dev); +} + +static int wm8711_spi_resume(struct spi_device *spi) +{ + return snd_soc_resume_device(&spi->dev); +} +#else +#define wm8711_spi_suspend NULL +#define wm8711_spi_resume NULL +#endif + +static struct spi_driver wm8711_spi_driver = { + .driver = { + .name = "wm8711", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = wm8711_spi_probe, + .suspend = wm8711_spi_suspend, + .resume = wm8711_spi_resume, + .remove = __devexit_p(wm8711_spi_remove), +}; +#endif /* CONFIG_SPI_MASTER */ + #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) static __devinit int wm8711_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) @@ -590,6 +646,13 @@ static int __init wm8711_modinit(void) printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n", ret); } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8731_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n", + ret); + } #endif return 0; } @@ -600,6 +663,9 @@ static void __exit wm8711_exit(void) #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) i2c_del_driver(&wm8711_i2c_driver); #endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8731_spi_driver); +#endif } module_exit(wm8711_exit); -- cgit v1.2.3 From 431f7771774e8f37dde5acb3f7c4c5f6fa1109e3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 18 Aug 2009 21:17:34 +0100 Subject: ASoC: WM8711 minor cleanups Coding style changes only. Signed-off-by: Mark Brown --- sound/soc/codecs/wm8711.c | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 1a7fca7d1ef..f98c2bc32f9 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -59,22 +59,6 @@ SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V, }; -/* add non dapm controls */ -static int wm8711_add_controls(struct snd_soc_codec *codec) -{ - int err, i; - - for (i = 0; i < ARRAY_SIZE(wm8711_snd_controls); i++) { - err = snd_ctl_add(codec->card, - snd_soc_cnew(&wm8711_snd_controls[i], codec, - NULL)); - if (err < 0) - return err; - } - - return 0; -} - /* Output Mixer */ static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = { SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0), @@ -336,11 +320,7 @@ static int wm8711_set_bias_level(struct snd_soc_codec *codec, return 0; } -#define WM8711_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ - SNDRV_PCM_RATE_96000) +#define WM8711_RATES SNDRV_PCM_RATE_8000_96000 #define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) @@ -361,7 +341,8 @@ struct snd_soc_dai wm8711_dai = { .channels_min = 1, .channels_max = 2, .rates = WM8711_RATES, - .formats = WM8711_FORMATS,}, + .formats = WM8711_FORMATS, + }, .ops = &wm8711_ops, }; EXPORT_SYMBOL_GPL(wm8711_dai); -- cgit v1.2.3 From b5ab887e6dfa12c32ef39827da47d5d021320a3f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 18 Aug 2009 21:29:31 +0100 Subject: ASoC: Add TLV information to WM8711 Signed-off-by: Mark Brown --- sound/soc/codecs/wm8711.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index f98c2bc32f9..ae083eb92fb 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "wm8711.h" @@ -50,10 +51,12 @@ static const u16 wm8711_reg[WM8711_CACHEREGNUM] = { #define wm8711_reset(c) snd_soc_write(c, WM8711_RESET, 0) +static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); + static const struct snd_kcontrol_new wm8711_snd_controls[] = { -SOC_DOUBLE_R("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V, - 0, 127, 0), +SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V, + 0, 127, 0, out_tlv), SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V, 7, 1, 0), -- cgit v1.2.3 From 85488037bb9b533b064be66412dbe1dbcd2734d9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 5 Sep 2009 18:52:16 +0100 Subject: ASoC: Add source argument to PLL configuration More and more devices feature PLLs and FLLs with the ability to select between multiple input clocks. In order to better support these devices a new argument, source, has been added to the set_pll() configuration API. Using set_clkdiv() is often difficult due to the need to stop the PLL/FLL before any reconfiguration can be done. Signed-off-by: Mark Brown --- sound/soc/codecs/wm8350.c | 2 +- sound/soc/codecs/wm8400.c | 3 ++- sound/soc/codecs/wm8510.c | 4 ++-- sound/soc/codecs/wm8580.c | 4 ++-- sound/soc/codecs/wm8753.c | 4 ++-- sound/soc/codecs/wm8900.c | 4 ++-- sound/soc/codecs/wm8940.c | 4 ++-- sound/soc/codecs/wm8960.c | 4 ++-- sound/soc/codecs/wm8974.c | 4 ++-- sound/soc/codecs/wm8990.c | 4 ++-- sound/soc/codecs/wm8993.c | 2 +- sound/soc/codecs/wm9713.c | 4 ++-- 12 files changed, 22 insertions(+), 21 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 71c9c4bb263..0ebd99b7493 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1101,7 +1101,7 @@ static inline int fll_factors(struct _fll_div *fll_div, unsigned int input, } static int wm8350_set_fll(struct snd_soc_dai *codec_dai, - int pll_id, unsigned int freq_in, + int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index b9ef4d91522..9cb8e50f0fb 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -1011,7 +1011,8 @@ static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors, } static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, - unsigned int freq_in, unsigned int freq_out) + int source, unsigned int freq_in, + unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; struct wm8400_priv *wm8400 = codec->private_data; diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 060d5d06ba9..5702435af81 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -271,8 +271,8 @@ static void pll_factors(unsigned int target, unsigned int source) pll_div.k = K; } -static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai, - int pll_id, unsigned int freq_in, unsigned int freq_out) +static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; u16 reg; diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 6bded8c7815..3be5c0b2552 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -407,8 +407,8 @@ static int pll_factors(struct _pll_div *pll_div, unsigned int target, return 0; } -static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, - int pll_id, unsigned int freq_in, unsigned int freq_out) +static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) { int offset; struct snd_soc_codec *codec = codec_dai->codec; diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index d80d414cfbb..f60f3a02d1f 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -723,8 +723,8 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target, pll_div->k = K; } -static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, - int pll_id, unsigned int freq_in, unsigned int freq_out) +static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) { u16 reg, enable; int offset; diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 5e9c855c003..882604ef768 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -814,8 +814,8 @@ reenable: return 0; } -static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai, - int pll_id, unsigned int freq_in, unsigned int freq_out) +static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) { return wm8900_set_fll(codec_dai->codec, pll_id, freq_in, freq_out); } diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index da97aae475a..914d788a2b7 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -536,8 +536,8 @@ static void pll_factors(unsigned int target, unsigned int source) } /* Untested at the moment */ -static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, - int pll_id, unsigned int freq_in, unsigned int freq_out) +static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; u16 reg; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index f59703be61c..416fb3c1701 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -540,8 +540,8 @@ static int pll_factors(unsigned int source, unsigned int target, return 0; } -static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, - int pll_id, unsigned int freq_in, unsigned int freq_out) +static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; u16 reg; diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index d8a013ab317..fa4d85bd048 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -329,8 +329,8 @@ static void pll_factors(unsigned int target, unsigned int source) pll_div.k = K; } -static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, - int pll_id, unsigned int freq_in, unsigned int freq_out) +static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; u16 reg; diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 2d702db4131..f657e9a5fe2 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -972,8 +972,8 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target, pll_div->k = K; } -static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai, - int pll_id, unsigned int freq_in, unsigned int freq_out) +static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) { u16 reg; struct snd_soc_codec *codec = codec_dai->codec; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index d9987999e92..6b32a285260 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -422,7 +422,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, return 0; } -static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, +static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source, unsigned int Fref, unsigned int Fout) { struct snd_soc_codec *codec = dai->codec; diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index abed37acf78..ca3d449ed89 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -800,8 +800,8 @@ static int wm9713_set_pll(struct snd_soc_codec *codec, return 0; } -static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai, - int pll_id, unsigned int freq_in, unsigned int freq_out) +static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; return wm9713_set_pll(codec, pll_id, freq_in, freq_out); -- cgit v1.2.3 From 2312fd8f6b252b7d3c1d74b20c75b7bff98bab65 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Thu, 10 Sep 2009 00:12:43 +0900 Subject: ASoC: AK4671: add ak4671 codec driver The AK4671 is a stereo CODEC with a built-in Microphone-Amplifier, Receiver-Amplifier and Headphone-Amplifier. The datasheet for the ak4671 can find at the following url: http://www.asahi-kasei.co.jp/akm/en/product/ak4671/ak4671_f01e.pdf Signed-off-by: Joonyoung Shim Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/ak4671.c | 825 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/ak4671.h | 156 +++++++++ 4 files changed, 987 insertions(+) create mode 100644 sound/soc/codecs/ak4671.c create mode 100644 sound/soc/codecs/ak4671.h (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0edca93af3b..a2bb659ec18 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -19,6 +19,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C select SND_SOC_AK4642 if I2C + select SND_SOC_AK4671 if I2C select SND_SOC_CS4270 if I2C select SND_SOC_MAX9877 if I2C select SND_SOC_PCM3008 @@ -96,6 +97,9 @@ config SND_SOC_AK4535 config SND_SOC_AK4642 tristate +config SND_SOC_AK4671 + tristate + # Cirrus Logic CS4270 Codec config SND_SOC_CS4270 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index fb4af28486b..13f7b4f2a15 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -6,6 +6,7 @@ snd-soc-ad73311-objs := ad73311.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o snd-soc-ak4642-objs := ak4642.o +snd-soc-ak4671-objs := ak4671.o snd-soc-cs4270-objs := cs4270.o snd-soc-cx20442-objs := cx20442.o snd-soc-l3-objs := l3.o @@ -56,6 +57,7 @@ obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o +obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c new file mode 100644 index 00000000000..b61214d1c5d --- /dev/null +++ b/sound/soc/codecs/ak4671.c @@ -0,0 +1,825 @@ +/* + * ak4671.c -- audio driver for AK4671 + * + * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ak4671.h" + +static struct snd_soc_codec *ak4671_codec; + +/* codec private data */ +struct ak4671_priv { + struct snd_soc_codec codec; + u8 reg_cache[AK4671_CACHEREGNUM]; +}; + +/* ak4671 register cache & default register settings */ +static const u8 ak4671_reg[AK4671_CACHEREGNUM] = { + 0x00, /* AK4671_AD_DA_POWER_MANAGEMENT (0x00) */ + 0xf6, /* AK4671_PLL_MODE_SELECT0 (0x01) */ + 0x00, /* AK4671_PLL_MODE_SELECT1 (0x02) */ + 0x02, /* AK4671_FORMAT_SELECT (0x03) */ + 0x00, /* AK4671_MIC_SIGNAL_SELECT (0x04) */ + 0x55, /* AK4671_MIC_AMP_GAIN (0x05) */ + 0x00, /* AK4671_MIXING_POWER_MANAGEMENT0 (0x06) */ + 0x00, /* AK4671_MIXING_POWER_MANAGEMENT1 (0x07) */ + 0xb5, /* AK4671_OUTPUT_VOLUME_CONTROL (0x08) */ + 0x00, /* AK4671_LOUT1_SIGNAL_SELECT (0x09) */ + 0x00, /* AK4671_ROUT1_SIGNAL_SELECT (0x0a) */ + 0x00, /* AK4671_LOUT2_SIGNAL_SELECT (0x0b) */ + 0x00, /* AK4671_ROUT2_SIGNAL_SELECT (0x0c) */ + 0x00, /* AK4671_LOUT3_SIGNAL_SELECT (0x0d) */ + 0x00, /* AK4671_ROUT3_SIGNAL_SELECT (0x0e) */ + 0x00, /* AK4671_LOUT1_POWER_MANAGERMENT (0x0f) */ + 0x00, /* AK4671_LOUT2_POWER_MANAGERMENT (0x10) */ + 0x80, /* AK4671_LOUT3_POWER_MANAGERMENT (0x11) */ + 0x91, /* AK4671_LCH_INPUT_VOLUME_CONTROL (0x12) */ + 0x91, /* AK4671_RCH_INPUT_VOLUME_CONTROL (0x13) */ + 0xe1, /* AK4671_ALC_REFERENCE_SELECT (0x14) */ + 0x00, /* AK4671_DIGITAL_MIXING_CONTROL (0x15) */ + 0x00, /* AK4671_ALC_TIMER_SELECT (0x16) */ + 0x00, /* AK4671_ALC_MODE_CONTROL (0x17) */ + 0x02, /* AK4671_MODE_CONTROL1 (0x18) */ + 0x01, /* AK4671_MODE_CONTROL2 (0x19) */ + 0x18, /* AK4671_LCH_OUTPUT_VOLUME_CONTROL (0x1a) */ + 0x18, /* AK4671_RCH_OUTPUT_VOLUME_CONTROL (0x1b) */ + 0x00, /* AK4671_SIDETONE_A_CONTROL (0x1c) */ + 0x02, /* AK4671_DIGITAL_FILTER_SELECT (0x1d) */ + 0x00, /* AK4671_FIL3_COEFFICIENT0 (0x1e) */ + 0x00, /* AK4671_FIL3_COEFFICIENT1 (0x1f) */ + 0x00, /* AK4671_FIL3_COEFFICIENT2 (0x20) */ + 0x00, /* AK4671_FIL3_COEFFICIENT3 (0x21) */ + 0x00, /* AK4671_EQ_COEFFICIENT0 (0x22) */ + 0x00, /* AK4671_EQ_COEFFICIENT1 (0x23) */ + 0x00, /* AK4671_EQ_COEFFICIENT2 (0x24) */ + 0x00, /* AK4671_EQ_COEFFICIENT3 (0x25) */ + 0x00, /* AK4671_EQ_COEFFICIENT4 (0x26) */ + 0x00, /* AK4671_EQ_COEFFICIENT5 (0x27) */ + 0xa9, /* AK4671_FIL1_COEFFICIENT0 (0x28) */ + 0x1f, /* AK4671_FIL1_COEFFICIENT1 (0x29) */ + 0xad, /* AK4671_FIL1_COEFFICIENT2 (0x2a) */ + 0x20, /* AK4671_FIL1_COEFFICIENT3 (0x2b) */ + 0x00, /* AK4671_FIL2_COEFFICIENT0 (0x2c) */ + 0x00, /* AK4671_FIL2_COEFFICIENT1 (0x2d) */ + 0x00, /* AK4671_FIL2_COEFFICIENT2 (0x2e) */ + 0x00, /* AK4671_FIL2_COEFFICIENT3 (0x2f) */ + 0x00, /* AK4671_DIGITAL_FILTER_SELECT2 (0x30) */ + 0x00, /* this register not used */ + 0x00, /* AK4671_E1_COEFFICIENT0 (0x32) */ + 0x00, /* AK4671_E1_COEFFICIENT1 (0x33) */ + 0x00, /* AK4671_E1_COEFFICIENT2 (0x34) */ + 0x00, /* AK4671_E1_COEFFICIENT3 (0x35) */ + 0x00, /* AK4671_E1_COEFFICIENT4 (0x36) */ + 0x00, /* AK4671_E1_COEFFICIENT5 (0x37) */ + 0x00, /* AK4671_E2_COEFFICIENT0 (0x38) */ + 0x00, /* AK4671_E2_COEFFICIENT1 (0x39) */ + 0x00, /* AK4671_E2_COEFFICIENT2 (0x3a) */ + 0x00, /* AK4671_E2_COEFFICIENT3 (0x3b) */ + 0x00, /* AK4671_E2_COEFFICIENT4 (0x3c) */ + 0x00, /* AK4671_E2_COEFFICIENT5 (0x3d) */ + 0x00, /* AK4671_E3_COEFFICIENT0 (0x3e) */ + 0x00, /* AK4671_E3_COEFFICIENT1 (0x3f) */ + 0x00, /* AK4671_E3_COEFFICIENT2 (0x40) */ + 0x00, /* AK4671_E3_COEFFICIENT3 (0x41) */ + 0x00, /* AK4671_E3_COEFFICIENT4 (0x42) */ + 0x00, /* AK4671_E3_COEFFICIENT5 (0x43) */ + 0x00, /* AK4671_E4_COEFFICIENT0 (0x44) */ + 0x00, /* AK4671_E4_COEFFICIENT1 (0x45) */ + 0x00, /* AK4671_E4_COEFFICIENT2 (0x46) */ + 0x00, /* AK4671_E4_COEFFICIENT3 (0x47) */ + 0x00, /* AK4671_E4_COEFFICIENT4 (0x48) */ + 0x00, /* AK4671_E4_COEFFICIENT5 (0x49) */ + 0x00, /* AK4671_E5_COEFFICIENT0 (0x4a) */ + 0x00, /* AK4671_E5_COEFFICIENT1 (0x4b) */ + 0x00, /* AK4671_E5_COEFFICIENT2 (0x4c) */ + 0x00, /* AK4671_E5_COEFFICIENT3 (0x4d) */ + 0x00, /* AK4671_E5_COEFFICIENT4 (0x4e) */ + 0x00, /* AK4671_E5_COEFFICIENT5 (0x4f) */ + 0x88, /* AK4671_EQ_CONTROL_250HZ_100HZ (0x50) */ + 0x88, /* AK4671_EQ_CONTROL_3500HZ_1KHZ (0x51) */ + 0x08, /* AK4671_EQ_CONTRO_10KHZ (0x52) */ + 0x00, /* AK4671_PCM_IF_CONTROL0 (0x53) */ + 0x00, /* AK4671_PCM_IF_CONTROL1 (0x54) */ + 0x00, /* AK4671_PCM_IF_CONTROL2 (0x55) */ + 0x18, /* AK4671_DIGITAL_VOLUME_B_CONTROL (0x56) */ + 0x18, /* AK4671_DIGITAL_VOLUME_C_CONTROL (0x57) */ + 0x00, /* AK4671_SIDETONE_VOLUME_CONTROL (0x58) */ + 0x00, /* AK4671_DIGITAL_MIXING_CONTROL2 (0x59) */ + 0x00, /* AK4671_SAR_ADC_CONTROL (0x5a) */ +}; + +/* + * LOUT1/ROUT1 output volume control: + * from -24 to 6 dB in 6 dB steps (mute instead of -30 dB) + */ +static DECLARE_TLV_DB_SCALE(out1_tlv, -3000, 600, 1); + +/* + * LOUT2/ROUT2 output volume control: + * from -33 to 6 dB in 3 dB steps (mute instead of -33 dB) + */ +static DECLARE_TLV_DB_SCALE(out2_tlv, -3300, 300, 1); + +/* + * LOUT3/ROUT3 output volume control: + * from -6 to 3 dB in 3 dB steps + */ +static DECLARE_TLV_DB_SCALE(out3_tlv, -600, 300, 0); + +/* + * Mic amp gain control: + * from -15 to 30 dB in 3 dB steps + * REVISIT: The actual min value(0x01) is -12 dB and the reg value 0x00 is not + * available + */ +static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -1500, 300, 0); + +static const struct snd_kcontrol_new ak4671_snd_controls[] = { + /* Common playback gain controls */ + SOC_SINGLE_TLV("Line Output1 Playback Volume", + AK4671_OUTPUT_VOLUME_CONTROL, 0, 0x6, 0, out1_tlv), + SOC_SINGLE_TLV("Headphone Output2 Playback Volume", + AK4671_OUTPUT_VOLUME_CONTROL, 4, 0xd, 0, out2_tlv), + SOC_SINGLE_TLV("Line Output3 Playback Volume", + AK4671_LOUT3_POWER_MANAGERMENT, 6, 0x3, 0, out3_tlv), + + /* Common capture gain controls */ + SOC_DOUBLE_TLV("Mic Amp Capture Volume", + AK4671_MIC_AMP_GAIN, 0, 4, 0xf, 0, mic_amp_tlv), +}; + +/* event handlers */ +static int ak4671_out2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + u8 reg; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT); + reg |= AK4671_MUTEN; + snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg); + break; + case SND_SOC_DAPM_PRE_PMD: + reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT); + reg &= ~AK4671_MUTEN; + snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg); + break; + } + + return 0; +} + +/* Output Mixers */ +static const struct snd_kcontrol_new ak4671_lout1_mixer_controls[] = { + SOC_DAPM_SINGLE("DACL", AK4671_LOUT1_SIGNAL_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("LINL1", AK4671_LOUT1_SIGNAL_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("LINL2", AK4671_LOUT1_SIGNAL_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("LINL3", AK4671_LOUT1_SIGNAL_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("LINL4", AK4671_LOUT1_SIGNAL_SELECT, 4, 1, 0), + SOC_DAPM_SINGLE("LOOPL", AK4671_LOUT1_SIGNAL_SELECT, 5, 1, 0), +}; + +static const struct snd_kcontrol_new ak4671_rout1_mixer_controls[] = { + SOC_DAPM_SINGLE("DACR", AK4671_ROUT1_SIGNAL_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("RINR1", AK4671_ROUT1_SIGNAL_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("RINR2", AK4671_ROUT1_SIGNAL_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("RINR3", AK4671_ROUT1_SIGNAL_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("RINR4", AK4671_ROUT1_SIGNAL_SELECT, 4, 1, 0), + SOC_DAPM_SINGLE("LOOPR", AK4671_ROUT1_SIGNAL_SELECT, 5, 1, 0), +}; + +static const struct snd_kcontrol_new ak4671_lout2_mixer_controls[] = { + SOC_DAPM_SINGLE("DACHL", AK4671_LOUT2_SIGNAL_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("LINH1", AK4671_LOUT2_SIGNAL_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("LINH2", AK4671_LOUT2_SIGNAL_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("LINH3", AK4671_LOUT2_SIGNAL_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("LINH4", AK4671_LOUT2_SIGNAL_SELECT, 4, 1, 0), + SOC_DAPM_SINGLE("LOOPHL", AK4671_LOUT2_SIGNAL_SELECT, 5, 1, 0), +}; + +static const struct snd_kcontrol_new ak4671_rout2_mixer_controls[] = { + SOC_DAPM_SINGLE("DACHR", AK4671_ROUT2_SIGNAL_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("RINH1", AK4671_ROUT2_SIGNAL_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("RINH2", AK4671_ROUT2_SIGNAL_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("RINH3", AK4671_ROUT2_SIGNAL_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("RINH4", AK4671_ROUT2_SIGNAL_SELECT, 4, 1, 0), + SOC_DAPM_SINGLE("LOOPHR", AK4671_ROUT2_SIGNAL_SELECT, 5, 1, 0), +}; + +static const struct snd_kcontrol_new ak4671_lout3_mixer_controls[] = { + SOC_DAPM_SINGLE("DACSL", AK4671_LOUT3_SIGNAL_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("LINS1", AK4671_LOUT3_SIGNAL_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("LINS2", AK4671_LOUT3_SIGNAL_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("LINS3", AK4671_LOUT3_SIGNAL_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("LINS4", AK4671_LOUT3_SIGNAL_SELECT, 4, 1, 0), + SOC_DAPM_SINGLE("LOOPSL", AK4671_LOUT3_SIGNAL_SELECT, 5, 1, 0), +}; + +static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = { + SOC_DAPM_SINGLE("DACSR", AK4671_ROUT3_SIGNAL_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("RINS1", AK4671_ROUT3_SIGNAL_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("RINS2", AK4671_ROUT3_SIGNAL_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("RINS3", AK4671_ROUT3_SIGNAL_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("RINS4", AK4671_ROUT3_SIGNAL_SELECT, 4, 1, 0), + SOC_DAPM_SINGLE("LOOPSR", AK4671_ROUT3_SIGNAL_SELECT, 5, 1, 0), +}; + +/* Input MUXs */ +static const char *ak4671_lin_mux_texts[] = + {"LIN1", "LIN2", "LIN3", "LIN4"}; +static const struct soc_enum ak4671_lin_mux_enum = + SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 0, + ARRAY_SIZE(ak4671_lin_mux_texts), + ak4671_lin_mux_texts); +static const struct snd_kcontrol_new ak4671_lin_mux_control = + SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum); + +static const char *ak4671_rin_mux_texts[] = + {"RIN1", "RIN2", "RIN3", "RIN4"}; +static const struct soc_enum ak4671_rin_mux_enum = + SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 2, + ARRAY_SIZE(ak4671_rin_mux_texts), + ak4671_rin_mux_texts); +static const struct snd_kcontrol_new ak4671_rin_mux_control = + SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum); + +static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = { + /* Inputs */ + SND_SOC_DAPM_INPUT("LIN1"), + SND_SOC_DAPM_INPUT("RIN1"), + SND_SOC_DAPM_INPUT("LIN2"), + SND_SOC_DAPM_INPUT("RIN2"), + SND_SOC_DAPM_INPUT("LIN3"), + SND_SOC_DAPM_INPUT("RIN3"), + SND_SOC_DAPM_INPUT("LIN4"), + SND_SOC_DAPM_INPUT("RIN4"), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + SND_SOC_DAPM_OUTPUT("LOUT3"), + SND_SOC_DAPM_OUTPUT("ROUT3"), + + /* DAC */ + SND_SOC_DAPM_DAC("DAC Left", "Left HiFi Playback", + AK4671_AD_DA_POWER_MANAGEMENT, 6, 0), + SND_SOC_DAPM_DAC("DAC Right", "Right HiFi Playback", + AK4671_AD_DA_POWER_MANAGEMENT, 7, 0), + + /* ADC */ + SND_SOC_DAPM_ADC("ADC Left", "Left HiFi Capture", + AK4671_AD_DA_POWER_MANAGEMENT, 4, 0), + SND_SOC_DAPM_ADC("ADC Right", "Right HiFi Capture", + AK4671_AD_DA_POWER_MANAGEMENT, 5, 0), + + /* PGA */ + SND_SOC_DAPM_PGA("LOUT2 Mix Amp", + AK4671_LOUT2_POWER_MANAGERMENT, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("ROUT2 Mix Amp", + AK4671_LOUT2_POWER_MANAGERMENT, 6, 0, NULL, 0), + + SND_SOC_DAPM_PGA("LIN1 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("RIN1 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("LIN2 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("RIN2 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("LIN3 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("RIN3 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("LIN4 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("RIN4 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 7, 0, NULL, 0), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("LOUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 0, 0, + &ak4671_lout1_mixer_controls[0], + ARRAY_SIZE(ak4671_lout1_mixer_controls)), + SND_SOC_DAPM_MIXER("ROUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 1, 0, + &ak4671_rout1_mixer_controls[0], + ARRAY_SIZE(ak4671_rout1_mixer_controls)), + SND_SOC_DAPM_MIXER_E("LOUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT, + 0, 0, &ak4671_lout2_mixer_controls[0], + ARRAY_SIZE(ak4671_lout2_mixer_controls), + ak4671_out2_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER_E("ROUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT, + 1, 0, &ak4671_rout2_mixer_controls[0], + ARRAY_SIZE(ak4671_rout2_mixer_controls), + ak4671_out2_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER("LOUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 0, 0, + &ak4671_lout3_mixer_controls[0], + ARRAY_SIZE(ak4671_lout3_mixer_controls)), + SND_SOC_DAPM_MIXER("ROUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 1, 0, + &ak4671_rout3_mixer_controls[0], + ARRAY_SIZE(ak4671_rout3_mixer_controls)), + + /* Input MUXs */ + SND_SOC_DAPM_MUX("LIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 2, 0, + &ak4671_lin_mux_control), + SND_SOC_DAPM_MUX("RIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 3, 0, + &ak4671_rin_mux_control), + + /* Mic Power */ + SND_SOC_DAPM_MICBIAS("Mic Bias", AK4671_AD_DA_POWER_MANAGEMENT, 1, 0), + + /* Supply */ + SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route intercon[] = { + {"DAC Left", "NULL", "PMPLL"}, + {"DAC Right", "NULL", "PMPLL"}, + {"ADC Left", "NULL", "PMPLL"}, + {"ADC Right", "NULL", "PMPLL"}, + + /* Outputs */ + {"LOUT1", "NULL", "LOUT1 Mixer"}, + {"ROUT1", "NULL", "ROUT1 Mixer"}, + {"LOUT2", "NULL", "LOUT2 Mix Amp"}, + {"ROUT2", "NULL", "ROUT2 Mix Amp"}, + {"LOUT3", "NULL", "LOUT3 Mixer"}, + {"ROUT3", "NULL", "ROUT3 Mixer"}, + + {"LOUT1 Mixer", "DACL", "DAC Left"}, + {"ROUT1 Mixer", "DACR", "DAC Right"}, + {"LOUT2 Mixer", "DACHL", "DAC Left"}, + {"ROUT2 Mixer", "DACHR", "DAC Right"}, + {"LOUT2 Mix Amp", "NULL", "LOUT2 Mixer"}, + {"ROUT2 Mix Amp", "NULL", "ROUT2 Mixer"}, + {"LOUT3 Mixer", "DACSL", "DAC Left"}, + {"ROUT3 Mixer", "DACSR", "DAC Right"}, + + /* Inputs */ + {"LIN MUX", "LIN1", "LIN1"}, + {"LIN MUX", "LIN2", "LIN2"}, + {"LIN MUX", "LIN3", "LIN3"}, + {"LIN MUX", "LIN4", "LIN4"}, + + {"RIN MUX", "RIN1", "RIN1"}, + {"RIN MUX", "RIN2", "RIN2"}, + {"RIN MUX", "RIN3", "RIN3"}, + {"RIN MUX", "RIN4", "RIN4"}, + + {"LIN1", NULL, "Mic Bias"}, + {"RIN1", NULL, "Mic Bias"}, + {"LIN2", NULL, "Mic Bias"}, + {"RIN2", NULL, "Mic Bias"}, + + {"ADC Left", "NULL", "LIN MUX"}, + {"ADC Right", "NULL", "RIN MUX"}, + + /* Analog Loops */ + {"LIN1 Mixing Circuit", "NULL", "LIN1"}, + {"RIN1 Mixing Circuit", "NULL", "RIN1"}, + {"LIN2 Mixing Circuit", "NULL", "LIN2"}, + {"RIN2 Mixing Circuit", "NULL", "RIN2"}, + {"LIN3 Mixing Circuit", "NULL", "LIN3"}, + {"RIN3 Mixing Circuit", "NULL", "RIN3"}, + {"LIN4 Mixing Circuit", "NULL", "LIN4"}, + {"RIN4 Mixing Circuit", "NULL", "RIN4"}, + + {"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"}, + {"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"}, + {"LOUT2 Mixer", "LINH1", "LIN1 Mixing Circuit"}, + {"ROUT2 Mixer", "RINH1", "RIN1 Mixing Circuit"}, + {"LOUT3 Mixer", "LINS1", "LIN1 Mixing Circuit"}, + {"ROUT3 Mixer", "RINS1", "RIN1 Mixing Circuit"}, + + {"LOUT1 Mixer", "LINL2", "LIN2 Mixing Circuit"}, + {"ROUT1 Mixer", "RINR2", "RIN2 Mixing Circuit"}, + {"LOUT2 Mixer", "LINH2", "LIN2 Mixing Circuit"}, + {"ROUT2 Mixer", "RINH2", "RIN2 Mixing Circuit"}, + {"LOUT3 Mixer", "LINS2", "LIN2 Mixing Circuit"}, + {"ROUT3 Mixer", "RINS2", "RIN2 Mixing Circuit"}, + + {"LOUT1 Mixer", "LINL3", "LIN3 Mixing Circuit"}, + {"ROUT1 Mixer", "RINR3", "RIN3 Mixing Circuit"}, + {"LOUT2 Mixer", "LINH3", "LIN3 Mixing Circuit"}, + {"ROUT2 Mixer", "RINH3", "RIN3 Mixing Circuit"}, + {"LOUT3 Mixer", "LINS3", "LIN3 Mixing Circuit"}, + {"ROUT3 Mixer", "RINS3", "RIN3 Mixing Circuit"}, + + {"LOUT1 Mixer", "LINL4", "LIN4 Mixing Circuit"}, + {"ROUT1 Mixer", "RINR4", "RIN4 Mixing Circuit"}, + {"LOUT2 Mixer", "LINH4", "LIN4 Mixing Circuit"}, + {"ROUT2 Mixer", "RINH4", "RIN4 Mixing Circuit"}, + {"LOUT3 Mixer", "LINS4", "LIN4 Mixing Circuit"}, + {"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"}, +}; + +static int ak4671_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, ak4671_dapm_widgets, + ARRAY_SIZE(ak4671_dapm_widgets)); + + snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +static int ak4671_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; + u8 fs; + + fs = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0); + fs &= ~AK4671_FS; + + switch (params_rate(params)) { + case 8000: + fs |= AK4671_FS_8KHZ; + break; + case 12000: + fs |= AK4671_FS_12KHZ; + break; + case 16000: + fs |= AK4671_FS_16KHZ; + break; + case 24000: + fs |= AK4671_FS_24KHZ; + break; + case 11025: + fs |= AK4671_FS_11_025KHZ; + break; + case 22050: + fs |= AK4671_FS_22_05KHZ; + break; + case 32000: + fs |= AK4671_FS_32KHZ; + break; + case 44100: + fs |= AK4671_FS_44_1KHZ; + break; + case 48000: + fs |= AK4671_FS_48KHZ; + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, fs); + + return 0; +} + +static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + u8 pll; + + pll = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0); + pll &= ~AK4671_PLL; + + switch (freq) { + case 11289600: + pll |= AK4671_PLL_11_2896MHZ; + break; + case 12000000: + pll |= AK4671_PLL_12MHZ; + break; + case 12288000: + pll |= AK4671_PLL_12_288MHZ; + break; + case 13000000: + pll |= AK4671_PLL_13MHZ; + break; + case 13500000: + pll |= AK4671_PLL_13_5MHZ; + break; + case 19200000: + pll |= AK4671_PLL_19_2MHZ; + break; + case 24000000: + pll |= AK4671_PLL_24MHZ; + break; + case 26000000: + pll |= AK4671_PLL_26MHZ; + break; + case 27000000: + pll |= AK4671_PLL_27MHZ; + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, pll); + + return 0; +} + +static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + u8 mode; + u8 format; + + /* set master/slave audio interface */ + mode = snd_soc_read(codec, AK4671_PLL_MODE_SELECT1); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + mode |= AK4671_M_S; + break; + case SND_SOC_DAIFMT_CBM_CFS: + mode &= ~(AK4671_M_S); + break; + default: + return -EINVAL; + } + + /* interface format */ + format = snd_soc_read(codec, AK4671_FORMAT_SELECT); + format &= ~AK4671_DIF; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format |= AK4671_DIF_I2S_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + format |= AK4671_DIF_MSB_MODE; + break; + case SND_SOC_DAIFMT_DSP_A: + format |= AK4671_DIF_DSP_MODE; + format |= AK4671_BCKP; + format |= AK4671_MSBS; + break; + default: + return -EINVAL; + } + + /* set mode and format */ + snd_soc_write(codec, AK4671_PLL_MODE_SELECT1, mode); + snd_soc_write(codec, AK4671_FORMAT_SELECT, format); + + return 0; +} + +static int ak4671_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u8 reg; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + case SND_SOC_BIAS_STANDBY: + reg = snd_soc_read(codec, AK4671_AD_DA_POWER_MANAGEMENT); + snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, + reg | AK4671_PMVCM); + break; + case SND_SOC_BIAS_OFF: + snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00); + break; + } + codec->bias_level = level; + return 0; +} + +#define AK4671_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000) + +#define AK4671_FORMATS SNDRV_PCM_FMTBIT_S16_LE + +static struct snd_soc_dai_ops ak4671_dai_ops = { + .hw_params = ak4671_hw_params, + .set_sysclk = ak4671_set_dai_sysclk, + .set_fmt = ak4671_set_dai_fmt, +}; + +struct snd_soc_dai ak4671_dai = { + .name = "AK4671", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AK4671_RATES, + .formats = AK4671_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AK4671_RATES, + .formats = AK4671_FORMATS,}, + .ops = &ak4671_dai_ops, +}; +EXPORT_SYMBOL_GPL(ak4671_dai); + +static int ak4671_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + if (ak4671_codec == NULL) { + dev_err(&pdev->dev, "Codec device not registered\n"); + return -ENODEV; + } + + socdev->card->codec = ak4671_codec; + codec = ak4671_codec; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + dev_err(codec->dev, "failed to create pcms: %d\n", ret); + goto pcm_err; + } + + snd_soc_add_controls(codec, ak4671_snd_controls, + ARRAY_SIZE(ak4671_snd_controls)); + ak4671_add_widgets(codec); + + ret = snd_soc_init_card(socdev); + if (ret < 0) { + dev_err(codec->dev, "failed to register card: %d\n", ret); + goto card_err; + } + + ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return ret; + +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + return ret; +} + +static int ak4671_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_ak4671 = { + .probe = ak4671_probe, + .remove = ak4671_remove, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_ak4671); + +static int ak4671_register(struct ak4671_priv *ak4671, + enum snd_soc_control_type control) +{ + int ret; + struct snd_soc_codec *codec = &ak4671->codec; + + if (ak4671_codec) { + dev_err(codec->dev, "Another AK4671 is registered\n"); + ret = -EINVAL; + goto err; + } + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + codec->private_data = ak4671; + codec->name = "AK4671"; + codec->owner = THIS_MODULE; + codec->bias_level = SND_SOC_BIAS_OFF; + codec->set_bias_level = ak4671_set_bias_level; + codec->dai = &ak4671_dai; + codec->num_dai = 1; + codec->reg_cache_size = AK4671_CACHEREGNUM; + codec->reg_cache = &ak4671->reg_cache; + + memcpy(codec->reg_cache, ak4671_reg, sizeof(ak4671_reg)); + + ret = snd_soc_codec_set_cache_io(codec, 8, 8, control); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + goto err; + } + + ak4671_dai.dev = codec->dev; + ak4671_codec = codec; + + ret = snd_soc_register_codec(codec); + if (ret != 0) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + goto err; + } + + ret = snd_soc_register_dai(&ak4671_dai); + if (ret != 0) { + dev_err(codec->dev, "Failed to register DAI: %d\n", ret); + goto err_codec; + } + + return 0; + +err_codec: + snd_soc_unregister_codec(codec); +err: + kfree(ak4671); + return ret; +} + +static void ak4671_unregister(struct ak4671_priv *ak4671) +{ + ak4671_set_bias_level(&ak4671->codec, SND_SOC_BIAS_OFF); + snd_soc_unregister_dai(&ak4671_dai); + snd_soc_unregister_codec(&ak4671->codec); + kfree(ak4671); + ak4671_codec = NULL; +} + +static int __devinit ak4671_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ak4671_priv *ak4671; + struct snd_soc_codec *codec; + + ak4671 = kzalloc(sizeof(struct ak4671_priv), GFP_KERNEL); + if (ak4671 == NULL) + return -ENOMEM; + + codec = &ak4671->codec; + codec->hw_write = (hw_write_t)i2c_master_send; + + i2c_set_clientdata(client, ak4671); + codec->control_data = client; + + codec->dev = &client->dev; + + return ak4671_register(ak4671, SND_SOC_I2C); +} + +static __devexit int ak4671_i2c_remove(struct i2c_client *client) +{ + struct ak4671_priv *ak4671 = i2c_get_clientdata(client); + + ak4671_unregister(ak4671); + + return 0; +} + +static const struct i2c_device_id ak4671_i2c_id[] = { + { "ak4671", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id); + +static struct i2c_driver ak4671_i2c_driver = { + .driver = { + .name = "ak4671", + .owner = THIS_MODULE, + }, + .probe = ak4671_i2c_probe, + .remove = __devexit_p(ak4671_i2c_remove), + .id_table = ak4671_i2c_id, +}; + +static int __init ak4671_modinit(void) +{ + return i2c_add_driver(&ak4671_i2c_driver); +} +module_init(ak4671_modinit); + +static void __exit ak4671_exit(void) +{ + i2c_del_driver(&ak4671_i2c_driver); +} +module_exit(ak4671_exit); + +MODULE_DESCRIPTION("ASoC AK4671 codec driver"); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h new file mode 100644 index 00000000000..e2fad964e88 --- /dev/null +++ b/sound/soc/codecs/ak4671.h @@ -0,0 +1,156 @@ +/* + * ak4671.h -- audio driver for AK4671 + * + * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _AK4671_H +#define _AK4671_H + +#define AK4671_AD_DA_POWER_MANAGEMENT 0x00 +#define AK4671_PLL_MODE_SELECT0 0x01 +#define AK4671_PLL_MODE_SELECT1 0x02 +#define AK4671_FORMAT_SELECT 0x03 +#define AK4671_MIC_SIGNAL_SELECT 0x04 +#define AK4671_MIC_AMP_GAIN 0x05 +#define AK4671_MIXING_POWER_MANAGEMENT0 0x06 +#define AK4671_MIXING_POWER_MANAGEMENT1 0x07 +#define AK4671_OUTPUT_VOLUME_CONTROL 0x08 +#define AK4671_LOUT1_SIGNAL_SELECT 0x09 +#define AK4671_ROUT1_SIGNAL_SELECT 0x0a +#define AK4671_LOUT2_SIGNAL_SELECT 0x0b +#define AK4671_ROUT2_SIGNAL_SELECT 0x0c +#define AK4671_LOUT3_SIGNAL_SELECT 0x0d +#define AK4671_ROUT3_SIGNAL_SELECT 0x0e +#define AK4671_LOUT1_POWER_MANAGERMENT 0x0f +#define AK4671_LOUT2_POWER_MANAGERMENT 0x10 +#define AK4671_LOUT3_POWER_MANAGERMENT 0x11 +#define AK4671_LCH_INPUT_VOLUME_CONTROL 0x12 +#define AK4671_RCH_INPUT_VOLUME_CONTROL 0x13 +#define AK4671_ALC_REFERENCE_SELECT 0x14 +#define AK4671_DIGITAL_MIXING_CONTROL 0x15 +#define AK4671_ALC_TIMER_SELECT 0x16 +#define AK4671_ALC_MODE_CONTROL 0x17 +#define AK4671_MODE_CONTROL1 0x18 +#define AK4671_MODE_CONTROL2 0x19 +#define AK4671_LCH_OUTPUT_VOLUME_CONTROL 0x1a +#define AK4671_RCH_OUTPUT_VOLUME_CONTROL 0x1b +#define AK4671_SIDETONE_A_CONTROL 0x1c +#define AK4671_DIGITAL_FILTER_SELECT 0x1d +#define AK4671_FIL3_COEFFICIENT0 0x1e +#define AK4671_FIL3_COEFFICIENT1 0x1f +#define AK4671_FIL3_COEFFICIENT2 0x20 +#define AK4671_FIL3_COEFFICIENT3 0x21 +#define AK4671_EQ_COEFFICIENT0 0x22 +#define AK4671_EQ_COEFFICIENT1 0x23 +#define AK4671_EQ_COEFFICIENT2 0x24 +#define AK4671_EQ_COEFFICIENT3 0x25 +#define AK4671_EQ_COEFFICIENT4 0x26 +#define AK4671_EQ_COEFFICIENT5 0x27 +#define AK4671_FIL1_COEFFICIENT0 0x28 +#define AK4671_FIL1_COEFFICIENT1 0x29 +#define AK4671_FIL1_COEFFICIENT2 0x2a +#define AK4671_FIL1_COEFFICIENT3 0x2b +#define AK4671_FIL2_COEFFICIENT0 0x2c +#define AK4671_FIL2_COEFFICIENT1 0x2d +#define AK4671_FIL2_COEFFICIENT2 0x2e +#define AK4671_FIL2_COEFFICIENT3 0x2f +#define AK4671_DIGITAL_FILTER_SELECT2 0x30 +#define AK4671_E1_COEFFICIENT0 0x32 +#define AK4671_E1_COEFFICIENT1 0x33 +#define AK4671_E1_COEFFICIENT2 0x34 +#define AK4671_E1_COEFFICIENT3 0x35 +#define AK4671_E1_COEFFICIENT4 0x36 +#define AK4671_E1_COEFFICIENT5 0x37 +#define AK4671_E2_COEFFICIENT0 0x38 +#define AK4671_E2_COEFFICIENT1 0x39 +#define AK4671_E2_COEFFICIENT2 0x3a +#define AK4671_E2_COEFFICIENT3 0x3b +#define AK4671_E2_COEFFICIENT4 0x3c +#define AK4671_E2_COEFFICIENT5 0x3d +#define AK4671_E3_COEFFICIENT0 0x3e +#define AK4671_E3_COEFFICIENT1 0x3f +#define AK4671_E3_COEFFICIENT2 0x40 +#define AK4671_E3_COEFFICIENT3 0x41 +#define AK4671_E3_COEFFICIENT4 0x42 +#define AK4671_E3_COEFFICIENT5 0x43 +#define AK4671_E4_COEFFICIENT0 0x44 +#define AK4671_E4_COEFFICIENT1 0x45 +#define AK4671_E4_COEFFICIENT2 0x46 +#define AK4671_E4_COEFFICIENT3 0x47 +#define AK4671_E4_COEFFICIENT4 0x48 +#define AK4671_E4_COEFFICIENT5 0x49 +#define AK4671_E5_COEFFICIENT0 0x4a +#define AK4671_E5_COEFFICIENT1 0x4b +#define AK4671_E5_COEFFICIENT2 0x4c +#define AK4671_E5_COEFFICIENT3 0x4d +#define AK4671_E5_COEFFICIENT4 0x4e +#define AK4671_E5_COEFFICIENT5 0x4f +#define AK4671_EQ_CONTROL_250HZ_100HZ 0x50 +#define AK4671_EQ_CONTROL_3500HZ_1KHZ 0x51 +#define AK4671_EQ_CONTRO_10KHZ 0x52 +#define AK4671_PCM_IF_CONTROL0 0x53 +#define AK4671_PCM_IF_CONTROL1 0x54 +#define AK4671_PCM_IF_CONTROL2 0x55 +#define AK4671_DIGITAL_VOLUME_B_CONTROL 0x56 +#define AK4671_DIGITAL_VOLUME_C_CONTROL 0x57 +#define AK4671_SIDETONE_VOLUME_CONTROL 0x58 +#define AK4671_DIGITAL_MIXING_CONTROL2 0x59 +#define AK4671_SAR_ADC_CONTROL 0x5a + +#define AK4671_CACHEREGNUM (AK4671_SAR_ADC_CONTROL + 1) + +/* Bitfield Definitions */ + +/* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */ +#define AK4671_PMVCM 0x01 + +/* AK4671_PLL_MODE_SELECT0 (0x01) Fields */ +#define AK4671_PLL 0x0f +#define AK4671_PLL_11_2896MHZ (4 << 0) +#define AK4671_PLL_12_288MHZ (5 << 0) +#define AK4671_PLL_12MHZ (6 << 0) +#define AK4671_PLL_24MHZ (7 << 0) +#define AK4671_PLL_19_2MHZ (8 << 0) +#define AK4671_PLL_13_5MHZ (12 << 0) +#define AK4671_PLL_27MHZ (13 << 0) +#define AK4671_PLL_13MHZ (14 << 0) +#define AK4671_PLL_26MHZ (15 << 0) +#define AK4671_FS 0xf0 +#define AK4671_FS_8KHZ (0 << 4) +#define AK4671_FS_12KHZ (1 << 4) +#define AK4671_FS_16KHZ (2 << 4) +#define AK4671_FS_24KHZ (3 << 4) +#define AK4671_FS_11_025KHZ (5 << 4) +#define AK4671_FS_22_05KHZ (7 << 4) +#define AK4671_FS_32KHZ (10 << 4) +#define AK4671_FS_48KHZ (11 << 4) +#define AK4671_FS_44_1KHZ (15 << 4) + +/* AK4671_PLL_MODE_SELECT1 (0x02) Fields */ +#define AK4671_PMPLL 0x01 +#define AK4671_M_S 0x02 + +/* AK4671_FORMAT_SELECT (0x03) Fields */ +#define AK4671_DIF 0x03 +#define AK4671_DIF_DSP_MODE (0 << 0) +#define AK4671_DIF_MSB_MODE (2 << 0) +#define AK4671_DIF_I2S_MODE (3 << 0) +#define AK4671_BCKP 0x04 +#define AK4671_MSBS 0x08 +#define AK4671_SDOD 0x10 + +/* AK4671_LOUT2_POWER_MANAGEMENT (0x10) Fields */ +#define AK4671_MUTEN 0x04 + +extern struct snd_soc_dai ak4671_dai; +extern struct snd_soc_codec_device soc_codec_dev_ak4671; + +#endif -- cgit v1.2.3 From c36b2fc73a6c0e7b185b17d594b38398ce1f7fff Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 30 Sep 2009 14:31:38 +0100 Subject: ASoC: Clean up WM8974 PLL configuration Don't use a static for WM8974 PLL factors - we don't support more than one device so it won't happen but no sense in leaving the race condition hanging around. Also, pre_div is a single bit and it's a bit simpler if we move the handling of the factor of 4 in the output into the coefficient setup. Signed-off-by: Mark Brown --- sound/soc/codecs/wm8974.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index 5104c8aa34f..f30f86b3bda 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -330,36 +330,38 @@ static int wm8974_add_widgets(struct snd_soc_codec *codec) } struct pll_ { - unsigned int pre_div:4; /* prescale - 1 */ + unsigned int pre_div:1; unsigned int n:4; unsigned int k; }; -static struct pll_ pll_div; - /* The size in bits of the pll divide multiplied by 10 * to allow rounding later */ #define FIXED_PLL_SIZE ((1 << 24) * 10) -static void pll_factors(unsigned int target, unsigned int source) +static void pll_factors(struct pll_ *pll_div, + unsigned int target, unsigned int source) { unsigned long long Kpart; unsigned int K, Ndiv, Nmod; + /* There is a fixed divide by 4 in the output path */ + target *= 4; + Ndiv = target / source; if (Ndiv < 6) { - source >>= 1; - pll_div.pre_div = 1; + source /= 2; + pll_div->pre_div = 1; Ndiv = target / source; } else - pll_div.pre_div = 0; + pll_div->pre_div = 0; if ((Ndiv < 6) || (Ndiv > 12)) printk(KERN_WARNING "WM8974 N value %u outwith recommended range!\n", Ndiv); - pll_div.n = Ndiv; + pll_div->n = Ndiv; Nmod = target % source; Kpart = FIXED_PLL_SIZE * (long long)Nmod; @@ -374,13 +376,14 @@ static void pll_factors(unsigned int target, unsigned int source) /* Move down to proper range now rounding is done */ K /= 10; - pll_div.k = K; + pll_div->k = K; } static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; + struct pll_ pll_div; u16 reg; if (freq_in == 0 || freq_out == 0) { @@ -394,7 +397,7 @@ static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, return 0; } - pll_factors(freq_out*4, freq_in); + pll_factors(&pll_div, freq_out, freq_in); wm8974_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n); wm8974_write(codec, WM8974_PLLK1, pll_div.k >> 18); -- cgit v1.2.3 From aa983d9d63c38f596fb87754205da9b7a8d2f6fd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 30 Sep 2009 14:16:11 +0100 Subject: ASoC: Factor out analogue platform data from WM8993 This is also shared with newer CODECs. Signed-off-by: Mark Brown --- sound/soc/codecs/wm8993.c | 36 +++++++++--------------------------- sound/soc/codecs/wm_hubs.c | 35 +++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm_hubs.h | 5 +++++ 3 files changed, 49 insertions(+), 27 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 6b32a285260..dac39771214 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1572,33 +1572,15 @@ static int wm8993_i2c_probe(struct i2c_client *i2c, /* Use automatic clock configuration */ snd_soc_update_bits(codec, WM8993_CLOCKING_4, WM8993_SR_MODE, 0); - if (!wm8993->pdata.lineout1_diff) - snd_soc_update_bits(codec, WM8993_LINE_MIXER1, - WM8993_LINEOUT1_MODE, - WM8993_LINEOUT1_MODE); - if (!wm8993->pdata.lineout2_diff) - snd_soc_update_bits(codec, WM8993_LINE_MIXER2, - WM8993_LINEOUT2_MODE, - WM8993_LINEOUT2_MODE); - - if (wm8993->pdata.lineout1fb) - snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL, - WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB); - - if (wm8993->pdata.lineout2fb) - snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL, - WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB); - - /* Apply the microphone bias/detection configuration - the - * platform data is directly applicable to the register. */ - snd_soc_update_bits(codec, WM8993_MICBIAS, - WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK | - WM8993_MICB1_LVL | WM8993_MICB2_LVL, - wm8993->pdata.jd_scthr << WM8993_JD_SCTHR_SHIFT | - wm8993->pdata.jd_thr << WM8993_JD_THR_SHIFT | - wm8993->pdata.micbias1_lvl | - wm8993->pdata.micbias1_lvl << 1); - + wm_hubs_handle_analogue_pdata(codec, wm8993->pdata.lineout1_diff, + wm8993->pdata.lineout2_diff, + wm8993->pdata.lineout1fb, + wm8993->pdata.lineout2fb, + wm8993->pdata.jd_scthr, + wm8993->pdata.jd_thr, + wm8993->pdata.micbias1_lvl, + wm8993->pdata.micbias2_lvl); + ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY); if (ret != 0) goto err; diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index e542027eea8..810a563d0eb 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -738,6 +738,41 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes); +int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec, + int lineout1_diff, int lineout2_diff, + int lineout1fb, int lineout2fb, + int jd_scthr, int jd_thr, int micbias1_lvl, + int micbias2_lvl) +{ + if (!lineout1_diff) + snd_soc_update_bits(codec, WM8993_LINE_MIXER1, + WM8993_LINEOUT1_MODE, + WM8993_LINEOUT1_MODE); + if (!lineout2_diff) + snd_soc_update_bits(codec, WM8993_LINE_MIXER2, + WM8993_LINEOUT2_MODE, + WM8993_LINEOUT2_MODE); + + if (lineout1fb) + snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL, + WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB); + + if (lineout2fb) + snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL, + WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB); + + snd_soc_update_bits(codec, WM8993_MICBIAS, + WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK | + WM8993_MICB1_LVL | WM8993_MICB2_LVL, + jd_scthr << WM8993_JD_SCTHR_SHIFT | + jd_thr << WM8993_JD_THR_SHIFT | + micbias1_lvl | + micbias2_lvl << WM8993_MICB2_LVL_SHIFT); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_hubs_handle_analogue_pdata); + MODULE_DESCRIPTION("Shared support for Wolfson hubs products"); MODULE_AUTHOR("Mark Brown "); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h index ec09cb6a293..36d3fba1de8 100644 --- a/sound/soc/codecs/wm_hubs.h +++ b/sound/soc/codecs/wm_hubs.h @@ -20,5 +20,10 @@ extern const unsigned int wm_hubs_spkmix_tlv[]; extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *); extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int); +extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *, + int lineout1_diff, int lineout2_diff, + int lineout1fb, int lineout2fb, + int jd_scthr, int jd_thr, + int micbias1_lvl, int micbias2_lvl); #endif -- cgit v1.2.3 From bb26276744a80d066681836f4d49c70010b129d6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 1 Oct 2009 07:39:45 +0200 Subject: ASoC: Fix build errors of wm8711.c with SPI Fix a couple of typos and a missing header file inclusion to build wm8711.c properly with CONFIG_SPI_MASTER. Signed-off-by: Takashi Iwai --- sound/soc/codecs/wm8711.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index ae083eb92fb..90ec8c58e2f 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -632,9 +633,9 @@ static int __init wm8711_modinit(void) } #endif #if defined(CONFIG_SPI_MASTER) - ret = spi_register_driver(&wm8731_spi_driver); + ret = spi_register_driver(&wm8711_spi_driver); if (ret != 0) { - printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n", + printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n", ret); } #endif @@ -648,7 +649,7 @@ static void __exit wm8711_exit(void) i2c_del_driver(&wm8711_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) - spi_unregister_driver(&wm8731_spi_driver); + spi_unregister_driver(&wm8711_spi_driver); #endif } module_exit(wm8711_exit); -- cgit v1.2.3 From 493b67efffc462703d583389aca96f850c18d3b3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 9 Oct 2009 15:55:41 +0300 Subject: ASoC: TPA6130A2 amplifier driver Driver for Texas Instruments TPA6130A2 stereo headphone amplifier. The driver provides playback gain control and also pre-defined DAPM_HP widgets and DAPM routings for power management. The DAPM_HP widget names are: "TPA6130A2 Headphone Left" "TPA6130A2 Headphone Right" From soc machine drivers to use with the tpa6130a2 amplifier, the tpa6130a2_add_controls has to be called, which adds the alsa controls and the DAPM routing needed for the tpa6130a2. After that the machine driver can connect the codec's output with 'TPA6130A2 Left' and 'TPA6130A2 Right': {"TPA6130A2 Left", NULL, "CODEC LEFT OUT"}, {"TPA6130A2 Right", NULL, "CODEC RIGHT OUT"}, Internally the left and right channels are powered separately. When none of the channels are needed the amplifier is powered down: hard power: valid GPIO number is passed within platform data soft power: Using the software shutdown of the amplifier Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tpa6130a2.c | 463 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tpa6130a2.h | 62 ++++++ 4 files changed, 531 insertions(+) create mode 100644 sound/soc/codecs/tpa6130a2.c create mode 100644 sound/soc/codecs/tpa6130a2.h (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3c46f34928e..fab01c99182 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -29,6 +29,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER select SND_SOC_TLV320AIC3X if I2C + select SND_SOC_TPA6130A2 if I2C select SND_SOC_TWL4030 if TWL4030_CORE select SND_SOC_UDA134X select SND_SOC_UDA1380 if I2C @@ -228,3 +229,6 @@ config SND_SOC_WM9713 # Amp config SND_SOC_MAX9877 tristate + +config SND_SOC_TPA6130A2 + tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index fc1c458cbe2..2f14391b96f 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -49,6 +49,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o # Amp snd-soc-max9877-objs := max9877.o +snd-soc-tpa6130a2-objs := tpa6130a2.o obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o @@ -101,3 +102,4 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o +obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c new file mode 100644 index 00000000000..1b77c959e2d --- /dev/null +++ b/sound/soc/codecs/tpa6130a2.c @@ -0,0 +1,463 @@ +/* + * ALSA SoC Texas Instruments TPA6130A2 headset stereo amplifier driver + * + * Copyright (C) Nokia Corporation + * + * Author: Peter Ujfalusi + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tpa6130a2.h" + +struct i2c_client *tpa6130a2_client; + +/* This struct is used to save the context */ +struct tpa6130a2_data { + struct mutex mutex; + unsigned char regs[TPA6130A2_CACHEREGNUM]; + int power_gpio; + unsigned char power_state; +}; + +static int tpa6130a2_i2c_read(int reg) +{ + struct tpa6130a2_data *data; + int val; + + BUG_ON(tpa6130a2_client == NULL); + data = i2c_get_clientdata(tpa6130a2_client); + + /* If powered off, return the cached value */ + if (data->power_state) { + val = i2c_smbus_read_byte_data(tpa6130a2_client, reg); + if (val < 0) + dev_err(&tpa6130a2_client->dev, "Read failed\n"); + else + data->regs[reg] = val; + } else { + val = data->regs[reg]; + } + + return val; +} + +static int tpa6130a2_i2c_write(int reg, u8 value) +{ + struct tpa6130a2_data *data; + int val = 0; + + BUG_ON(tpa6130a2_client == NULL); + data = i2c_get_clientdata(tpa6130a2_client); + + if (data->power_state) { + val = i2c_smbus_write_byte_data(tpa6130a2_client, reg, value); + if (val < 0) + dev_err(&tpa6130a2_client->dev, "Write failed\n"); + } + + /* Either powered on or off, we save the context */ + data->regs[reg] = value; + + return val; +} + +static u8 tpa6130a2_read(int reg) +{ + struct tpa6130a2_data *data; + + BUG_ON(tpa6130a2_client == NULL); + data = i2c_get_clientdata(tpa6130a2_client); + + return data->regs[reg]; +} + +static void tpa6130a2_initialize(void) +{ + struct tpa6130a2_data *data; + int i; + + BUG_ON(tpa6130a2_client == NULL); + data = i2c_get_clientdata(tpa6130a2_client); + + for (i = 1; i < TPA6130A2_REG_VERSION; i++) + tpa6130a2_i2c_write(i, data->regs[i]); +} + +void tpa6130a2_power(int power) +{ + struct tpa6130a2_data *data; + u8 val; + + BUG_ON(tpa6130a2_client == NULL); + data = i2c_get_clientdata(tpa6130a2_client); + + mutex_lock(&data->mutex); + if (power) { + /* Power on */ + if (data->power_gpio >= 0) { + gpio_set_value(data->power_gpio, 1); + data->power_state = 1; + tpa6130a2_initialize(); + } + /* Clear SWS */ + val = tpa6130a2_read(TPA6130A2_REG_CONTROL); + val &= ~TPA6130A2_SWS; + tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); + } else { + /* set SWS */ + val = tpa6130a2_read(TPA6130A2_REG_CONTROL); + val |= TPA6130A2_SWS; + tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); + /* Power off */ + if (data->power_gpio >= 0) { + gpio_set_value(data->power_gpio, 0); + data->power_state = 0; + } + } + mutex_unlock(&data->mutex); +} + +static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct tpa6130a2_data *data; + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int mask = mc->max; + unsigned int invert = mc->invert; + + BUG_ON(tpa6130a2_client == NULL); + data = i2c_get_clientdata(tpa6130a2_client); + + mutex_lock(&data->mutex); + + ucontrol->value.integer.value[0] = + (tpa6130a2_read(reg) >> shift) & mask; + + if (invert) + ucontrol->value.integer.value[0] = + mask - ucontrol->value.integer.value[0]; + + mutex_unlock(&data->mutex); + return 0; +} + +static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct tpa6130a2_data *data; + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int mask = mc->max; + unsigned int invert = mc->invert; + unsigned int val = (ucontrol->value.integer.value[0] & mask); + unsigned int val_reg; + + BUG_ON(tpa6130a2_client == NULL); + data = i2c_get_clientdata(tpa6130a2_client); + + if (invert) + val = mask - val; + + mutex_lock(&data->mutex); + + val_reg = tpa6130a2_read(reg); + if (((val_reg >> shift) & mask) == val) { + mutex_unlock(&data->mutex); + return 0; + } + + val_reg &= ~(mask << shift); + val_reg |= val << shift; + tpa6130a2_i2c_write(reg, val_reg); + + mutex_unlock(&data->mutex); + + return 1; +} + +/* + * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going + * down in gain. + */ +static const unsigned int tpa6130_tlv[] = { + TLV_DB_RANGE_HEAD(10), + 0, 1, TLV_DB_SCALE_ITEM(-5950, 600, 0), + 2, 3, TLV_DB_SCALE_ITEM(-5000, 250, 0), + 4, 5, TLV_DB_SCALE_ITEM(-4550, 160, 0), + 6, 7, TLV_DB_SCALE_ITEM(-4140, 190, 0), + 8, 9, TLV_DB_SCALE_ITEM(-3650, 120, 0), + 10, 11, TLV_DB_SCALE_ITEM(-3330, 160, 0), + 12, 13, TLV_DB_SCALE_ITEM(-3040, 180, 0), + 14, 20, TLV_DB_SCALE_ITEM(-2710, 110, 0), + 21, 37, TLV_DB_SCALE_ITEM(-1960, 74, 0), + 38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0), +}; + +static const struct snd_kcontrol_new tpa6130a2_controls[] = { + SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume", + TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0, + tpa6130a2_get_reg, tpa6130a2_set_reg, + tpa6130_tlv), +}; + +/* + * Enable or disable channel (left or right) + * The bit number for mute and amplifier are the same per channel: + * bit 6: Right channel + * bit 7: Left channel + * in both registers. + */ +static void tpa6130a2_channel_enable(u8 channel, int enable) +{ + struct tpa6130a2_data *data; + u8 val; + + BUG_ON(tpa6130a2_client == NULL); + data = i2c_get_clientdata(tpa6130a2_client); + + if (enable) { + /* Enable channel */ + /* Enable amplifier */ + val = tpa6130a2_read(TPA6130A2_REG_CONTROL); + val |= channel; + tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); + + /* Unmute channel */ + val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE); + val &= ~channel; + tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val); + } else { + /* Disable channel */ + /* Mute channel */ + val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE); + val |= channel; + tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val); + + /* Disable amplifier */ + val = tpa6130a2_read(TPA6130A2_REG_CONTROL); + val &= ~channel; + tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); + } +} + +static int tpa6130a2_left_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 1); + break; + case SND_SOC_DAPM_POST_PMD: + tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 0); + break; + } + return 0; +} + +static int tpa6130a2_right_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 1); + break; + case SND_SOC_DAPM_POST_PMD: + tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 0); + break; + } + return 0; +} + +static int tpa6130a2_supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + tpa6130a2_power(1); + break; + case SND_SOC_DAPM_POST_PMD: + tpa6130a2_power(0); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = { + SND_SOC_DAPM_PGA_E("TPA6130A2 Left", SND_SOC_NOPM, + 0, 0, NULL, 0, tpa6130a2_left_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("TPA6130A2 Right", SND_SOC_NOPM, + 0, 0, NULL, 0, tpa6130a2_right_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("TPA6130A2 Enable", SND_SOC_NOPM, + 0, 0, tpa6130a2_supply_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), + /* Outputs */ + SND_SOC_DAPM_HP("TPA6130A2 Headphone Left", NULL), + SND_SOC_DAPM_HP("TPA6130A2 Headphone Right", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Left"}, + {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Right"}, + + {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Enable"}, + {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Enable"}, +}; + +int tpa6130a2_add_controls(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets, + ARRAY_SIZE(tpa6130a2_dapm_widgets)); + + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + return snd_soc_add_controls(codec, tpa6130a2_controls, + ARRAY_SIZE(tpa6130a2_controls)); + +} +EXPORT_SYMBOL_GPL(tpa6130a2_add_controls); + +static int tpa6130a2_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev; + struct tpa6130a2_data *data; + struct tpa6130a2_platform_data *pdata; + int ret; + + dev = &client->dev; + + if (client->dev.platform_data == NULL) { + dev_err(dev, "Platform data not set\n"); + dump_stack(); + return -ENODEV; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) { + dev_err(dev, "Can not allocate memory\n"); + return -ENOMEM; + } + + tpa6130a2_client = client; + + i2c_set_clientdata(tpa6130a2_client, data); + + pdata = (struct tpa6130a2_platform_data *)client->dev.platform_data; + data->power_gpio = pdata->power_gpio; + + mutex_init(&data->mutex); + + /* Set default register values */ + data->regs[TPA6130A2_REG_CONTROL] = TPA6130A2_SWS; + data->regs[TPA6130A2_REG_VOL_MUTE] = TPA6130A2_MUTE_R | + TPA6130A2_MUTE_L; + + if (data->power_gpio >= 0) { + ret = gpio_request(data->power_gpio, "tpa6130a2 enable"); + if (ret < 0) { + dev_err(dev, "Failed to request power GPIO (%d)\n", + data->power_gpio); + goto fail; + } + gpio_direction_output(data->power_gpio, 0); + } else { + data->power_state = 1; + tpa6130a2_initialize(); + } + + tpa6130a2_power(1); + + /* Read version */ + ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) & + TPA6130A2_VERSION_MASK; + if ((ret != 1) && (ret != 2)) + dev_warn(dev, "UNTESTED version detected (%d)\n", ret); + + /* Disable the chip */ + tpa6130a2_power(0); + + return 0; +fail: + kfree(data); + i2c_set_clientdata(tpa6130a2_client, NULL); + tpa6130a2_client = 0; + + return ret; +} + +static int tpa6130a2_remove(struct i2c_client *client) +{ + struct tpa6130a2_data *data = i2c_get_clientdata(client); + + tpa6130a2_power(0); + + if (data->power_gpio >= 0) + gpio_free(data->power_gpio); + kfree(data); + tpa6130a2_client = 0; + + return 0; +} + +static const struct i2c_device_id tpa6130a2_id[] = { + { "tpa6130a2", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tpa6130a2_id); + +static struct i2c_driver tpa6130a2_i2c_driver = { + .driver = { + .name = "tpa6130a2", + .owner = THIS_MODULE, + }, + .probe = tpa6130a2_probe, + .remove = __devexit_p(tpa6130a2_remove), + .id_table = tpa6130a2_id, +}; + +static int __init tpa6130a2_init(void) +{ + return i2c_add_driver(&tpa6130a2_i2c_driver); +} + +static void __exit tpa6130a2_exit(void) +{ + i2c_del_driver(&tpa6130a2_i2c_driver); +} + +MODULE_AUTHOR("Peter Ujfalusi"); +MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver"); +MODULE_LICENSE("GPL"); + +module_init(tpa6130a2_init); +module_exit(tpa6130a2_exit); diff --git a/sound/soc/codecs/tpa6130a2.h b/sound/soc/codecs/tpa6130a2.h new file mode 100644 index 00000000000..6a794f16cee --- /dev/null +++ b/sound/soc/codecs/tpa6130a2.h @@ -0,0 +1,62 @@ +/* + * ALSA SoC TPA6130A2 amplifier driver + * + * Copyright (C) Nokia Corporation + * + * Author: Peter Ujfalusi + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __TPA6130A2_H__ +#define __TPA6130A2_H__ + +/* Register addresses */ +#define TPA6130A2_REG_CONTROL 0x01 +#define TPA6130A2_REG_VOL_MUTE 0x02 +#define TPA6130A2_REG_OUT_IMPEDANCE 0x03 +#define TPA6130A2_REG_VERSION 0x04 + +#define TPA6130A2_CACHEREGNUM (TPA6130A2_REG_VERSION + 1) + +/* Register bits */ +/* TPA6130A2_REG_CONTROL (0x01) */ +#define TPA6130A2_SWS (0x01 << 0) +#define TPA6130A2_TERMAL (0x01 << 1) +#define TPA6130A2_MODE(x) (x << 4) +#define TPA6130A2_MODE_STEREO (0x00) +#define TPA6130A2_MODE_DUAL_MONO (0x01) +#define TPA6130A2_MODE_BRIDGE (0x02) +#define TPA6130A2_MODE_MASK (0x03) +#define TPA6130A2_HP_EN_R (0x01 << 6) +#define TPA6130A2_HP_EN_L (0x01 << 7) + +/* TPA6130A2_REG_VOL_MUTE (0x02) */ +#define TPA6130A2_VOLUME(x) ((x & 0x3f) << 0) +#define TPA6130A2_MUTE_R (0x01 << 6) +#define TPA6130A2_MUTE_L (0x01 << 7) + +/* TPA6130A2_REG_OUT_IMPEDANCE (0x03) */ +#define TPA6130A2_HIZ_R (0x01 << 0) +#define TPA6130A2_HIZ_L (0x01 << 1) + +/* TPA6130A2_REG_VERSION (0x04) */ +#define TPA6130A2_VERSION_MASK (0x0f) + +extern int tpa6130a2_add_controls(struct snd_soc_codec *codec); +extern void tpa6130a2_power(int power); + +#endif /* __TPA6130A2_H__ */ -- cgit v1.2.3 From ebab1b1d07266ab8ca9f65065e68b02f05504c4e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 Oct 2009 19:13:47 +0100 Subject: ASoC: Minor fixups to tpa6130a2 driver - Staticise ttpa6130a2_client. - Remove unneeded cast from void. - Use explict NULL rather than 0. Signed-off-by: Mark Brown --- sound/soc/codecs/tpa6130a2.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 1b77c959e2d..0a6e7b4ace6 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -32,7 +32,7 @@ #include "tpa6130a2.h" -struct i2c_client *tpa6130a2_client; +static struct i2c_client *tpa6130a2_client; /* This struct is used to save the context */ struct tpa6130a2_data { @@ -372,7 +372,7 @@ static int tpa6130a2_probe(struct i2c_client *client, i2c_set_clientdata(tpa6130a2_client, data); - pdata = (struct tpa6130a2_platform_data *)client->dev.platform_data; + pdata = client->dev.platform_data; data->power_gpio = pdata->power_gpio; mutex_init(&data->mutex); @@ -410,7 +410,7 @@ static int tpa6130a2_probe(struct i2c_client *client, fail: kfree(data); i2c_set_clientdata(tpa6130a2_client, NULL); - tpa6130a2_client = 0; + tpa6130a2_client = NULL; return ret; } @@ -424,7 +424,7 @@ static int tpa6130a2_remove(struct i2c_client *client) if (data->power_gpio >= 0) gpio_free(data->power_gpio); kfree(data); - tpa6130a2_client = 0; + tpa6130a2_client = NULL; return 0; } -- cgit v1.2.3 From 814b7963e50e331f129acc25ad92bd4db45c300f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 12 Oct 2009 11:43:55 +0300 Subject: ASoC: TPA6130A2: Make tpa6130a2_power as static The power for the amplifier should be handled internally by the tpa6130a2 driver. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/tpa6130a2.c | 2 +- sound/soc/codecs/tpa6130a2.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 0a6e7b4ace6..6b650c1aa3d 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -106,7 +106,7 @@ static void tpa6130a2_initialize(void) tpa6130a2_i2c_write(i, data->regs[i]); } -void tpa6130a2_power(int power) +static void tpa6130a2_power(int power) { struct tpa6130a2_data *data; u8 val; diff --git a/sound/soc/codecs/tpa6130a2.h b/sound/soc/codecs/tpa6130a2.h index 6a794f16cee..57e867fd86d 100644 --- a/sound/soc/codecs/tpa6130a2.h +++ b/sound/soc/codecs/tpa6130a2.h @@ -57,6 +57,5 @@ #define TPA6130A2_VERSION_MASK (0x0f) extern int tpa6130a2_add_controls(struct snd_soc_codec *codec); -extern void tpa6130a2_power(int power); #endif /* __TPA6130A2_H__ */ -- cgit v1.2.3 From d2058b0cd039aad89b111d83b9c347e9d8f57a84 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 13 Oct 2009 17:39:56 +0100 Subject: ASoC: Remove snd_soc_suspend_device() The PM core will grow pm_link infrastructure in 2.6.33 which can be used to implement the intended functionality of the ASoC-specific device suspend and resume callbacks so drop them. Signed-off-by: Mark Brown --- sound/soc/codecs/cs4270.c | 20 -------------------- sound/soc/codecs/wm8350.c | 17 ----------------- sound/soc/codecs/wm8400.c | 17 ----------------- sound/soc/codecs/wm8523.c | 17 ----------------- sound/soc/codecs/wm8580.c | 17 ----------------- sound/soc/codecs/wm8711.c | 17 ----------------- sound/soc/codecs/wm8731.c | 34 ---------------------------------- sound/soc/codecs/wm8753.c | 35 ----------------------------------- sound/soc/codecs/wm8776.c | 34 ---------------------------------- sound/soc/codecs/wm8900.c | 17 ----------------- sound/soc/codecs/wm8903.c | 17 ----------------- sound/soc/codecs/wm8940.c | 17 ----------------- sound/soc/codecs/wm8960.c | 17 ----------------- sound/soc/codecs/wm8961.c | 17 ----------------- sound/soc/codecs/wm8988.c | 34 ---------------------------------- sound/soc/codecs/wm9081.c | 17 ----------------- 16 files changed, 344 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index ca1e24a8f12..59bb16d033d 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -802,22 +802,6 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id); * and all registers are written back to the hardware when resuming. */ -static int cs4270_i2c_suspend(struct i2c_client *client, pm_message_t mesg) -{ - struct cs4270_private *cs4270 = i2c_get_clientdata(client); - struct snd_soc_codec *codec = &cs4270->codec; - - return snd_soc_suspend_device(codec->dev); -} - -static int cs4270_i2c_resume(struct i2c_client *client) -{ - struct cs4270_private *cs4270 = i2c_get_clientdata(client); - struct snd_soc_codec *codec = &cs4270->codec; - - return snd_soc_resume_device(codec->dev); -} - static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg) { struct snd_soc_codec *codec = cs4270_codec; @@ -853,8 +837,6 @@ static int cs4270_soc_resume(struct platform_device *pdev) return snd_soc_write(codec, CS4270_PWRCTL, reg); } #else -#define cs4270_i2c_suspend NULL -#define cs4270_i2c_resume NULL #define cs4270_soc_suspend NULL #define cs4270_soc_resume NULL #endif /* CONFIG_PM */ @@ -873,8 +855,6 @@ static struct i2c_driver cs4270_i2c_driver = { .id_table = cs4270_id, .probe = cs4270_i2c_probe, .remove = cs4270_i2c_remove, - .suspend = cs4270_i2c_suspend, - .resume = cs4270_i2c_resume, }; /* diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 72abc5a6d8d..714114b50d1 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1680,21 +1680,6 @@ static int __devexit wm8350_codec_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int wm8350_codec_suspend(struct platform_device *pdev, pm_message_t m) -{ - return snd_soc_suspend_device(&pdev->dev); -} - -static int wm8350_codec_resume(struct platform_device *pdev) -{ - return snd_soc_resume_device(&pdev->dev); -} -#else -#define wm8350_codec_suspend NULL -#define wm8350_codec_resume NULL -#endif - static struct platform_driver wm8350_codec_driver = { .driver = { .name = "wm8350-codec", @@ -1702,8 +1687,6 @@ static struct platform_driver wm8350_codec_driver = { }, .probe = wm8350_codec_probe, .remove = __devexit_p(wm8350_codec_remove), - .suspend = wm8350_codec_suspend, - .resume = wm8350_codec_resume, }; static __init int wm8350_init(void) diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 9cb8e50f0fb..bd7eecba20f 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -1559,21 +1559,6 @@ static int __exit wm8400_codec_remove(struct platform_device *dev) return 0; } -#ifdef CONFIG_PM -static int wm8400_pdev_suspend(struct platform_device *pdev, pm_message_t msg) -{ - return snd_soc_suspend_device(&pdev->dev); -} - -static int wm8400_pdev_resume(struct platform_device *pdev) -{ - return snd_soc_resume_device(&pdev->dev); -} -#else -#define wm8400_pdev_suspend NULL -#define wm8400_pdev_resume NULL -#endif - static struct platform_driver wm8400_codec_driver = { .driver = { .name = "wm8400-codec", @@ -1581,8 +1566,6 @@ static struct platform_driver wm8400_codec_driver = { }, .probe = wm8400_codec_probe, .remove = __exit_p(wm8400_codec_remove), - .suspend = wm8400_pdev_suspend, - .resume = wm8400_pdev_resume, }; static int __init wm8400_codec_init(void) diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 25870a4652f..268cab21c2c 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -638,21 +638,6 @@ static __devexit int wm8523_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int wm8523_i2c_suspend(struct i2c_client *i2c, pm_message_t msg) -{ - return snd_soc_suspend_device(&i2c->dev); -} - -static int wm8523_i2c_resume(struct i2c_client *i2c) -{ - return snd_soc_resume_device(&i2c->dev); -} -#else -#define wm8523_i2c_suspend NULL -#define wm8523_i2c_resume NULL -#endif - static const struct i2c_device_id wm8523_i2c_id[] = { { "wm8523", 0 }, { } @@ -666,8 +651,6 @@ static struct i2c_driver wm8523_i2c_driver = { }, .probe = wm8523_i2c_probe, .remove = __devexit_p(wm8523_i2c_remove), - .suspend = wm8523_i2c_suspend, - .resume = wm8523_i2c_resume, .id_table = wm8523_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 3be5c0b2552..a09b23e0366 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -961,21 +961,6 @@ static int wm8580_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int wm8580_i2c_suspend(struct i2c_client *client, pm_message_t msg) -{ - return snd_soc_suspend_device(&client->dev); -} - -static int wm8580_i2c_resume(struct i2c_client *client) -{ - return snd_soc_resume_device(&client->dev); -} -#else -#define wm8580_i2c_suspend NULL -#define wm8580_i2c_resume NULL -#endif - static const struct i2c_device_id wm8580_i2c_id[] = { { "wm8580", 0 }, { } @@ -989,8 +974,6 @@ static struct i2c_driver wm8580_i2c_driver = { }, .probe = wm8580_i2c_probe, .remove = wm8580_i2c_remove, - .suspend = wm8580_i2c_suspend, - .resume = wm8580_i2c_resume, .id_table = wm8580_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 90ec8c58e2f..54189fbf9e9 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -548,21 +548,6 @@ static int __devexit wm8711_spi_remove(struct spi_device *spi) return 0; } -#ifdef CONFIG_PM -static int wm8711_spi_suspend(struct spi_device *spi, pm_message_t msg) -{ - return snd_soc_suspend_device(&spi->dev); -} - -static int wm8711_spi_resume(struct spi_device *spi) -{ - return snd_soc_resume_device(&spi->dev); -} -#else -#define wm8711_spi_suspend NULL -#define wm8711_spi_resume NULL -#endif - static struct spi_driver wm8711_spi_driver = { .driver = { .name = "wm8711", @@ -570,8 +555,6 @@ static struct spi_driver wm8711_spi_driver = { .owner = THIS_MODULE, }, .probe = wm8711_spi_probe, - .suspend = wm8711_spi_suspend, - .resume = wm8711_spi_resume, .remove = __devexit_p(wm8711_spi_remove), }; #endif /* CONFIG_SPI_MASTER */ diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index d3fd4f28d96..0e59219a59f 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -623,21 +623,6 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi) return 0; } -#ifdef CONFIG_PM -static int wm8731_spi_suspend(struct spi_device *spi, pm_message_t msg) -{ - return snd_soc_suspend_device(&spi->dev); -} - -static int wm8731_spi_resume(struct spi_device *spi) -{ - return snd_soc_resume_device(&spi->dev); -} -#else -#define wm8731_spi_suspend NULL -#define wm8731_spi_resume NULL -#endif - static struct spi_driver wm8731_spi_driver = { .driver = { .name = "wm8731", @@ -645,8 +630,6 @@ static struct spi_driver wm8731_spi_driver = { .owner = THIS_MODULE, }, .probe = wm8731_spi_probe, - .suspend = wm8731_spi_suspend, - .resume = wm8731_spi_resume, .remove = __devexit_p(wm8731_spi_remove), }; #endif /* CONFIG_SPI_MASTER */ @@ -679,21 +662,6 @@ static __devexit int wm8731_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int wm8731_i2c_suspend(struct i2c_client *i2c, pm_message_t msg) -{ - return snd_soc_suspend_device(&i2c->dev); -} - -static int wm8731_i2c_resume(struct i2c_client *i2c) -{ - return snd_soc_resume_device(&i2c->dev); -} -#else -#define wm8731_i2c_suspend NULL -#define wm8731_i2c_resume NULL -#endif - static const struct i2c_device_id wm8731_i2c_id[] = { { "wm8731", 0 }, { } @@ -707,8 +675,6 @@ static struct i2c_driver wm8731_i2c_driver = { }, .probe = wm8731_i2c_probe, .remove = __devexit_p(wm8731_i2c_remove), - .suspend = wm8731_i2c_suspend, - .resume = wm8731_i2c_resume, .id_table = wm8731_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 9b27efb052f..8f7305257d2 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1767,21 +1767,6 @@ static int wm8753_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int wm8753_i2c_suspend(struct i2c_client *client, pm_message_t msg) -{ - return snd_soc_suspend_device(&client->dev); -} - -static int wm8753_i2c_resume(struct i2c_client *client) -{ - return snd_soc_resume_device(&client->dev); -} -#else -#define wm8753_i2c_suspend NULL -#define wm8753_i2c_resume NULL -#endif - static const struct i2c_device_id wm8753_i2c_id[] = { { "wm8753", 0 }, { } @@ -1795,8 +1780,6 @@ static struct i2c_driver wm8753_i2c_driver = { }, .probe = wm8753_i2c_probe, .remove = wm8753_i2c_remove, - .suspend = wm8753_i2c_suspend, - .resume = wm8753_i2c_resume, .id_table = wm8753_i2c_id, }; #endif @@ -1852,22 +1835,6 @@ static int __devexit wm8753_spi_remove(struct spi_device *spi) return 0; } -#ifdef CONFIG_PM -static int wm8753_spi_suspend(struct spi_device *spi, pm_message_t msg) -{ - return snd_soc_suspend_device(&spi->dev); -} - -static int wm8753_spi_resume(struct spi_device *spi) -{ - return snd_soc_resume_device(&spi->dev); -} - -#else -#define wm8753_spi_suspend NULL -#define wm8753_spi_resume NULL -#endif - static struct spi_driver wm8753_spi_driver = { .driver = { .name = "wm8753", @@ -1876,8 +1843,6 @@ static struct spi_driver wm8753_spi_driver = { }, .probe = wm8753_spi_probe, .remove = __devexit_p(wm8753_spi_remove), - .suspend = wm8753_spi_suspend, - .resume = wm8753_spi_resume, }; #endif diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index a9829aa26e5..a0bbb28eed7 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -616,21 +616,6 @@ static int __devexit wm8776_spi_remove(struct spi_device *spi) return 0; } -#ifdef CONFIG_PM -static int wm8776_spi_suspend(struct spi_device *spi, pm_message_t msg) -{ - return snd_soc_suspend_device(&spi->dev); -} - -static int wm8776_spi_resume(struct spi_device *spi) -{ - return snd_soc_resume_device(&spi->dev); -} -#else -#define wm8776_spi_suspend NULL -#define wm8776_spi_resume NULL -#endif - static struct spi_driver wm8776_spi_driver = { .driver = { .name = "wm8776", @@ -638,8 +623,6 @@ static struct spi_driver wm8776_spi_driver = { .owner = THIS_MODULE, }, .probe = wm8776_spi_probe, - .suspend = wm8776_spi_suspend, - .resume = wm8776_spi_resume, .remove = __devexit_p(wm8776_spi_remove), }; #endif /* CONFIG_SPI_MASTER */ @@ -673,21 +656,6 @@ static __devexit int wm8776_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int wm8776_i2c_suspend(struct i2c_client *i2c, pm_message_t msg) -{ - return snd_soc_suspend_device(&i2c->dev); -} - -static int wm8776_i2c_resume(struct i2c_client *i2c) -{ - return snd_soc_resume_device(&i2c->dev); -} -#else -#define wm8776_i2c_suspend NULL -#define wm8776_i2c_resume NULL -#endif - static const struct i2c_device_id wm8776_i2c_id[] = { { "wm8776", 0 }, { } @@ -701,8 +669,6 @@ static struct i2c_driver wm8776_i2c_driver = { }, .probe = wm8776_i2c_probe, .remove = __devexit_p(wm8776_i2c_remove), - .suspend = wm8776_i2c_suspend, - .resume = wm8776_i2c_resume, .id_table = wm8776_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 882604ef768..b48804b5cac 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1312,21 +1312,6 @@ static __devexit int wm8900_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int wm8900_i2c_suspend(struct i2c_client *client, pm_message_t msg) -{ - return snd_soc_suspend_device(&client->dev); -} - -static int wm8900_i2c_resume(struct i2c_client *client) -{ - return snd_soc_resume_device(&client->dev); -} -#else -#define wm8900_i2c_suspend NULL -#define wm8900_i2c_resume NULL -#endif - static const struct i2c_device_id wm8900_i2c_id[] = { { "wm8900", 0 }, { } @@ -1340,8 +1325,6 @@ static struct i2c_driver wm8900_i2c_driver = { }, .probe = wm8900_i2c_probe, .remove = __devexit_p(wm8900_i2c_remove), - .suspend = wm8900_i2c_suspend, - .resume = wm8900_i2c_resume, .id_table = wm8900_i2c_id, }; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index fe1307b500c..94cdb813041 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1655,21 +1655,6 @@ static __devexit int wm8903_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int wm8903_i2c_suspend(struct i2c_client *client, pm_message_t msg) -{ - return snd_soc_suspend_device(&client->dev); -} - -static int wm8903_i2c_resume(struct i2c_client *client) -{ - return snd_soc_resume_device(&client->dev); -} -#else -#define wm8903_i2c_suspend NULL -#define wm8903_i2c_resume NULL -#endif - /* i2c codec control layer */ static const struct i2c_device_id wm8903_i2c_id[] = { { "wm8903", 0 }, @@ -1684,8 +1669,6 @@ static struct i2c_driver wm8903_i2c_driver = { }, .probe = wm8903_i2c_probe, .remove = __devexit_p(wm8903_i2c_remove), - .suspend = wm8903_i2c_suspend, - .resume = wm8903_i2c_resume, .id_table = wm8903_i2c_id, }; diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 1685cfb993c..8d4fd3c08c0 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -877,21 +877,6 @@ static int __devexit wm8940_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int wm8940_i2c_suspend(struct i2c_client *client, pm_message_t msg) -{ - return snd_soc_suspend_device(&client->dev); -} - -static int wm8940_i2c_resume(struct i2c_client *client) -{ - return snd_soc_resume_device(&client->dev); -} -#else -#define wm8940_i2c_suspend NULL -#define wm8940_i2c_resume NULL -#endif - static const struct i2c_device_id wm8940_i2c_id[] = { { "wm8940", 0 }, { } @@ -905,8 +890,6 @@ static struct i2c_driver wm8940_i2c_driver = { }, .probe = wm8940_i2c_probe, .remove = __devexit_p(wm8940_i2c_remove), - .suspend = wm8940_i2c_suspend, - .resume = wm8940_i2c_resume, .id_table = wm8940_i2c_id, }; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 416fb3c1701..b9b096a8539 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -883,21 +883,6 @@ static __devexit int wm8960_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int wm8960_i2c_suspend(struct i2c_client *client, pm_message_t msg) -{ - return snd_soc_suspend_device(&client->dev); -} - -static int wm8960_i2c_resume(struct i2c_client *client) -{ - return snd_soc_resume_device(&client->dev); -} -#else -#define wm8960_i2c_suspend NULL -#define wm8960_i2c_resume NULL -#endif - static const struct i2c_device_id wm8960_i2c_id[] = { { "wm8960", 0 }, { } @@ -911,8 +896,6 @@ static struct i2c_driver wm8960_i2c_driver = { }, .probe = wm8960_i2c_probe, .remove = __devexit_p(wm8960_i2c_remove), - .suspend = wm8960_i2c_suspend, - .resume = wm8960_i2c_resume, .id_table = wm8960_i2c_id, }; diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index 50303208589..b5c6f2cd5ae 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -1206,21 +1206,6 @@ static __devexit int wm8961_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int wm8961_i2c_suspend(struct i2c_client *client, pm_message_t state) -{ - return snd_soc_suspend_device(&client->dev); -} - -static int wm8961_i2c_resume(struct i2c_client *client) -{ - return snd_soc_resume_device(&client->dev); -} -#else -#define wm8961_i2c_suspend NULL -#define wm8961_i2c_resume NULL -#endif - static const struct i2c_device_id wm8961_i2c_id[] = { { "wm8961", 0 }, { } @@ -1234,8 +1219,6 @@ static struct i2c_driver wm8961_i2c_driver = { }, .probe = wm8961_i2c_probe, .remove = __devexit_p(wm8961_i2c_remove), - .suspend = wm8961_i2c_suspend, - .resume = wm8961_i2c_resume, .id_table = wm8961_i2c_id, }; diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 3f530f8a972..d8d8f68b81e 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -944,21 +944,6 @@ static int wm8988_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int wm8988_i2c_suspend(struct i2c_client *client, pm_message_t msg) -{ - return snd_soc_suspend_device(&client->dev); -} - -static int wm8988_i2c_resume(struct i2c_client *client) -{ - return snd_soc_resume_device(&client->dev); -} -#else -#define wm8988_i2c_suspend NULL -#define wm8988_i2c_resume NULL -#endif - static const struct i2c_device_id wm8988_i2c_id[] = { { "wm8988", 0 }, { } @@ -972,8 +957,6 @@ static struct i2c_driver wm8988_i2c_driver = { }, .probe = wm8988_i2c_probe, .remove = wm8988_i2c_remove, - .suspend = wm8988_i2c_suspend, - .resume = wm8988_i2c_resume, .id_table = wm8988_i2c_id, }; #endif @@ -1006,21 +989,6 @@ static int __devexit wm8988_spi_remove(struct spi_device *spi) return 0; } -#ifdef CONFIG_PM -static int wm8988_spi_suspend(struct spi_device *spi, pm_message_t msg) -{ - return snd_soc_suspend_device(&spi->dev); -} - -static int wm8988_spi_resume(struct spi_device *spi) -{ - return snd_soc_resume_device(&spi->dev); -} -#else -#define wm8988_spi_suspend NULL -#define wm8988_spi_resume NULL -#endif - static struct spi_driver wm8988_spi_driver = { .driver = { .name = "wm8988", @@ -1029,8 +997,6 @@ static struct spi_driver wm8988_spi_driver = { }, .probe = wm8988_spi_probe, .remove = __devexit_p(wm8988_spi_remove), - .suspend = wm8988_spi_suspend, - .resume = wm8988_spi_resume, }; #endif diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 686e5aa9720..4cb6b104b72 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -1452,21 +1452,6 @@ static __devexit int wm9081_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int wm9081_i2c_suspend(struct i2c_client *client, pm_message_t msg) -{ - return snd_soc_suspend_device(&client->dev); -} - -static int wm9081_i2c_resume(struct i2c_client *client) -{ - return snd_soc_resume_device(&client->dev); -} -#else -#define wm9081_i2c_suspend NULL -#define wm9081_i2c_resume NULL -#endif - static const struct i2c_device_id wm9081_i2c_id[] = { { "wm9081", 0 }, { } @@ -1480,8 +1465,6 @@ static struct i2c_driver wm9081_i2c_driver = { }, .probe = wm9081_i2c_probe, .remove = __devexit_p(wm9081_i2c_remove), - .suspend = wm9081_i2c_suspend, - .resume = wm9081_i2c_resume, .id_table = wm9081_i2c_id, }; -- cgit v1.2.3 From c8bf93f0fe8c5a509a29e30f3bac823fa0f6d96e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 15 Oct 2009 09:03:56 +0300 Subject: ASoC: Codec driver for Texas Instruments tlv320dac33 codec Driver for Texas Instruments TLV320DAC33 (SLAS546) low power stereo audio DAC. TLV320DAC33 is a stereo audio codec with integrated 24KB FIFO for low power audio playback. The digital interface can use I2S, DSP (A or B), Right and Left justified formats. DAC33 has stereo analog input, which can be bypassed to the analog outputs. Regarding to the internal 24KB FIFO the driver implements 'FIFO bypass' mode (default) and nSample mode (FIFO is in use). a) In 'FIFO bypass' mode the internal FIFO is not in use, the codec is working synchronously as a normal codec (it needs constant stream of data on the digital interface). b) The nSample mode implementation uses one interrupt line from DAC33 to the host: Alarm threshold is set to 10ms of audio data (limit by the driver implementation). DAC33 will signal an interrupt, when the FIFO level goes under the Alarm threshold. The host will write to nSample register a value (number of stereo samples), to tell DAC33 how many samples it should read in a burst from the host. When the DAC33 received the number of samples, it disables the clocks on the I2S bus. When the FIFO use again goes under the Alarm threshold, DAC33 signals the host with an interrupt, and the process is repeated. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tlv320dac33.c | 1237 ++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tlv320dac33.h | 267 +++++++++ 4 files changed, 1510 insertions(+) create mode 100644 sound/soc/codecs/tlv320dac33.c create mode 100644 sound/soc/codecs/tlv320dac33.h (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index fab01c99182..d30fce71cfe 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -30,6 +30,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TLV320AIC26 if SPI_MASTER select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C + select SND_SOC_TLV320DAC33 if I2C select SND_SOC_TWL4030 if TWL4030_CORE select SND_SOC_UDA134X select SND_SOC_UDA1380 if I2C @@ -142,6 +143,9 @@ config SND_SOC_TLV320AIC26 config SND_SOC_TLV320AIC3X tristate +config SND_SOC_TLV320DAC33 + tristate + config SND_SOC_TWL4030 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 2f14391b96f..8f519ee9600 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -17,6 +17,7 @@ snd-soc-stac9766-objs := stac9766.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o +snd-soc-tlv320dac33-objs := tlv320dac33.o snd-soc-twl4030-objs := twl4030.o snd-soc-uda134x-objs := uda134x.o snd-soc-uda1380-objs := uda1380.o @@ -70,6 +71,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o +obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c new file mode 100644 index 00000000000..3ca8934fc26 --- /dev/null +++ b/sound/soc/codecs/tlv320dac33.c @@ -0,0 +1,1237 @@ +/* + * ALSA SoC Texas Instruments TLV320DAC33 codec driver + * + * Author: Peter Ujfalusi + * + * Copyright: (C) 2009 Nokia Corporation + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "tlv320dac33.h" + +#define DAC33_BUFFER_SIZE_BYTES 24576 /* bytes, 12288 16 bit words, + * 6144 stereo */ +#define DAC33_BUFFER_SIZE_SAMPLES 6144 + +#define NSAMPLE_MAX 5700 + +#define LATENCY_TIME_MS 20 + +static struct snd_soc_codec *tlv320dac33_codec; + +enum dac33_state { + DAC33_IDLE = 0, + DAC33_PREFILL, + DAC33_PLAYBACK, + DAC33_FLUSH, +}; + +struct tlv320dac33_priv { + struct mutex mutex; + struct workqueue_struct *dac33_wq; + struct work_struct work; + struct snd_soc_codec codec; + int power_gpio; + int chip_power; + int irq; + unsigned int refclk; + + unsigned int alarm_threshold; /* set to be half of LATENCY_TIME_MS */ + unsigned int nsample_min; /* nsample should not be lower than + * this */ + unsigned int nsample_max; /* nsample should not be higher than + * this */ + unsigned int nsample_switch; /* Use FIFO or bypass FIFO switch */ + unsigned int nsample; /* burst read amount from host */ + + enum dac33_state state; +}; + +static const u8 dac33_reg[DAC33_CACHEREGNUM] = { +0x00, 0x00, 0x00, 0x00, /* 0x00 - 0x03 */ +0x00, 0x00, 0x00, 0x00, /* 0x04 - 0x07 */ +0x00, 0x00, 0x00, 0x00, /* 0x08 - 0x0b */ +0x00, 0x00, 0x00, 0x00, /* 0x0c - 0x0f */ +0x00, 0x00, 0x00, 0x00, /* 0x10 - 0x13 */ +0x00, 0x00, 0x00, 0x00, /* 0x14 - 0x17 */ +0x00, 0x00, 0x00, 0x00, /* 0x18 - 0x1b */ +0x00, 0x00, 0x00, 0x00, /* 0x1c - 0x1f */ +0x00, 0x00, 0x00, 0x00, /* 0x20 - 0x23 */ +0x00, 0x00, 0x00, 0x00, /* 0x24 - 0x27 */ +0x00, 0x00, 0x00, 0x00, /* 0x28 - 0x2b */ +0x00, 0x00, 0x00, 0x80, /* 0x2c - 0x2f */ +0x80, 0x00, 0x00, 0x00, /* 0x30 - 0x33 */ +0x00, 0x00, 0x00, 0x00, /* 0x34 - 0x37 */ +0x00, 0x00, /* 0x38 - 0x39 */ +/* Registers 0x3a - 0x3f are reserved */ + 0x00, 0x00, /* 0x3a - 0x3b */ +0x00, 0x00, 0x00, 0x00, /* 0x3c - 0x3f */ + +0x00, 0x00, 0x00, 0x00, /* 0x40 - 0x43 */ +0x00, 0x80, /* 0x44 - 0x45 */ +/* Registers 0x46 - 0x47 are reserved */ + 0x80, 0x80, /* 0x46 - 0x47 */ + +0x80, 0x00, 0x00, /* 0x48 - 0x4a */ +/* Registers 0x4b - 0x7c are reserved */ + 0x00, /* 0x4b */ +0x00, 0x00, 0x00, 0x00, /* 0x4c - 0x4f */ +0x00, 0x00, 0x00, 0x00, /* 0x50 - 0x53 */ +0x00, 0x00, 0x00, 0x00, /* 0x54 - 0x57 */ +0x00, 0x00, 0x00, 0x00, /* 0x58 - 0x5b */ +0x00, 0x00, 0x00, 0x00, /* 0x5c - 0x5f */ +0x00, 0x00, 0x00, 0x00, /* 0x60 - 0x63 */ +0x00, 0x00, 0x00, 0x00, /* 0x64 - 0x67 */ +0x00, 0x00, 0x00, 0x00, /* 0x68 - 0x6b */ +0x00, 0x00, 0x00, 0x00, /* 0x6c - 0x6f */ +0x00, 0x00, 0x00, 0x00, /* 0x70 - 0x73 */ +0x00, 0x00, 0x00, 0x00, /* 0x74 - 0x77 */ +0x00, 0x00, 0x00, 0x00, /* 0x78 - 0x7b */ +0x00, /* 0x7c */ + + 0xda, 0x33, 0x03, /* 0x7d - 0x7f */ +}; + +/* Register read and write */ +static inline unsigned int dac33_read_reg_cache(struct snd_soc_codec *codec, + unsigned reg) +{ + u8 *cache = codec->reg_cache; + if (reg >= DAC33_CACHEREGNUM) + return 0; + + return cache[reg]; +} + +static inline void dac33_write_reg_cache(struct snd_soc_codec *codec, + u8 reg, u8 value) +{ + u8 *cache = codec->reg_cache; + if (reg >= DAC33_CACHEREGNUM) + return; + + cache[reg] = value; +} + +static int dac33_read(struct snd_soc_codec *codec, unsigned int reg, + u8 *value) +{ + struct tlv320dac33_priv *dac33 = codec->private_data; + int val; + + *value = reg & 0xff; + + /* If powered off, return the cached value */ + if (dac33->chip_power) { + val = i2c_smbus_read_byte_data(codec->control_data, value[0]); + if (val < 0) { + dev_err(codec->dev, "Read failed (%d)\n", val); + value[0] = dac33_read_reg_cache(codec, reg); + } else { + value[0] = val; + dac33_write_reg_cache(codec, reg, val); + } + } else { + value[0] = dac33_read_reg_cache(codec, reg); + } + + return 0; +} + +static int dac33_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + struct tlv320dac33_priv *dac33 = codec->private_data; + u8 data[2]; + int ret = 0; + + /* + * data is + * D15..D8 dac33 register offset + * D7...D0 register data + */ + data[0] = reg & 0xff; + data[1] = value & 0xff; + + dac33_write_reg_cache(codec, data[0], data[1]); + if (dac33->chip_power) { + ret = codec->hw_write(codec->control_data, data, 2); + if (ret != 2) + dev_err(codec->dev, "Write failed (%d)\n", ret); + else + ret = 0; + } + + return ret; +} + +static int dac33_write_locked(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + struct tlv320dac33_priv *dac33 = codec->private_data; + int ret; + + mutex_lock(&dac33->mutex); + ret = dac33_write(codec, reg, value); + mutex_unlock(&dac33->mutex); + + return ret; +} + +#define DAC33_I2C_ADDR_AUTOINC 0x80 +static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + struct tlv320dac33_priv *dac33 = codec->private_data; + u8 data[3]; + int ret = 0; + + /* + * data is + * D23..D16 dac33 register offset + * D15..D8 register data MSB + * D7...D0 register data LSB + */ + data[0] = reg & 0xff; + data[1] = (value >> 8) & 0xff; + data[2] = value & 0xff; + + dac33_write_reg_cache(codec, data[0], data[1]); + dac33_write_reg_cache(codec, data[0] + 1, data[2]); + + if (dac33->chip_power) { + /* We need to set autoincrement mode for 16 bit writes */ + data[0] |= DAC33_I2C_ADDR_AUTOINC; + ret = codec->hw_write(codec->control_data, data, 3); + if (ret != 3) + dev_err(codec->dev, "Write failed (%d)\n", ret); + else + ret = 0; + } + + return ret; +} + +static void dac33_restore_regs(struct snd_soc_codec *codec) +{ + struct tlv320dac33_priv *dac33 = codec->private_data; + u8 *cache = codec->reg_cache; + u8 data[2]; + int i, ret; + + if (!dac33->chip_power) + return; + + for (i = DAC33_PWR_CTRL; i <= DAC33_INTP_CTRL_B; i++) { + data[0] = i; + data[1] = cache[i]; + /* Skip the read only registers */ + if ((i >= DAC33_INT_OSC_STATUS && + i <= DAC33_INT_OSC_FREQ_RAT_READ_B) || + (i >= DAC33_FIFO_WPTR_MSB && i <= DAC33_FIFO_IRQ_FLAG) || + i == DAC33_DAC_STATUS_FLAGS || + i == DAC33_SRC_EST_REF_CLK_RATIO_A || + i == DAC33_SRC_EST_REF_CLK_RATIO_B) + continue; + ret = codec->hw_write(codec->control_data, data, 2); + if (ret != 2) + dev_err(codec->dev, "Write failed (%d)\n", ret); + } + for (i = DAC33_LDAC_PWR_CTRL; i <= DAC33_LINEL_TO_LLO_VOL; i++) { + data[0] = i; + data[1] = cache[i]; + ret = codec->hw_write(codec->control_data, data, 2); + if (ret != 2) + dev_err(codec->dev, "Write failed (%d)\n", ret); + } + for (i = DAC33_LINER_TO_RLO_VOL; i <= DAC33_OSC_TRIM; i++) { + data[0] = i; + data[1] = cache[i]; + ret = codec->hw_write(codec->control_data, data, 2); + if (ret != 2) + dev_err(codec->dev, "Write failed (%d)\n", ret); + } +} + +static inline void dac33_soft_power(struct snd_soc_codec *codec, int power) +{ + u8 reg; + + reg = dac33_read_reg_cache(codec, DAC33_PWR_CTRL); + if (power) + reg |= DAC33_PDNALLB; + else + reg &= ~DAC33_PDNALLB; + dac33_write(codec, DAC33_PWR_CTRL, reg); +} + +static void dac33_hard_power(struct snd_soc_codec *codec, int power) +{ + struct tlv320dac33_priv *dac33 = codec->private_data; + + mutex_lock(&dac33->mutex); + if (power) { + if (dac33->power_gpio >= 0) { + gpio_set_value(dac33->power_gpio, 1); + dac33->chip_power = 1; + /* Restore registers */ + dac33_restore_regs(codec); + } + dac33_soft_power(codec, 1); + } else { + dac33_soft_power(codec, 0); + if (dac33->power_gpio >= 0) { + gpio_set_value(dac33->power_gpio, 0); + dac33->chip_power = 0; + } + } + mutex_unlock(&dac33->mutex); + +} + +static int dac33_get_nsample(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct tlv320dac33_priv *dac33 = codec->private_data; + + ucontrol->value.integer.value[0] = dac33->nsample; + + return 0; +} + +static int dac33_set_nsample(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct tlv320dac33_priv *dac33 = codec->private_data; + int ret = 0; + + if (dac33->nsample == ucontrol->value.integer.value[0]) + return 0; + + if (ucontrol->value.integer.value[0] < dac33->nsample_min || + ucontrol->value.integer.value[0] > dac33->nsample_max) + ret = -EINVAL; + else + dac33->nsample = ucontrol->value.integer.value[0]; + + return ret; +} + +static int dac33_get_nsample_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct tlv320dac33_priv *dac33 = codec->private_data; + + ucontrol->value.integer.value[0] = dac33->nsample_switch; + + return 0; +} + +static int dac33_set_nsample_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct tlv320dac33_priv *dac33 = codec->private_data; + int ret = 0; + + if (dac33->nsample_switch == ucontrol->value.integer.value[0]) + return 0; + /* Do not allow changes while stream is running*/ + if (codec->active) + return -EPERM; + + if (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] > 1) + ret = -EINVAL; + else + dac33->nsample_switch = ucontrol->value.integer.value[0]; + + return ret; +} + +/* + * DACL/R digital volume control: + * from 0 dB to -63.5 in 0.5 dB steps + * Need to be inverted later on: + * 0x00 == 0 dB + * 0x7f == -63.5 dB + */ +static DECLARE_TLV_DB_SCALE(dac_digivol_tlv, -6350, 50, 0); + +static const struct snd_kcontrol_new dac33_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC Digital Playback Volume", + DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL, + 0, 0x7f, 1, dac_digivol_tlv), + SOC_DOUBLE_R("DAC Digital Playback Switch", + DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL, 7, 1, 1), + SOC_DOUBLE_R("Line to Line Out Volume", + DAC33_LINEL_TO_LLO_VOL, DAC33_LINER_TO_RLO_VOL, 0, 127, 1), +}; + +static const struct snd_kcontrol_new dac33_nsample_snd_controls[] = { + SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0, + dac33_get_nsample, dac33_set_nsample), + SOC_SINGLE_EXT("nSample Switch", 0, 0, 1, 0, + dac33_get_nsample_switch, dac33_set_nsample_switch), +}; + +/* Analog bypass */ +static const struct snd_kcontrol_new dac33_dapm_abypassl_control = + SOC_DAPM_SINGLE("Switch", DAC33_LINEL_TO_LLO_VOL, 7, 1, 1); + +static const struct snd_kcontrol_new dac33_dapm_abypassr_control = + SOC_DAPM_SINGLE("Switch", DAC33_LINER_TO_RLO_VOL, 7, 1, 1); + +static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("LEFT_LO"), + SND_SOC_DAPM_OUTPUT("RIGHT_LO"), + + SND_SOC_DAPM_INPUT("LINEL"), + SND_SOC_DAPM_INPUT("LINER"), + + SND_SOC_DAPM_DAC("DACL", "Left Playback", DAC33_LDAC_PWR_CTRL, 2, 0), + SND_SOC_DAPM_DAC("DACR", "Right Playback", DAC33_RDAC_PWR_CTRL, 2, 0), + + /* Analog bypass */ + SND_SOC_DAPM_SWITCH("Analog Left Bypass", SND_SOC_NOPM, 0, 0, + &dac33_dapm_abypassl_control), + SND_SOC_DAPM_SWITCH("Analog Right Bypass", SND_SOC_NOPM, 0, 0, + &dac33_dapm_abypassr_control), + + SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Left Amp Power", + DAC33_OUT_AMP_PWR_CTRL, 6, 3, 3, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Right Amp Power", + DAC33_OUT_AMP_PWR_CTRL, 4, 3, 3, 0), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Analog bypass */ + {"Analog Left Bypass", "Switch", "LINEL"}, + {"Analog Right Bypass", "Switch", "LINER"}, + + {"Output Left Amp Power", NULL, "DACL"}, + {"Output Right Amp Power", NULL, "DACR"}, + + {"Output Left Amp Power", NULL, "Analog Left Bypass"}, + {"Output Right Amp Power", NULL, "Analog Right Bypass"}, + + /* output */ + {"LEFT_LO", NULL, "Output Left Amp Power"}, + {"RIGHT_LO", NULL, "Output Right Amp Power"}, +}; + +static int dac33_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, dac33_dapm_widgets, + ARRAY_SIZE(dac33_dapm_widgets)); + + /* set up audio path interconnects */ + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_widgets(codec); + + return 0; +} + +static int dac33_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + dac33_soft_power(codec, 1); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->bias_level == SND_SOC_BIAS_OFF) + dac33_hard_power(codec, 1); + dac33_soft_power(codec, 0); + break; + case SND_SOC_BIAS_OFF: + dac33_hard_power(codec, 0); + break; + } + codec->bias_level = level; + + return 0; +} + +static void dac33_work(struct work_struct *work) +{ + struct snd_soc_codec *codec; + struct tlv320dac33_priv *dac33; + u8 reg; + + dac33 = container_of(work, struct tlv320dac33_priv, work); + codec = &dac33->codec; + + mutex_lock(&dac33->mutex); + switch (dac33->state) { + case DAC33_PREFILL: + dac33->state = DAC33_PLAYBACK; + dac33_write16(codec, DAC33_NSAMPLE_MSB, + DAC33_THRREG(dac33->nsample)); + dac33_write16(codec, DAC33_PREFILL_MSB, + DAC33_THRREG(dac33->alarm_threshold)); + break; + case DAC33_PLAYBACK: + dac33_write16(codec, DAC33_NSAMPLE_MSB, + DAC33_THRREG(dac33->nsample)); + break; + case DAC33_IDLE: + break; + case DAC33_FLUSH: + dac33->state = DAC33_IDLE; + /* Mask all interrupts from dac33 */ + dac33_write(codec, DAC33_FIFO_IRQ_MASK, 0); + + /* flush fifo */ + reg = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A); + reg |= DAC33_FIFOFLUSH; + dac33_write(codec, DAC33_FIFO_CTRL_A, reg); + break; + } + mutex_unlock(&dac33->mutex); +} + +static irqreturn_t dac33_interrupt_handler(int irq, void *dev) +{ + struct snd_soc_codec *codec = dev; + struct tlv320dac33_priv *dac33 = codec->private_data; + + queue_work(dac33->dac33_wq, &dac33->work); + + return IRQ_HANDLED; +} + +static void dac33_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + struct tlv320dac33_priv *dac33 = codec->private_data; + unsigned int pwr_ctrl; + + /* Stop pending workqueue */ + if (dac33->nsample_switch) + cancel_work_sync(&dac33->work); + + mutex_lock(&dac33->mutex); + pwr_ctrl = dac33_read_reg_cache(codec, DAC33_PWR_CTRL); + pwr_ctrl &= ~(DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB); + dac33_write(codec, DAC33_PWR_CTRL, pwr_ctrl); + mutex_unlock(&dac33->mutex); +} + +static void dac33_oscwait(struct snd_soc_codec *codec) +{ + int timeout = 20; + u8 reg; + + do { + msleep(1); + dac33_read(codec, DAC33_INT_OSC_STATUS, ®); + } while (((reg & 0x03) != DAC33_OSCSTATUS_NORMAL) && timeout--); + if ((reg & 0x03) != DAC33_OSCSTATUS_NORMAL) + dev_err(codec->dev, + "internal oscillator calibration failed\n"); +} + +static int dac33_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + + /* Check parameters for validity */ + switch (params_rate(params)) { + case 44100: + case 48000: + break; + default: + dev_err(codec->dev, "unsupported rate %d\n", + params_rate(params)); + return -EINVAL; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + default: + dev_err(codec->dev, "unsupported format %d\n", + params_format(params)); + return -EINVAL; + } + + return 0; +} + +#define CALC_OSCSET(rate, refclk) ( \ + ((((rate * 10000) / refclk) * 4096) + 5000) / 10000) +#define CALC_RATIOSET(rate, refclk) ( \ + ((((refclk * 100000) / rate) * 16384) + 50000) / 100000) + +/* + * tlv320dac33 is strict on the sequence of the register writes, if the register + * writes happens in different order, than dac33 might end up in unknown state. + * Use the known, working sequence of register writes to initialize the dac33. + */ +static int dac33_prepare_chip(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + struct tlv320dac33_priv *dac33 = codec->private_data; + unsigned int oscset, ratioset, pwr_ctrl, reg_tmp; + u8 aictrl_a, fifoctrl_a; + + switch (substream->runtime->rate) { + case 44100: + case 48000: + oscset = CALC_OSCSET(substream->runtime->rate, dac33->refclk); + ratioset = CALC_RATIOSET(substream->runtime->rate, + dac33->refclk); + break; + default: + dev_err(codec->dev, "unsupported rate %d\n", + substream->runtime->rate); + return -EINVAL; + } + + + aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A); + aictrl_a &= ~(DAC33_NCYCL_MASK | DAC33_WLEN_MASK); + fifoctrl_a = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A); + fifoctrl_a &= ~DAC33_WIDTH; + switch (substream->runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + aictrl_a |= (DAC33_NCYCL_16 | DAC33_WLEN_16); + fifoctrl_a |= DAC33_WIDTH; + break; + default: + dev_err(codec->dev, "unsupported format %d\n", + substream->runtime->format); + return -EINVAL; + } + + mutex_lock(&dac33->mutex); + dac33_soft_power(codec, 1); + + reg_tmp = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL); + dac33_write(codec, DAC33_INT_OSC_CTRL, reg_tmp); + + /* Write registers 0x08 and 0x09 (MSB, LSB) */ + dac33_write16(codec, DAC33_INT_OSC_FREQ_RAT_A, oscset); + + /* calib time: 128 is a nice number ;) */ + dac33_write(codec, DAC33_CALIB_TIME, 128); + + /* adjustment treshold & step */ + dac33_write(codec, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) | + DAC33_ADJSTEP(1)); + + /* div=4 / gain=1 / div */ + dac33_write(codec, DAC33_INT_OSC_CTRL_C, DAC33_REFDIV(4)); + + pwr_ctrl = dac33_read_reg_cache(codec, DAC33_PWR_CTRL); + pwr_ctrl |= DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB; + dac33_write(codec, DAC33_PWR_CTRL, pwr_ctrl); + + dac33_oscwait(codec); + + if (dac33->nsample_switch) { + /* 50-51 : ASRC Control registers */ + dac33_write(codec, DAC33_ASRC_CTRL_A, (1 << 4)); /* div=2 */ + dac33_write(codec, DAC33_ASRC_CTRL_B, 1); /* ??? */ + + /* Write registers 0x34 and 0x35 (MSB, LSB) */ + dac33_write16(codec, DAC33_SRC_REF_CLK_RATIO_A, ratioset); + + /* Set interrupts to high active */ + dac33_write(codec, DAC33_INTP_CTRL_A, DAC33_INTPM_AHIGH); + + dac33_write(codec, DAC33_FIFO_IRQ_MODE_B, + DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL)); + dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT); + } else { + /* 50-51 : ASRC Control registers */ + dac33_write(codec, DAC33_ASRC_CTRL_A, DAC33_SRCBYP); + dac33_write(codec, DAC33_ASRC_CTRL_B, 0); /* ??? */ + } + + if (dac33->nsample_switch) + fifoctrl_a &= ~DAC33_FBYPAS; + else + fifoctrl_a |= DAC33_FBYPAS; + dac33_write(codec, DAC33_FIFO_CTRL_A, fifoctrl_a); + + dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a); + reg_tmp = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B); + if (dac33->nsample_switch) + reg_tmp &= ~DAC33_BCLKON; + else + reg_tmp |= DAC33_BCLKON; + dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, reg_tmp); + + if (dac33->nsample_switch) { + /* 20: BCLK divide ratio */ + dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 3); + + dac33_write16(codec, DAC33_ATHR_MSB, + DAC33_THRREG(dac33->alarm_threshold)); + } else { + dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32); + } + + mutex_unlock(&dac33->mutex); + + return 0; +} + +static void dac33_calculate_times(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + struct tlv320dac33_priv *dac33 = codec->private_data; + unsigned int nsample_limit; + + /* Number of samples (16bit, stereo) in one period */ + dac33->nsample_min = snd_pcm_lib_period_bytes(substream) / 4; + + /* Number of samples (16bit, stereo) in ALSA buffer */ + dac33->nsample_max = snd_pcm_lib_buffer_bytes(substream) / 4; + /* Subtract one period from the total */ + dac33->nsample_max -= dac33->nsample_min; + + /* Number of samples for LATENCY_TIME_MS / 2 */ + dac33->alarm_threshold = substream->runtime->rate / + (1000 / (LATENCY_TIME_MS / 2)); + + /* Find and fix up the lowest nsmaple limit */ + nsample_limit = substream->runtime->rate / (1000 / LATENCY_TIME_MS); + + if (dac33->nsample_min < nsample_limit) + dac33->nsample_min = nsample_limit; + + if (dac33->nsample < dac33->nsample_min) + dac33->nsample = dac33->nsample_min; + + /* + * Find and fix up the highest nsmaple limit + * In order to not overflow the DAC33 buffer substract the + * alarm_threshold value from the size of the DAC33 buffer + */ + nsample_limit = DAC33_BUFFER_SIZE_SAMPLES - dac33->alarm_threshold; + + if (dac33->nsample_max > nsample_limit) + dac33->nsample_max = nsample_limit; + + if (dac33->nsample > dac33->nsample_max) + dac33->nsample = dac33->nsample_max; +} + +static int dac33_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + dac33_calculate_times(substream); + dac33_prepare_chip(substream); + + return 0; +} + +static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + struct tlv320dac33_priv *dac33 = codec->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (dac33->nsample_switch) { + dac33->state = DAC33_PREFILL; + queue_work(dac33->dac33_wq, &dac33->work); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (dac33->nsample_switch) { + dac33->state = DAC33_FLUSH; + queue_work(dac33->dac33_wq, &dac33->work); + } + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int dac33_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 tlv320dac33_priv *dac33 = codec->private_data; + u8 ioc_reg, asrcb_reg; + + ioc_reg = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL); + asrcb_reg = dac33_read_reg_cache(codec, DAC33_ASRC_CTRL_B); + switch (clk_id) { + case TLV320DAC33_MCLK: + ioc_reg |= DAC33_REFSEL; + asrcb_reg |= DAC33_SRCREFSEL; + break; + case TLV320DAC33_SLEEPCLK: + ioc_reg &= ~DAC33_REFSEL; + asrcb_reg &= ~DAC33_SRCREFSEL; + break; + default: + dev_err(codec->dev, "Invalid clock ID (%d)\n", clk_id); + break; + } + dac33->refclk = freq; + + dac33_write_reg_cache(codec, DAC33_INT_OSC_CTRL, ioc_reg); + dac33_write_reg_cache(codec, DAC33_ASRC_CTRL_B, asrcb_reg); + + return 0; +} + +static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u8 aictrl_a, aictrl_b; + + aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A); + aictrl_b = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B); + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + /* Codec Master */ + aictrl_a |= (DAC33_MSBCLK | DAC33_MSWCLK); + break; + case SND_SOC_DAIFMT_CBS_CFS: + /* Codec Slave */ + aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK); + break; + default: + return -EINVAL; + } + + aictrl_a &= ~DAC33_AFMT_MASK; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + aictrl_a |= DAC33_AFMT_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + aictrl_a |= DAC33_AFMT_DSP; + aictrl_b &= ~DAC33_DATA_DELAY_MASK; + aictrl_b |= DAC33_DATA_DELAY(1); /* 1 bit delay */ + break; + case SND_SOC_DAIFMT_DSP_B: + aictrl_a |= DAC33_AFMT_DSP; + aictrl_b &= ~DAC33_DATA_DELAY_MASK; /* No delay */ + break; + case SND_SOC_DAIFMT_RIGHT_J: + aictrl_a |= DAC33_AFMT_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + aictrl_a |= DAC33_AFMT_LEFT_J; + break; + default: + dev_err(codec->dev, "Unsupported format (%u)\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + dac33_write_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a); + dac33_write_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b); + + return 0; +} + +static void dac33_init_chip(struct snd_soc_codec *codec) +{ + /* 44-46: DAC Control Registers */ + /* A : DAC sample rate Fsref/1.5 */ + dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(1)); + /* B : DAC src=normal, not muted */ + dac33_write(codec, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT | + DAC33_DACSRCL_LEFT); + /* C : (defaults) */ + dac33_write(codec, DAC33_DAC_CTRL_C, 0x00); + + /* 64-65 : L&R DAC power control + Line In -> OUT 1V/V Gain, DAC -> OUT 4V/V Gain*/ + dac33_write(codec, DAC33_LDAC_PWR_CTRL, DAC33_LROUT_GAIN(2)); + dac33_write(codec, DAC33_RDAC_PWR_CTRL, DAC33_LROUT_GAIN(2)); + + /* 73 : volume soft stepping control, + clock source = internal osc (?) */ + dac33_write(codec, DAC33_ANA_VOL_SOFT_STEP_CTRL, DAC33_VOLCLKEN); + + /* 66 : LOP/LOM Modes */ + dac33_write(codec, DAC33_OUT_AMP_CM_CTRL, 0xff); + + /* 68 : LOM inverted from LOP */ + dac33_write(codec, DAC33_OUT_AMP_CTRL, (3<<2)); + + dac33_write(codec, DAC33_PWR_CTRL, DAC33_PDNALLB); +} + +static int dac33_soc_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + struct tlv320dac33_priv *dac33; + int ret = 0; + + BUG_ON(!tlv320dac33_codec); + + codec = tlv320dac33_codec; + socdev->card->codec = codec; + dac33 = codec->private_data; + + /* Power up the codec */ + dac33_hard_power(codec, 1); + /* Set default configuration */ + dac33_init_chip(codec); + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + dev_err(codec->dev, "failed to create pcms\n"); + goto pcm_err; + } + + snd_soc_add_controls(codec, dac33_snd_controls, + ARRAY_SIZE(dac33_snd_controls)); + /* Only add the nSample controls, if we have valid IRQ number */ + if (dac33->irq >= 0) + snd_soc_add_controls(codec, dac33_nsample_snd_controls, + ARRAY_SIZE(dac33_nsample_snd_controls)); + + dac33_add_widgets(codec); + + /* power on device */ + dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + ret = snd_soc_init_card(socdev); + if (ret < 0) { + dev_err(codec->dev, "failed to register card\n"); + goto card_err; + } + + return 0; +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + dac33_hard_power(codec, 0); + return ret; +} + +static int dac33_soc_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + dac33_set_bias_level(codec, SND_SOC_BIAS_OFF); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + + return 0; +} + +static int dac33_soc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + dac33_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int dac33_soc_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + dac33_set_bias_level(codec, codec->suspend_bias_level); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_tlv320dac33 = { + .probe = dac33_soc_probe, + .remove = dac33_soc_remove, + .suspend = dac33_soc_suspend, + .resume = dac33_soc_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320dac33); + +#define DAC33_RATES (SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) +#define DAC33_FORMATS SNDRV_PCM_FMTBIT_S16_LE + +static struct snd_soc_dai_ops dac33_dai_ops = { + .shutdown = dac33_shutdown, + .hw_params = dac33_hw_params, + .prepare = dac33_pcm_prepare, + .trigger = dac33_pcm_trigger, + .set_sysclk = dac33_set_dai_sysclk, + .set_fmt = dac33_set_dai_fmt, +}; + +struct snd_soc_dai dac33_dai = { + .name = "tlv320dac33", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = DAC33_RATES, + .formats = DAC33_FORMATS,}, + .ops = &dac33_dai_ops, +}; +EXPORT_SYMBOL_GPL(dac33_dai); + +static int dac33_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tlv320dac33_platform_data *pdata; + struct tlv320dac33_priv *dac33; + struct snd_soc_codec *codec; + int ret = 0; + + if (client->dev.platform_data == NULL) { + dev_err(&client->dev, "Platform data not set\n"); + return -ENODEV; + } + pdata = client->dev.platform_data; + + dac33 = kzalloc(sizeof(struct tlv320dac33_priv), GFP_KERNEL); + if (dac33 == NULL) + return -ENOMEM; + + codec = &dac33->codec; + codec->private_data = dac33; + codec->control_data = client; + + mutex_init(&codec->mutex); + mutex_init(&dac33->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + codec->name = "tlv320dac33"; + codec->owner = THIS_MODULE; + codec->read = dac33_read_reg_cache; + codec->write = dac33_write_locked; + codec->hw_write = (hw_write_t) i2c_master_send; + codec->bias_level = SND_SOC_BIAS_OFF; + codec->set_bias_level = dac33_set_bias_level; + codec->dai = &dac33_dai; + codec->num_dai = 1; + codec->reg_cache_size = ARRAY_SIZE(dac33_reg); + codec->reg_cache = kmemdup(dac33_reg, ARRAY_SIZE(dac33_reg), + GFP_KERNEL); + if (codec->reg_cache == NULL) { + ret = -ENOMEM; + goto error_reg; + } + + i2c_set_clientdata(client, dac33); + + dac33->power_gpio = pdata->power_gpio; + dac33->irq = client->irq; + dac33->nsample = NSAMPLE_MAX; + /* Disable FIFO use by default */ + dac33->nsample_switch = 0; + + tlv320dac33_codec = codec; + + codec->dev = &client->dev; + dac33_dai.dev = codec->dev; + + /* Check if the reset GPIO number is valid and request it */ + if (dac33->power_gpio >= 0) { + ret = gpio_request(dac33->power_gpio, "tlv320dac33 reset"); + if (ret < 0) { + dev_err(codec->dev, + "Failed to request reset GPIO (%d)\n", + dac33->power_gpio); + snd_soc_unregister_dai(&dac33_dai); + snd_soc_unregister_codec(codec); + goto error_gpio; + } + gpio_direction_output(dac33->power_gpio, 0); + } else { + dac33->chip_power = 1; + } + + /* Check if the IRQ number is valid and request it */ + if (dac33->irq >= 0) { + ret = request_irq(dac33->irq, dac33_interrupt_handler, + IRQF_TRIGGER_RISING | IRQF_DISABLED, + codec->name, codec); + if (ret < 0) { + dev_err(codec->dev, "Could not request IRQ%d (%d)\n", + dac33->irq, ret); + dac33->irq = -1; + } + if (dac33->irq != -1) { + /* Setup work queue */ + dac33->dac33_wq = create_rt_workqueue("tlv320dac33"); + if (dac33->dac33_wq == NULL) { + free_irq(dac33->irq, &dac33->codec); + ret = -ENOMEM; + goto error_wq; + } + + INIT_WORK(&dac33->work, dac33_work); + } + } + + ret = snd_soc_register_codec(codec); + if (ret != 0) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + goto error_codec; + } + + ret = snd_soc_register_dai(&dac33_dai); + if (ret != 0) { + dev_err(codec->dev, "Failed to register DAI: %d\n", ret); + snd_soc_unregister_codec(codec); + goto error_codec; + } + + /* Shut down the codec for now */ + dac33_hard_power(codec, 0); + + return ret; + +error_codec: + if (dac33->irq >= 0) { + free_irq(dac33->irq, &dac33->codec); + destroy_workqueue(dac33->dac33_wq); + } +error_wq: + if (dac33->power_gpio >= 0) + gpio_free(dac33->power_gpio); +error_gpio: + kfree(codec->reg_cache); +error_reg: + tlv320dac33_codec = NULL; + kfree(dac33); + + return ret; +} + +static int dac33_i2c_remove(struct i2c_client *client) +{ + struct tlv320dac33_priv *dac33; + + dac33 = i2c_get_clientdata(client); + dac33_hard_power(&dac33->codec, 0); + + if (dac33->power_gpio >= 0) + gpio_free(dac33->power_gpio); + if (dac33->irq >= 0) + free_irq(dac33->irq, &dac33->codec); + + destroy_workqueue(dac33->dac33_wq); + snd_soc_unregister_dai(&dac33_dai); + snd_soc_unregister_codec(&dac33->codec); + kfree(dac33->codec.reg_cache); + kfree(dac33); + tlv320dac33_codec = NULL; + + return 0; +} + +static const struct i2c_device_id tlv320dac33_i2c_id[] = { + { + .name = "tlv320dac33", + .driver_data = 0, + }, + { }, +}; + +static struct i2c_driver tlv320dac33_i2c_driver = { + .driver = { + .name = "tlv320dac33", + .owner = THIS_MODULE, + }, + .probe = dac33_i2c_probe, + .remove = __devexit_p(dac33_i2c_remove), + .id_table = tlv320dac33_i2c_id, +}; + +static int __init dac33_module_init(void) +{ + int r; + r = i2c_add_driver(&tlv320dac33_i2c_driver); + if (r < 0) { + printk(KERN_ERR "DAC33: driver registration failed\n"); + return r; + } + return 0; +} +module_init(dac33_module_init); + +static void __exit dac33_module_exit(void) +{ + i2c_del_driver(&tlv320dac33_i2c_driver); +} +module_exit(dac33_module_exit); + + +MODULE_DESCRIPTION("ASoC TLV320DAC33 codec driver"); +MODULE_AUTHOR("Peter Ujfalusi "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320dac33.h b/sound/soc/codecs/tlv320dac33.h new file mode 100644 index 00000000000..0fedd709028 --- /dev/null +++ b/sound/soc/codecs/tlv320dac33.h @@ -0,0 +1,267 @@ +/* + * ALSA SoC Texas Instruments TLV320DAC33 codec driver + * + * Author: Peter Ujfalusi + * + * Copyright: (C) 2009 Nokia Corporation + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __TLV320DAC33_H +#define __TLV320DAC33_H + +#define DAC33_PAGE_SELECT 0x00 +#define DAC33_PWR_CTRL 0x01 +#define DAC33_PLL_CTRL_A 0x02 +#define DAC33_PLL_CTRL_B 0x03 +#define DAC33_PLL_CTRL_C 0x04 +#define DAC33_PLL_CTRL_D 0x05 +#define DAC33_PLL_CTRL_E 0x06 +#define DAC33_INT_OSC_CTRL 0x07 +#define DAC33_INT_OSC_FREQ_RAT_A 0x08 +#define DAC33_INT_OSC_FREQ_RAT_B 0x09 +#define DAC33_INT_OSC_DAC_RATIO_SET 0x0A +#define DAC33_CALIB_TIME 0x0B +#define DAC33_INT_OSC_CTRL_B 0x0C +#define DAC33_INT_OSC_CTRL_C 0x0D +#define DAC33_INT_OSC_STATUS 0x0E +#define DAC33_INT_OSC_DAC_RATIO_READ 0x0F +#define DAC33_INT_OSC_FREQ_RAT_READ_A 0x10 +#define DAC33_INT_OSC_FREQ_RAT_READ_B 0x11 +#define DAC33_SER_AUDIOIF_CTRL_A 0x12 +#define DAC33_SER_AUDIOIF_CTRL_B 0x13 +#define DAC33_SER_AUDIOIF_CTRL_C 0x14 +#define DAC33_FIFO_CTRL_A 0x15 +#define DAC33_UTHR_MSB 0x16 +#define DAC33_UTHR_LSB 0x17 +#define DAC33_ATHR_MSB 0x18 +#define DAC33_ATHR_LSB 0x19 +#define DAC33_LTHR_MSB 0x1A +#define DAC33_LTHR_LSB 0x1B +#define DAC33_PREFILL_MSB 0x1C +#define DAC33_PREFILL_LSB 0x1D +#define DAC33_NSAMPLE_MSB 0x1E +#define DAC33_NSAMPLE_LSB 0x1F +#define DAC33_FIFO_WPTR_MSB 0x20 +#define DAC33_FIFO_WPTR_LSB 0x21 +#define DAC33_FIFO_RPTR_MSB 0x22 +#define DAC33_FIFO_RPTR_LSB 0x23 +#define DAC33_FIFO_DEPTH_MSB 0x24 +#define DAC33_FIFO_DEPTH_LSB 0x25 +#define DAC33_SAMPLES_REMAINING_MSB 0x26 +#define DAC33_SAMPLES_REMAINING_LSB 0x27 +#define DAC33_FIFO_IRQ_FLAG 0x28 +#define DAC33_FIFO_IRQ_MASK 0x29 +#define DAC33_FIFO_IRQ_MODE_A 0x2A +#define DAC33_FIFO_IRQ_MODE_B 0x2B +#define DAC33_DAC_CTRL_A 0x2C +#define DAC33_DAC_CTRL_B 0x2D +#define DAC33_DAC_CTRL_C 0x2E +#define DAC33_LDAC_DIG_VOL_CTRL 0x2F +#define DAC33_RDAC_DIG_VOL_CTRL 0x30 +#define DAC33_DAC_STATUS_FLAGS 0x31 +#define DAC33_ASRC_CTRL_A 0x32 +#define DAC33_ASRC_CTRL_B 0x33 +#define DAC33_SRC_REF_CLK_RATIO_A 0x34 +#define DAC33_SRC_REF_CLK_RATIO_B 0x35 +#define DAC33_SRC_EST_REF_CLK_RATIO_A 0x36 +#define DAC33_SRC_EST_REF_CLK_RATIO_B 0x37 +#define DAC33_INTP_CTRL_A 0x38 +#define DAC33_INTP_CTRL_B 0x39 +/* Registers 0x3A - 0x3F Reserved */ +#define DAC33_LDAC_PWR_CTRL 0x40 +#define DAC33_RDAC_PWR_CTRL 0x41 +#define DAC33_OUT_AMP_CM_CTRL 0x42 +#define DAC33_OUT_AMP_PWR_CTRL 0x43 +#define DAC33_OUT_AMP_CTRL 0x44 +#define DAC33_LINEL_TO_LLO_VOL 0x45 +/* Registers 0x45 - 0x47 Reserved */ +#define DAC33_LINER_TO_RLO_VOL 0x48 +#define DAC33_ANA_VOL_SOFT_STEP_CTRL 0x49 +#define DAC33_OSC_TRIM 0x4A +/* Registers 0x4B - 0x7C Reserved */ +#define DAC33_DEVICE_ID_MSB 0x7D +#define DAC33_DEVICE_ID_LSB 0x7E +#define DAC33_DEVICE_REV_ID 0x7F + +#define DAC33_CACHEREGNUM 128 + +/* Bit definitions */ + +/* DAC33_PWR_CTRL (0x01) */ +#define DAC33_DACRPDNB (0x01 << 0) +#define DAC33_DACLPDNB (0x01 << 1) +#define DAC33_OSCPDNB (0x01 << 2) +#define DAC33_PLLPDNB (0x01 << 3) +#define DAC33_PDNALLB (0x01 << 4) +#define DAC33_SOFT_RESET (0x01 << 7) + +/* DAC33_INT_OSC_CTRL (0x07) */ +#define DAC33_REFSEL (0x01 << 1) + +/* DAC33_INT_OSC_CTRL_B (0x0C) */ +#define DAC33_ADJSTEP(x) (x << 0) +#define DAC33_ADJTHRSHLD(x) (x << 4) + +/* DAC33_INT_OSC_CTRL_C (0x0D) */ +#define DAC33_REFDIV(x) (x << 4) + +/* DAC33_INT_OSC_STATUS (0x0E) */ +#define DAC33_OSCSTATUS_IDLE_CALIB (0x00) +#define DAC33_OSCSTATUS_NORMAL (0x01) +#define DAC33_OSCSTATUS_ADJUSTMENT (0x03) +#define DAC33_OSCSTATUS_NOT_USED (0x02) + +/* DAC33_SER_AUDIOIF_CTRL_A (0x12) */ +#define DAC33_MSWCLK (0x01 << 0) +#define DAC33_MSBCLK (0x01 << 1) +#define DAC33_AFMT_MASK (0x03 << 2) +#define DAC33_AFMT_I2S (0x00 << 2) +#define DAC33_AFMT_DSP (0x01 << 2) +#define DAC33_AFMT_RIGHT_J (0x02 << 2) +#define DAC33_AFMT_LEFT_J (0x03 << 2) +#define DAC33_WLEN_MASK (0x03 << 4) +#define DAC33_WLEN_16 (0x00 << 4) +#define DAC33_WLEN_20 (0x01 << 4) +#define DAC33_WLEN_24 (0x02 << 4) +#define DAC33_WLEN_32 (0x03 << 4) +#define DAC33_NCYCL_MASK (0x03 << 6) +#define DAC33_NCYCL_16 (0x00 << 6) +#define DAC33_NCYCL_20 (0x01 << 6) +#define DAC33_NCYCL_24 (0x02 << 6) +#define DAC33_NCYCL_32 (0x03 << 6) + +/* DAC33_SER_AUDIOIF_CTRL_B (0x13) */ +#define DAC33_DATA_DELAY_MASK (0x03 << 2) +#define DAC33_DATA_DELAY(x) (x << 2) +#define DAC33_BCLKON (0x01 << 5) + +/* DAC33_FIFO_CTRL_A (0x15) */ +#define DAC33_WIDTH (0x01 << 0) +#define DAC33_FBYPAS (0x01 << 1) +#define DAC33_FAUTO (0x01 << 2) +#define DAC33_FIFOFLUSH (0x01 << 3) + +/* + * UTHR, ATHR, LTHR, PREFILL, NSAMPLE (0x16 - 0x1F) + * 13-bit values +*/ +#define DAC33_THRREG(x) (((x) & 0x1FFF) << 3) + +/* DAC33_FIFO_IRQ_MASK (0x29) */ +#define DAC33_MNS (0x01 << 0) +#define DAC33_MPS (0x01 << 1) +#define DAC33_MAT (0x01 << 2) +#define DAC33_MLT (0x01 << 3) +#define DAC33_MUT (0x01 << 4) +#define DAC33_MUF (0x01 << 5) +#define DAC33_MOF (0x01 << 6) + +#define DAC33_FIFO_IRQ_MODE_MASK (0x03) +#define DAC33_FIFO_IRQ_MODE_RISING (0x00) +#define DAC33_FIFO_IRQ_MODE_FALLING (0x01) +#define DAC33_FIFO_IRQ_MODE_LEVEL (0x02) +#define DAC33_FIFO_IRQ_MODE_EDGE (0x03) + +/* DAC33_FIFO_IRQ_MODE_A (0x2A) */ +#define DAC33_UTM(x) (x << 0) +#define DAC33_UFM(x) (x << 2) +#define DAC33_OFM(x) (x << 4) + +/* DAC33_FIFO_IRQ_MODE_B (0x2B) */ +#define DAC33_NSM(x) (x << 0) +#define DAC33_PSM(x) (x << 2) +#define DAC33_ATM(x) (x << 4) +#define DAC33_LTM(x) (x << 4) + +/* DAC33_DAC_CTRL_A (0x2C) */ +#define DAC33_DACRATE(x) (x << 0) +#define DAC33_DACDUAL (0x01 << 4) +#define DAC33_DACLKSEL_MASK (0x03 << 5) +#define DAC33_DACLKSEL_INTSOC (0x00 << 5) +#define DAC33_DACLKSEL_PLL (0x01 << 5) +#define DAC33_DACLKSEL_MCLK (0x02 << 5) +#define DAC33_DACLKSEL_BCLK (0x03 << 5) + +/* DAC33_DAC_CTRL_B (0x2D) */ +#define DAC33_DACSRCR_MASK (0x03 << 0) +#define DAC33_DACSRCR_MUTE (0x00 << 0) +#define DAC33_DACSRCR_RIGHT (0x01 << 0) +#define DAC33_DACSRCR_LEFT (0x02 << 0) +#define DAC33_DACSRCR_MONOMIX (0x03 << 0) +#define DAC33_DACSRCL_MASK (0x03 << 2) +#define DAC33_DACSRCL_MUTE (0x00 << 2) +#define DAC33_DACSRCL_LEFT (0x01 << 2) +#define DAC33_DACSRCL_RIGHT (0x02 << 2) +#define DAC33_DACSRCL_MONOMIX (0x03 << 2) +#define DAC33_DVOLSTEP_MASK (0x03 << 4) +#define DAC33_DVOLSTEP_SS_PERFS (0x00 << 4) +#define DAC33_DVOLSTEP_SS_PER2FS (0x01 << 4) +#define DAC33_DVOLSTEP_SS_DISABLED (0x02 << 4) +#define DAC33_DVOLCTRL_MASK (0x03 << 6) +#define DAC33_DVOLCTRL_LR_INDEPENDENT1 (0x00 << 6) +#define DAC33_DVOLCTRL_LR_RIGHT_CONTROL (0x01 << 6) +#define DAC33_DVOLCTRL_LR_LEFT_CONTROL (0x02 << 6) +#define DAC33_DVOLCTRL_LR_INDEPENDENT2 (0x03 << 6) + +/* DAC33_DAC_CTRL_C (0x2E) */ +#define DAC33_DEEMENR (0x01 << 0) +#define DAC33_EFFENR (0x01 << 1) +#define DAC33_DEEMENL (0x01 << 2) +#define DAC33_EFFENL (0x01 << 3) +#define DAC33_EN3D (0x01 << 4) +#define DAC33_RESYNMUTE (0x01 << 5) +#define DAC33_RESYNEN (0x01 << 6) + +/* DAC33_ASRC_CTRL_A (0x32) */ +#define DAC33_SRCBYP (0x01 << 0) +#define DAC33_SRCLKSEL_MASK (0x03 << 1) +#define DAC33_SRCLKSEL_INTSOC (0x00 << 1) +#define DAC33_SRCLKSEL_PLL (0x01 << 1) +#define DAC33_SRCLKSEL_MCLK (0x02 << 1) +#define DAC33_SRCLKSEL_BCLK (0x03 << 1) +#define DAC33_SRCLKDIV(x) (x << 3) + +/* DAC33_ASRC_CTRL_B (0x33) */ +#define DAC33_SRCSETUP(x) (x << 0) +#define DAC33_SRCREFSEL (0x01 << 4) +#define DAC33_SRCREFDIV(x) (x << 5) + +/* DAC33_INTP_CTRL_A (0x38) */ +#define DAC33_INTPSEL (0x01 << 0) +#define DAC33_INTPM_MASK (0x03 << 1) +#define DAC33_INTPM_ALOW_OPENDRAIN (0x00 << 1) +#define DAC33_INTPM_ALOW (0x01 << 1) +#define DAC33_INTPM_AHIGH (0x02 << 1) + +/* DAC33_LDAC_PWR_CTRL (0x40) */ +/* DAC33_RDAC_PWR_CTRL (0x41) */ +#define DAC33_DACLRNUM (0x01 << 2) +#define DAC33_LROUT_GAIN(x) (x << 0) + +/* DAC33_ANA_VOL_SOFT_STEP_CTRL (0x49) */ +#define DAC33_VOLCLKSEL (0x01 << 0) +#define DAC33_VOLCLKEN (0x01 << 1) +#define DAC33_VOLBYPASS (0x01 << 2) + +#define TLV320DAC33_MCLK 0 +#define TLV320DAC33_SLEEPCLK 1 + +extern struct snd_soc_dai dac33_dai; +extern struct snd_soc_codec_device soc_codec_dev_tlv320dac33; + +#endif /* __TLV320DAC33_H */ -- cgit v1.2.3 From d8707cecdf396bdb506252829d03837b2c67c939 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 19 Oct 2009 15:42:19 +0300 Subject: ASoC: TWL4030: Only update the needed bits in *set_dai_sysclk Do not rewrite the whole register, but only update the needed bits in set_dai_sysclk functions. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 4df7c6c61c7..559e9b27928 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -1785,19 +1785,21 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; struct twl4030_priv *twl4030 = codec->private_data; - u8 infreq; + u8 apll_ctrl; + apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); + apll_ctrl &= ~TWL4030_APLL_INFREQ; switch (freq) { case 19200000: - infreq = TWL4030_APLL_INFREQ_19200KHZ; + apll_ctrl |= TWL4030_APLL_INFREQ_19200KHZ; twl4030->sysclk = 19200; break; case 26000000: - infreq = TWL4030_APLL_INFREQ_26000KHZ; + apll_ctrl |= TWL4030_APLL_INFREQ_26000KHZ; twl4030->sysclk = 26000; break; case 38400000: - infreq = TWL4030_APLL_INFREQ_38400KHZ; + apll_ctrl |= TWL4030_APLL_INFREQ_38400KHZ; twl4030->sysclk = 38400; break; default: @@ -1806,8 +1808,7 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, return -EINVAL; } - infreq |= TWL4030_APLL_EN; - twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq); + twl4030_write(codec, TWL4030_REG_APLL_CTL, apll_ctrl); return 0; } @@ -1989,11 +1990,13 @@ static int twl4030_voice_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; - u8 infreq; + u8 apll_ctrl; + apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); + apll_ctrl &= ~TWL4030_APLL_INFREQ; switch (freq) { case 26000000: - infreq = TWL4030_APLL_INFREQ_26000KHZ; + apll_ctrl |= TWL4030_APLL_INFREQ_26000KHZ; break; default: printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n", @@ -2001,8 +2004,7 @@ static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, return -EINVAL; } - infreq |= TWL4030_APLL_EN; - twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq); + twl4030_write(codec, TWL4030_REG_APLL_CTL, apll_ctrl); return 0; } -- cgit v1.2.3 From 017deee63934349a70292666acfedea8e6eb6eb8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 21 Oct 2009 09:58:35 +0300 Subject: ASoC: tlv320dac33: typo fix in the header Fix the definition of DAC33_LTM field, the LTM bits in FIFO_IRQ_MODE_B register are starting at bit 6. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320dac33.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/tlv320dac33.h b/sound/soc/codecs/tlv320dac33.h index 0fedd709028..eb8ae07f0bd 100644 --- a/sound/soc/codecs/tlv320dac33.h +++ b/sound/soc/codecs/tlv320dac33.h @@ -186,7 +186,7 @@ #define DAC33_NSM(x) (x << 0) #define DAC33_PSM(x) (x << 2) #define DAC33_ATM(x) (x << 4) -#define DAC33_LTM(x) (x << 4) +#define DAC33_LTM(x) (x << 6) /* DAC33_DAC_CTRL_A (0x2C) */ #define DAC33_DACRATE(x) (x << 0) -- cgit v1.2.3 From 1f0f9b67f98a873fca8288ccb7f2a0f3c8f34371 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 22 Oct 2009 13:26:47 +0300 Subject: ASoC: TWL4030: use the twl4030-codec.h for register descriptions Remove the register descriptions from the twl4030.h file and use the linux/mfd/twl4030-codec.h instead, which has the codec related register descriptions also. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.h | 242 ++------------------------------------------- 1 file changed, 6 insertions(+), 236 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h index 2b4bfa23f98..dd6396ec9c7 100644 --- a/sound/soc/codecs/twl4030.h +++ b/sound/soc/codecs/twl4030.h @@ -22,245 +22,13 @@ #ifndef __TWL4030_AUDIO_H__ #define __TWL4030_AUDIO_H__ -#define TWL4030_REG_CODEC_MODE 0x1 -#define TWL4030_REG_OPTION 0x2 -#define TWL4030_REG_UNKNOWN 0x3 -#define TWL4030_REG_MICBIAS_CTL 0x4 -#define TWL4030_REG_ANAMICL 0x5 -#define TWL4030_REG_ANAMICR 0x6 -#define TWL4030_REG_AVADC_CTL 0x7 -#define TWL4030_REG_ADCMICSEL 0x8 -#define TWL4030_REG_DIGMIXING 0x9 -#define TWL4030_REG_ATXL1PGA 0xA -#define TWL4030_REG_ATXR1PGA 0xB -#define TWL4030_REG_AVTXL2PGA 0xC -#define TWL4030_REG_AVTXR2PGA 0xD -#define TWL4030_REG_AUDIO_IF 0xE -#define TWL4030_REG_VOICE_IF 0xF -#define TWL4030_REG_ARXR1PGA 0x10 -#define TWL4030_REG_ARXL1PGA 0x11 -#define TWL4030_REG_ARXR2PGA 0x12 -#define TWL4030_REG_ARXL2PGA 0x13 -#define TWL4030_REG_VRXPGA 0x14 -#define TWL4030_REG_VSTPGA 0x15 -#define TWL4030_REG_VRX2ARXPGA 0x16 -#define TWL4030_REG_AVDAC_CTL 0x17 -#define TWL4030_REG_ARX2VTXPGA 0x18 -#define TWL4030_REG_ARXL1_APGA_CTL 0x19 -#define TWL4030_REG_ARXR1_APGA_CTL 0x1A -#define TWL4030_REG_ARXL2_APGA_CTL 0x1B -#define TWL4030_REG_ARXR2_APGA_CTL 0x1C -#define TWL4030_REG_ATX2ARXPGA 0x1D -#define TWL4030_REG_BT_IF 0x1E -#define TWL4030_REG_BTPGA 0x1F -#define TWL4030_REG_BTSTPGA 0x20 -#define TWL4030_REG_EAR_CTL 0x21 -#define TWL4030_REG_HS_SEL 0x22 -#define TWL4030_REG_HS_GAIN_SET 0x23 -#define TWL4030_REG_HS_POPN_SET 0x24 -#define TWL4030_REG_PREDL_CTL 0x25 -#define TWL4030_REG_PREDR_CTL 0x26 -#define TWL4030_REG_PRECKL_CTL 0x27 -#define TWL4030_REG_PRECKR_CTL 0x28 -#define TWL4030_REG_HFL_CTL 0x29 -#define TWL4030_REG_HFR_CTL 0x2A -#define TWL4030_REG_ALC_CTL 0x2B -#define TWL4030_REG_ALC_SET1 0x2C -#define TWL4030_REG_ALC_SET2 0x2D -#define TWL4030_REG_BOOST_CTL 0x2E -#define TWL4030_REG_SOFTVOL_CTL 0x2F -#define TWL4030_REG_DTMF_FREQSEL 0x30 -#define TWL4030_REG_DTMF_TONEXT1H 0x31 -#define TWL4030_REG_DTMF_TONEXT1L 0x32 -#define TWL4030_REG_DTMF_TONEXT2H 0x33 -#define TWL4030_REG_DTMF_TONEXT2L 0x34 -#define TWL4030_REG_DTMF_TONOFF 0x35 -#define TWL4030_REG_DTMF_WANONOFF 0x36 -#define TWL4030_REG_I2S_RX_SCRAMBLE_H 0x37 -#define TWL4030_REG_I2S_RX_SCRAMBLE_M 0x38 -#define TWL4030_REG_I2S_RX_SCRAMBLE_L 0x39 -#define TWL4030_REG_APLL_CTL 0x3A -#define TWL4030_REG_DTMF_CTL 0x3B -#define TWL4030_REG_DTMF_PGA_CTL2 0x3C -#define TWL4030_REG_DTMF_PGA_CTL1 0x3D -#define TWL4030_REG_MISC_SET_1 0x3E -#define TWL4030_REG_PCMBTMUX 0x3F -#define TWL4030_REG_RX_PATH_SEL 0x43 -#define TWL4030_REG_VDL_APGA_CTL 0x44 -#define TWL4030_REG_VIBRA_CTL 0x45 -#define TWL4030_REG_VIBRA_SET 0x46 -#define TWL4030_REG_VIBRA_PWM_SET 0x47 -#define TWL4030_REG_ANAMIC_GAIN 0x48 -#define TWL4030_REG_MISC_SET_2 0x49 -#define TWL4030_REG_SW_SHADOW 0x4A +/* Register descriptions are here */ +#include +/* Sgadow register used by the audio driver */ +#define TWL4030_REG_SW_SHADOW 0x4A #define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1) -/* Bitfield Definitions */ - -/* TWL4030_CODEC_MODE (0x01) Fields */ - -#define TWL4030_APLL_RATE 0xF0 -#define TWL4030_APLL_RATE_8000 0x00 -#define TWL4030_APLL_RATE_11025 0x10 -#define TWL4030_APLL_RATE_12000 0x20 -#define TWL4030_APLL_RATE_16000 0x40 -#define TWL4030_APLL_RATE_22050 0x50 -#define TWL4030_APLL_RATE_24000 0x60 -#define TWL4030_APLL_RATE_32000 0x80 -#define TWL4030_APLL_RATE_44100 0x90 -#define TWL4030_APLL_RATE_48000 0xA0 -#define TWL4030_APLL_RATE_96000 0xE0 -#define TWL4030_SEL_16K 0x08 -#define TWL4030_CODECPDZ 0x02 -#define TWL4030_OPT_MODE 0x01 -#define TWL4030_OPTION_1 (1 << 0) -#define TWL4030_OPTION_2 (0 << 0) - -/* TWL4030_OPTION (0x02) Fields */ - -#define TWL4030_ATXL1_EN (1 << 0) -#define TWL4030_ATXR1_EN (1 << 1) -#define TWL4030_ATXL2_VTXL_EN (1 << 2) -#define TWL4030_ATXR2_VTXR_EN (1 << 3) -#define TWL4030_ARXL1_VRX_EN (1 << 4) -#define TWL4030_ARXR1_EN (1 << 5) -#define TWL4030_ARXL2_EN (1 << 6) -#define TWL4030_ARXR2_EN (1 << 7) - -/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */ - -#define TWL4030_MICBIAS2_CTL 0x40 -#define TWL4030_MICBIAS1_CTL 0x20 -#define TWL4030_HSMICBIAS_EN 0x04 -#define TWL4030_MICBIAS2_EN 0x02 -#define TWL4030_MICBIAS1_EN 0x01 - -/* ANAMICL (0x05) Fields */ - -#define TWL4030_CNCL_OFFSET_START 0x80 -#define TWL4030_OFFSET_CNCL_SEL 0x60 -#define TWL4030_OFFSET_CNCL_SEL_ARX1 0x00 -#define TWL4030_OFFSET_CNCL_SEL_ARX2 0x20 -#define TWL4030_OFFSET_CNCL_SEL_VRX 0x40 -#define TWL4030_OFFSET_CNCL_SEL_ALL 0x60 -#define TWL4030_MICAMPL_EN 0x10 -#define TWL4030_CKMIC_EN 0x08 -#define TWL4030_AUXL_EN 0x04 -#define TWL4030_HSMIC_EN 0x02 -#define TWL4030_MAINMIC_EN 0x01 - -/* ANAMICR (0x06) Fields */ - -#define TWL4030_MICAMPR_EN 0x10 -#define TWL4030_AUXR_EN 0x04 -#define TWL4030_SUBMIC_EN 0x01 - -/* AVADC_CTL (0x07) Fields */ - -#define TWL4030_ADCL_EN 0x08 -#define TWL4030_AVADC_CLK_PRIORITY 0x04 -#define TWL4030_ADCR_EN 0x02 - -/* TWL4030_REG_ADCMICSEL (0x08) Fields */ - -#define TWL4030_DIGMIC1_EN 0x08 -#define TWL4030_TX2IN_SEL 0x04 -#define TWL4030_DIGMIC0_EN 0x02 -#define TWL4030_TX1IN_SEL 0x01 - -/* AUDIO_IF (0x0E) Fields */ - -#define TWL4030_AIF_SLAVE_EN 0x80 -#define TWL4030_DATA_WIDTH 0x60 -#define TWL4030_DATA_WIDTH_16S_16W 0x00 -#define TWL4030_DATA_WIDTH_32S_16W 0x40 -#define TWL4030_DATA_WIDTH_32S_24W 0x60 -#define TWL4030_AIF_FORMAT 0x18 -#define TWL4030_AIF_FORMAT_CODEC 0x00 -#define TWL4030_AIF_FORMAT_LEFT 0x08 -#define TWL4030_AIF_FORMAT_RIGHT 0x10 -#define TWL4030_AIF_FORMAT_TDM 0x18 -#define TWL4030_AIF_TRI_EN 0x04 -#define TWL4030_CLK256FS_EN 0x02 -#define TWL4030_AIF_EN 0x01 - -/* VOICE_IF (0x0F) Fields */ - -#define TWL4030_VIF_SLAVE_EN 0x80 -#define TWL4030_VIF_DIN_EN 0x40 -#define TWL4030_VIF_DOUT_EN 0x20 -#define TWL4030_VIF_SWAP 0x10 -#define TWL4030_VIF_FORMAT 0x08 -#define TWL4030_VIF_TRI_EN 0x04 -#define TWL4030_VIF_SUB_EN 0x02 -#define TWL4030_VIF_EN 0x01 - -/* EAR_CTL (0x21) */ -#define TWL4030_EAR_GAIN 0x30 - -/* HS_GAIN_SET (0x23) Fields */ - -#define TWL4030_HSR_GAIN 0x0C -#define TWL4030_HSR_GAIN_PWR_DOWN 0x00 -#define TWL4030_HSR_GAIN_PLUS_6DB 0x04 -#define TWL4030_HSR_GAIN_0DB 0x08 -#define TWL4030_HSR_GAIN_MINUS_6DB 0x0C -#define TWL4030_HSL_GAIN 0x03 -#define TWL4030_HSL_GAIN_PWR_DOWN 0x00 -#define TWL4030_HSL_GAIN_PLUS_6DB 0x01 -#define TWL4030_HSL_GAIN_0DB 0x02 -#define TWL4030_HSL_GAIN_MINUS_6DB 0x03 - -/* HS_POPN_SET (0x24) Fields */ - -#define TWL4030_VMID_EN 0x40 -#define TWL4030_EXTMUTE 0x20 -#define TWL4030_RAMP_DELAY 0x1C -#define TWL4030_RAMP_DELAY_20MS 0x00 -#define TWL4030_RAMP_DELAY_40MS 0x04 -#define TWL4030_RAMP_DELAY_81MS 0x08 -#define TWL4030_RAMP_DELAY_161MS 0x0C -#define TWL4030_RAMP_DELAY_323MS 0x10 -#define TWL4030_RAMP_DELAY_645MS 0x14 -#define TWL4030_RAMP_DELAY_1291MS 0x18 -#define TWL4030_RAMP_DELAY_2581MS 0x1C -#define TWL4030_RAMP_EN 0x02 - -/* PREDL_CTL (0x25) */ -#define TWL4030_PREDL_GAIN 0x30 - -/* PREDR_CTL (0x26) */ -#define TWL4030_PREDR_GAIN 0x30 - -/* PRECKL_CTL (0x27) */ -#define TWL4030_PRECKL_GAIN 0x30 - -/* PRECKR_CTL (0x28) */ -#define TWL4030_PRECKR_GAIN 0x30 - -/* HFL_CTL (0x29, 0x2A) Fields */ -#define TWL4030_HF_CTL_HB_EN 0x04 -#define TWL4030_HF_CTL_LOOP_EN 0x08 -#define TWL4030_HF_CTL_RAMP_EN 0x10 -#define TWL4030_HF_CTL_REF_EN 0x20 - -/* APLL_CTL (0x3A) Fields */ - -#define TWL4030_APLL_EN 0x10 -#define TWL4030_APLL_INFREQ 0x0F -#define TWL4030_APLL_INFREQ_19200KHZ 0x05 -#define TWL4030_APLL_INFREQ_26000KHZ 0x06 -#define TWL4030_APLL_INFREQ_38400KHZ 0x0F - -/* REG_MISC_SET_1 (0x3E) Fields */ - -#define TWL4030_CLK64_EN 0x80 -#define TWL4030_SCRAMBLE_EN 0x40 -#define TWL4030_FMLOOP_EN 0x20 -#define TWL4030_SMOOTH_ANAVOL_EN 0x02 -#define TWL4030_DIGMIC_LR_SWAP_EN 0x01 - /* TWL4030_REG_SW_SHADOW (0x4A) Fields */ #define TWL4030_HFL_EN 0x01 #define TWL4030_HFR_EN 0x02 @@ -279,3 +47,5 @@ struct twl4030_setup_data { }; #endif /* End of __TWL4030_AUDIO_H__ */ + + -- cgit v1.2.3 From 7a1fecf57f435e50ed86851cbb701f4b28e65135 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 22 Oct 2009 13:26:48 +0300 Subject: ASoC: TWL4030: Driver registration via twl4030_codec MFD Change the way how the twl4030 soc codec driver is loaded/probed. Use the device probing via tlw4030_codec MFD device. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/twl4030.c | 203 ++++++++++++++++++++++++++++----------------- 2 files changed, 127 insertions(+), 77 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index d30fce71cfe..3df3497335b 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -147,6 +147,7 @@ config SND_SOC_TLV320DAC33 tristate config SND_SOC_TWL4030 + select TWL4030_CODEC tristate config SND_SOC_UDA134X diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 559e9b27928..5c5a4c0a424 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -120,6 +120,8 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { /* codec private data */ struct twl4030_priv { + struct snd_soc_codec codec; + unsigned int bypass_state; unsigned int codec_powered; unsigned int codec_muted; @@ -183,19 +185,20 @@ static int twl4030_write(struct snd_soc_codec *codec, static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) { struct twl4030_priv *twl4030 = codec->private_data; - u8 mode; + int mode; if (enable == twl4030->codec_powered) return; - mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); if (enable) - mode |= TWL4030_CODECPDZ; + mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); else - mode &= ~TWL4030_CODECPDZ; + mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER); - twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); - twl4030->codec_powered = enable; + if (mode >= 0) { + twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode); + twl4030->codec_powered = enable; + } /* REVISIT: this delay is present in TI sample drivers */ /* but there seems to be no TRM requirement for it */ @@ -219,22 +222,20 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute) { struct twl4030_priv *twl4030 = codec->private_data; - u8 reg_val; + int status; if (mute == twl4030->codec_muted) return; - if (mute) { + if (mute) /* Disable PLL */ - reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); - reg_val &= ~TWL4030_APLL_EN; - twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); - } else { + status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL); + else /* Enable PLL */ - reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); - reg_val |= TWL4030_APLL_EN; - twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); - } + status = twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL); + + if (status >= 0) + twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); twl4030->codec_muted = mute; } @@ -2123,7 +2124,7 @@ struct snd_soc_dai twl4030_dai[] = { }; EXPORT_SYMBOL_GPL(twl4030_dai); -static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) +static int twl4030_soc_suspend(struct platform_device *pdev, pm_message_t state) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->card->codec; @@ -2133,7 +2134,7 @@ static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int twl4030_resume(struct platform_device *pdev) +static int twl4030_soc_resume(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->card->codec; @@ -2143,32 +2144,21 @@ static int twl4030_resume(struct platform_device *pdev) return 0; } -/* - * initialize the driver - * register the mixer and dsp interfaces with the kernel - */ +static struct snd_soc_codec *twl4030_codec; -static int twl4030_init(struct snd_soc_device *socdev) +static int twl4030_soc_probe(struct platform_device *pdev) { - struct snd_soc_codec *codec = socdev->card->codec; + struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct twl4030_setup_data *setup = socdev->codec_data; - struct twl4030_priv *twl4030 = codec->private_data; - int ret = 0; + struct snd_soc_codec *codec; + struct twl4030_priv *twl4030; + int ret; - printk(KERN_INFO "TWL4030 Audio Codec init \n"); + BUG_ON(!twl4030_codec); - codec->name = "twl4030"; - codec->owner = THIS_MODULE; - codec->read = twl4030_read_reg_cache; - codec->write = twl4030_write; - codec->set_bias_level = twl4030_set_bias_level; - codec->dai = twl4030_dai; - codec->num_dai = ARRAY_SIZE(twl4030_dai), - codec->reg_cache_size = sizeof(twl4030_reg); - codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg), - GFP_KERNEL); - if (codec->reg_cache == NULL) - return -ENOMEM; + codec = twl4030_codec; + twl4030 = codec->private_data; + socdev->card->codec = codec; /* Configuration for headset ramp delay from setup data */ if (setup) { @@ -2190,100 +2180,159 @@ static int twl4030_init(struct snd_soc_device *socdev) /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) { - printk(KERN_ERR "twl4030: failed to create pcms\n"); - goto pcm_err; + dev_err(&pdev->dev, "failed to create pcms\n"); + return ret; } - twl4030_init_chip(codec); - - /* power on device */ - twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_add_controls(codec, twl4030_snd_controls, ARRAY_SIZE(twl4030_snd_controls)); twl4030_add_widgets(codec); ret = snd_soc_init_card(socdev); if (ret < 0) { - printk(KERN_ERR "twl4030: failed to register card\n"); + dev_err(&pdev->dev, "failed to register card\n"); goto card_err; } - return ret; + return 0; card_err: snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); -pcm_err: - kfree(codec->reg_cache); + return ret; } -static struct snd_soc_device *twl4030_socdev; - -static int twl4030_probe(struct platform_device *pdev) +static int twl4030_soc_remove(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + kfree(codec->private_data); + kfree(codec); + + return 0; +} + +static int __devinit twl4030_codec_probe(struct platform_device *pdev) +{ + struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data; struct snd_soc_codec *codec; struct twl4030_priv *twl4030; + int ret; - codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); - if (codec == NULL) - return -ENOMEM; + if (!pdata || !(pdata->audio_mclk == 19200000 || + pdata->audio_mclk == 26000000 || + pdata->audio_mclk == 38400000)) { + dev_err(&pdev->dev, "Invalid platform_data\n"); + return -EINVAL; + } twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL); if (twl4030 == NULL) { - kfree(codec); + dev_err(&pdev->dev, "Can not allocate memroy\n"); return -ENOMEM; } + codec = &twl4030->codec; codec->private_data = twl4030; - socdev->card->codec = codec; + codec->dev = &pdev->dev; + twl4030_dai[0].dev = &pdev->dev; + twl4030_dai[1].dev = &pdev->dev; + mutex_init(&codec->mutex); INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); - twl4030_socdev = socdev; - twl4030_init(socdev); + codec->name = "twl4030"; + codec->owner = THIS_MODULE; + codec->read = twl4030_read_reg_cache; + codec->write = twl4030_write; + codec->set_bias_level = twl4030_set_bias_level; + codec->dai = twl4030_dai; + codec->num_dai = ARRAY_SIZE(twl4030_dai), + codec->reg_cache_size = sizeof(twl4030_reg); + codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg), + GFP_KERNEL); + if (codec->reg_cache == NULL) { + ret = -ENOMEM; + goto error_cache; + } + + platform_set_drvdata(pdev, twl4030); + twl4030_codec = codec; + + /* Set the defaults, and power up the codec */ + twl4030_init_chip(codec); + twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + ret = snd_soc_register_codec(codec); + if (ret != 0) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + goto error_codec; + } + + ret = snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); + if (ret != 0) { + dev_err(codec->dev, "Failed to register DAIs: %d\n", ret); + snd_soc_unregister_codec(codec); + goto error_codec; + } return 0; + +error_codec: + twl4030_power_down(codec); + kfree(codec->reg_cache); +error_cache: + kfree(twl4030); + return ret; } -static int twl4030_remove(struct platform_device *pdev) +static int __devexit twl4030_codec_remove(struct platform_device *pdev) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->card->codec; + struct twl4030_priv *twl4030 = platform_get_drvdata(pdev); - printk(KERN_INFO "TWL4030 Audio Codec remove\n"); - twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); - kfree(codec->private_data); - kfree(codec); + kfree(twl4030); + twl4030_codec = NULL; return 0; } -struct snd_soc_codec_device soc_codec_dev_twl4030 = { - .probe = twl4030_probe, - .remove = twl4030_remove, - .suspend = twl4030_suspend, - .resume = twl4030_resume, +MODULE_ALIAS("platform:twl4030_codec_audio"); + +static struct platform_driver twl4030_codec_driver = { + .probe = twl4030_codec_probe, + .remove = __devexit_p(twl4030_codec_remove), + .driver = { + .name = "twl4030_codec_audio", + .owner = THIS_MODULE, + }, }; -EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030); static int __init twl4030_modinit(void) { - return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); + return platform_driver_register(&twl4030_codec_driver); } module_init(twl4030_modinit); static void __exit twl4030_exit(void) { - snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); + platform_driver_unregister(&twl4030_codec_driver); } module_exit(twl4030_exit); +struct snd_soc_codec_device soc_codec_dev_twl4030 = { + .probe = twl4030_soc_probe, + .remove = twl4030_soc_remove, + .suspend = twl4030_soc_suspend, + .resume = twl4030_soc_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030); + MODULE_DESCRIPTION("ASoC TWL4030 codec driver"); MODULE_AUTHOR("Steve Sakoman"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 7dea7c01dac9b74faa9afa93fc9bb5f2d37521dc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 26 Oct 2009 15:20:17 +0000 Subject: ASoC: Add regulator support for WM8731 Signed-off-by: Mark Brown --- sound/soc/codecs/wm8731.c | 51 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 0e59219a59f..bb95af95097 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -33,9 +34,18 @@ static struct snd_soc_codec *wm8731_codec; struct snd_soc_codec_device soc_codec_dev_wm8731; +#define WM8731_NUM_SUPPLIES 4 +static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { + "AVDD", + "HPVDD", + "DCVDD", + "DBVDD", +}; + /* codec private data */ struct wm8731_priv { struct snd_soc_codec codec; + struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; u16 reg_cache[WM8731_CACHEREGNUM]; unsigned int sysclk; }; @@ -422,9 +432,12 @@ static int wm8731_suspend(struct platform_device *pdev, pm_message_t state) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->card->codec; + struct wm8731_priv *wm8731 = codec->private_data; snd_soc_write(codec, WM8731_ACTIVE, 0x0); wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); + regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); return 0; } @@ -432,10 +445,16 @@ static int wm8731_resume(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->card->codec; - int i; + struct wm8731_priv *wm8731 = codec->private_data; + int i, ret; u8 data[2]; u16 *cache = codec->reg_cache; + ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + if (ret != 0) + return ret; + /* Sync reg_cache with the hardware */ for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) { data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); @@ -444,6 +463,7 @@ static int wm8731_resume(struct platform_device *pdev) } wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); wm8731_set_bias_level(codec, codec->suspend_bias_level); + return 0; } #else @@ -512,7 +532,7 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731); static int wm8731_register(struct wm8731_priv *wm8731, enum snd_soc_control_type control) { - int ret; + int ret, i; struct snd_soc_codec *codec = &wm8731->codec; if (wm8731_codec) { @@ -543,10 +563,27 @@ static int wm8731_register(struct wm8731_priv *wm8731, goto err; } + for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) + wm8731->supplies[i].supply = wm8731_supply_names[i]; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + goto err; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + goto err_regulator_get; + } + ret = wm8731_reset(codec); if (ret < 0) { dev_err(codec->dev, "Failed to issue reset: %d\n", ret); - goto err; + goto err_regulator_enable; } wm8731_dai.dev = codec->dev; @@ -567,7 +604,7 @@ static int wm8731_register(struct wm8731_priv *wm8731, ret = snd_soc_register_codec(codec); if (ret != 0) { dev_err(codec->dev, "Failed to register codec: %d\n", ret); - goto err; + goto err_regulator_enable; } ret = snd_soc_register_dai(&wm8731_dai); @@ -581,6 +618,10 @@ static int wm8731_register(struct wm8731_priv *wm8731, err_codec: snd_soc_unregister_codec(codec); +err_regulator_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); +err_regulator_get: + regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); err: kfree(wm8731); return ret; @@ -591,6 +632,8 @@ static void wm8731_unregister(struct wm8731_priv *wm8731) wm8731_set_bias_level(&wm8731->codec, SND_SOC_BIAS_OFF); snd_soc_unregister_dai(&wm8731_dai); snd_soc_unregister_codec(&wm8731->codec); + regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); + regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); kfree(wm8731); wm8731_codec = NULL; } -- cgit v1.2.3 From 78e08e2f209e5e7777e81919d32cfcddad126cfa Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 28 Oct 2009 10:57:04 +0200 Subject: ASoC: TWL4030: Remove bypass tracking Since ASoC core now handling the codec bias differently there is no need to do the tracking of bypass switch states anymore. Handling of the common bit for analog loopbacks is done with DAPM_SUPPLY for the bypass paths. Now this bit is only enabled when there is a complete analog bypass path, compared to the previous implementation, when the global switch was enabled if there were any of the analog bypass switch was on (regardless if there were complete path or not) Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 128 +++++++++++---------------------------------- 1 file changed, 30 insertions(+), 98 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 5c5a4c0a424..24002269f03 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -122,7 +122,6 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { struct twl4030_priv { struct snd_soc_codec codec; - unsigned int bypass_state; unsigned int codec_powered; unsigned int codec_muted; @@ -725,67 +724,6 @@ static int headsetrpga_event(struct snd_soc_dapm_widget *w, return 0; } -static int bypass_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct soc_mixer_control *m = - (struct soc_mixer_control *)w->kcontrols->private_value; - struct twl4030_priv *twl4030 = w->codec->private_data; - unsigned char reg, misc; - - reg = twl4030_read_reg_cache(w->codec, m->reg); - - /* - * bypass_state[0:3] - analog HiFi bypass - * bypass_state[4] - analog voice bypass - * bypass_state[5] - digital voice bypass - * bypass_state[6:7] - digital HiFi bypass - */ - if (m->reg == TWL4030_REG_VSTPGA) { - /* Voice digital bypass */ - if (reg) - twl4030->bypass_state |= (1 << 5); - else - twl4030->bypass_state &= ~(1 << 5); - } else if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) { - /* Analog bypass */ - if (reg & (1 << m->shift)) - twl4030->bypass_state |= - (1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); - else - twl4030->bypass_state &= - ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); - } else if (m->reg == TWL4030_REG_VDL_APGA_CTL) { - /* Analog voice bypass */ - if (reg & (1 << m->shift)) - twl4030->bypass_state |= (1 << 4); - else - twl4030->bypass_state &= ~(1 << 4); - } else { - /* Digital bypass */ - if (reg & (0x7 << m->shift)) - twl4030->bypass_state |= (1 << (m->shift ? 7 : 6)); - else - twl4030->bypass_state &= ~(1 << (m->shift ? 7 : 6)); - } - - /* Enable master analog loopback mode if any analog switch is enabled*/ - misc = twl4030_read_reg_cache(w->codec, TWL4030_REG_MISC_SET_1); - if (twl4030->bypass_state & 0x1F) - misc |= TWL4030_FMLOOP_EN; - else - misc &= ~TWL4030_FMLOOP_EN; - twl4030_write(w->codec, TWL4030_REG_MISC_SET_1, misc); - - if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) { - if (twl4030->bypass_state) - twl4030_codec_mute(w->codec, 0); - else - twl4030_codec_mute(w->codec, 1); - } - return 0; -} - /* * Some of the gain controls in TWL (mostly those which are associated with * the outputs) are implemented in an interesting way: @@ -1193,32 +1131,28 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_NOPM, 0, 0), /* Analog bypasses */ - SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_abypassr1_control, bypass_event, - SND_SOC_DAPM_POST_REG), - SND_SOC_DAPM_SWITCH_E("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_abypassl1_control, - bypass_event, SND_SOC_DAPM_POST_REG), - SND_SOC_DAPM_SWITCH_E("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_abypassr2_control, - bypass_event, SND_SOC_DAPM_POST_REG), - SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_abypassl2_control, - bypass_event, SND_SOC_DAPM_POST_REG), - SND_SOC_DAPM_SWITCH_E("Voice Analog Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_abypassv_control, - bypass_event, SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassr1_control), + SND_SOC_DAPM_SWITCH("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassl1_control), + SND_SOC_DAPM_SWITCH("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassr2_control), + SND_SOC_DAPM_SWITCH("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassl2_control), + SND_SOC_DAPM_SWITCH("Voice Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassv_control), + + /* Master analog loopback switch */ + SND_SOC_DAPM_SUPPLY("FM Loop Enable", TWL4030_REG_MISC_SET_1, 5, 0, + NULL, 0), /* Digital bypasses */ - SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_dbypassl_control, bypass_event, - SND_SOC_DAPM_POST_REG), - SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_dbypassr_control, bypass_event, - SND_SOC_DAPM_POST_REG), - SND_SOC_DAPM_SWITCH_E("Voice Digital Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_dbypassv_control, bypass_event, - SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_SWITCH("Left Digital Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_dbypassl_control), + SND_SOC_DAPM_SWITCH("Right Digital Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_dbypassr_control), + SND_SOC_DAPM_SWITCH("Voice Digital Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_dbypassv_control), /* Digital mixers, power control for the physical DACs */ SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer", @@ -1490,6 +1424,13 @@ static const struct snd_soc_dapm_route intercon[] = { {"Left2 Analog Loopback", "Switch", "Analog Left"}, {"Voice Analog Loopback", "Switch", "Analog Left"}, + /* Supply for the Analog loopbacks */ + {"Right1 Analog Loopback", NULL, "FM Loop Enable"}, + {"Left1 Analog Loopback", NULL, "FM Loop Enable"}, + {"Right2 Analog Loopback", NULL, "FM Loop Enable"}, + {"Left2 Analog Loopback", NULL, "FM Loop Enable"}, + {"Voice Analog Loopback", NULL, "FM Loop Enable"}, + {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"}, {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"}, {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"}, @@ -1521,25 +1462,16 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec) static int twl4030_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - struct twl4030_priv *twl4030 = codec->private_data; - switch (level) { case SND_SOC_BIAS_ON: twl4030_codec_mute(codec, 0); break; case SND_SOC_BIAS_PREPARE: - twl4030_power_up(codec); - if (twl4030->bypass_state) - twl4030_codec_mute(codec, 0); - else - twl4030_codec_mute(codec, 1); break; case SND_SOC_BIAS_STANDBY: - twl4030_power_up(codec); - if (twl4030->bypass_state) - twl4030_codec_mute(codec, 0); - else - twl4030_codec_mute(codec, 1); + if (codec->bias_level == SND_SOC_BIAS_OFF) + twl4030_power_up(codec); + twl4030_codec_mute(codec, 1); break; case SND_SOC_BIAS_OFF: twl4030_power_down(codec); -- cgit v1.2.3 From 2845fa13e5cbe708ece7fafe29c91f32c66e4f59 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 28 Oct 2009 10:57:05 +0200 Subject: ASoC: TWL4030: Change codec_muted to apll_enabled codec_muted is missleading, change it to apll_enabled, which is what it is doing: enabing and disabling the APLL. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 24002269f03..9163713a030 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -123,7 +123,7 @@ struct twl4030_priv { struct snd_soc_codec codec; unsigned int codec_powered; - unsigned int codec_muted; + unsigned int apll_enabled; struct snd_pcm_substream *master_substream; struct snd_pcm_substream *slave_substream; @@ -218,25 +218,25 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) } -static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute) +static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) { struct twl4030_priv *twl4030 = codec->private_data; int status; - if (mute == twl4030->codec_muted) + if (enable == twl4030->apll_enabled) return; - if (mute) - /* Disable PLL */ - status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL); - else + if (enable) /* Enable PLL */ status = twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL); + else + /* Disable PLL */ + status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL); if (status >= 0) twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); - twl4030->codec_muted = mute; + twl4030->apll_enabled = enable; } static void twl4030_power_up(struct snd_soc_codec *codec) @@ -1464,14 +1464,14 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec, { switch (level) { case SND_SOC_BIAS_ON: - twl4030_codec_mute(codec, 0); + twl4030_apll_enable(codec, 1); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: if (codec->bias_level == SND_SOC_BIAS_OFF) twl4030_power_up(codec); - twl4030_codec_mute(codec, 1); + twl4030_apll_enable(codec, 0); break; case SND_SOC_BIAS_OFF: twl4030_power_down(codec); -- cgit v1.2.3 From 7e1aa1dcd0d886df72586e3a94b1a7382952f21f Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 29 Oct 2009 02:24:32 +0100 Subject: ASoC: CS4270: export de-emphasis filter as ALSA control The CS4270 codec features an de-emphasis filter for compensation of audio material filtered by an 50/15 uS algorithm. Not sure whether we should always enable it for 44100Hz sampling frequency, but it should at least be configurable by the user. Signed-off-by: Daniel Mack Acked-by: Timur Tabi Signed-off-by: Mark Brown --- sound/soc/codecs/cs4270.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 59bb16d033d..565842dcfc6 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -520,6 +520,7 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = { SOC_SINGLE("Digital Sidetone Switch", CS4270_FORMAT, 5, 1, 0), SOC_SINGLE("Soft Ramp Switch", CS4270_TRANS, 6, 1, 0), SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0), + SOC_SINGLE("De-emphasis filter", CS4270_TRANS, 0, 1, 0), SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1), SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0), SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1), -- cgit v1.2.3 From 86139a13ced74b3911c33940f0049b8f97bae07a Mon Sep 17 00:00:00 2001 From: Jari Vanhala Date: Thu, 29 Oct 2009 11:58:09 +0200 Subject: ASoC: TWL4030: Vibra motor stop fix when it is driven with audio This patch fixes vibrator playing incoherently, when driven with audio. There is something wrong in switch 3 at H-bridge and VIBRA_SET still affects PWM generator. Slowest value fixes things. Signed-off-by: Jari Vanhala Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 9163713a030..ccaeb366eb7 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -613,6 +613,13 @@ static int handsfreerpga_event(struct snd_soc_dapm_widget *w, return 0; } +static int vibramux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + twl4030_write(w->codec, TWL4030_REG_VIBRA_SET, 0xff); + return 0; +} + static void headset_ramp(struct snd_soc_codec *codec, int ramp) { struct snd_soc_device *socdev = codec->socdev; @@ -1243,8 +1250,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { 0, 0, NULL, 0, handsfreerpga_event, SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), /* Vibra */ - SND_SOC_DAPM_MUX("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0, - &twl4030_dapm_vibra_control), + SND_SOC_DAPM_MUX_E("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0, + &twl4030_dapm_vibra_control, vibramux_event, + SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0, &twl4030_dapm_vibrapath_control), -- cgit v1.2.3 From 7729cf749350b04c80ee1652961de238afc9d5b1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 29 Oct 2009 11:58:10 +0200 Subject: ASoC: TWL4030: Change APLL powering sequence It seams that certain part of the twl4030 codec needs the APLL enabled before they are enabled. Paths which has any digital processing needs need the APLL enabled before they can function. For example the vibra output will have some random data after it is enabled and before the APLL also enabled. If only analog components are in use (analog bypass), than it seams, that the APLL does not need to be enabled. This lowers the power consumption with around ~0.005A. Adding DAPM_SUPPLY to the Digital playback route and also to the capture route to enable and disable the APLL. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index ccaeb366eb7..277e99ce555 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -620,6 +620,20 @@ static int vibramux_event(struct snd_soc_dapm_widget *w, return 0; } +static int apll_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + twl4030_apll_enable(w->codec, 1); + break; + case SND_SOC_DAPM_POST_PMD: + twl4030_apll_enable(w->codec, 0); + break; + } + return 0; +} + static void headset_ramp(struct snd_soc_codec *codec, int ramp) { struct snd_soc_device *socdev = codec->socdev; @@ -1185,6 +1199,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer", TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event, + SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), + /* Output MIXER controls */ /* Earpiece */ SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0, @@ -1312,6 +1329,13 @@ static const struct snd_soc_dapm_route intercon[] = { {"Digital R2 Playback Mixer", NULL, "DAC Right2"}, {"Digital Voice Playback Mixer", NULL, "DAC Voice"}, + /* Supply for the digital part (APLL) */ + {"Digital R1 Playback Mixer", NULL, "APLL Enable"}, + {"Digital L1 Playback Mixer", NULL, "APLL Enable"}, + {"Digital R2 Playback Mixer", NULL, "APLL Enable"}, + {"Digital L2 Playback Mixer", NULL, "APLL Enable"}, + {"Digital Voice Playback Mixer", NULL, "APLL Enable"}, + {"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"}, {"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"}, {"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"}, @@ -1472,14 +1496,12 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec, { switch (level) { case SND_SOC_BIAS_ON: - twl4030_apll_enable(codec, 1); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: if (codec->bias_level == SND_SOC_BIAS_OFF) twl4030_power_up(codec); - twl4030_apll_enable(codec, 0); break; case SND_SOC_BIAS_OFF: twl4030_power_down(codec); -- cgit v1.2.3 From 1c3d20027133f145523a072e84ab55d9132920c9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 29 Oct 2009 13:05:52 +0200 Subject: ASoC: TWL4030: Add APLL supply for the capture path Capture path also need the APLL enabled, adding DAPM_SUPPLY for the Virtual ADCs. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 277e99ce555..f9121ef7fe5 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -1449,6 +1449,11 @@ static const struct snd_soc_dapm_route intercon[] = { {"ADC Virtual Left2", NULL, "TX2 Capture Route"}, {"ADC Virtual Right2", NULL, "TX2 Capture Route"}, + {"ADC Virtual Left1", NULL, "APLL Enable"}, + {"ADC Virtual Right1", NULL, "APLL Enable"}, + {"ADC Virtual Left2", NULL, "APLL Enable"}, + {"ADC Virtual Right2", NULL, "APLL Enable"}, + /* Analog bypass routes */ {"Right1 Analog Loopback", "Switch", "Analog Right"}, {"Left1 Analog Loopback", "Switch", "Analog Left"}, -- cgit v1.2.3 From 89933dee5b17c09f2673c2bfd853625a848f91f5 Mon Sep 17 00:00:00 2001 From: Neil Jones Date: Mon, 2 Nov 2009 15:14:17 +0000 Subject: ASoC: Add support for the WM8727 DAC. Add support for the Wolfson Microelectronics WM8727 DAC, this is a simple non-configurable DAC. Signed-off-by: Neil Jones Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 ++ sound/soc/codecs/Makefile | 2 + sound/soc/codecs/wm8727.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm8727.h | 21 +++++++ 4 files changed, 170 insertions(+) create mode 100644 sound/soc/codecs/wm8727.c create mode 100644 sound/soc/codecs/wm8727.h (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3df3497335b..4a3e8dcf24d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -40,6 +40,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8523 if I2C select SND_SOC_WM8580 if I2C select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI + select SND_SOC_WM8727 select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI @@ -174,6 +175,9 @@ config SND_SOC_WM8580 config SND_SOC_WM8711 tristate +config SND_SOC_WM8727 + tristate + config SND_SOC_WM8728 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 8f519ee9600..cacfc7692d7 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -27,6 +27,7 @@ snd-soc-wm8510-objs := wm8510.o snd-soc-wm8523-objs := wm8523.o snd-soc-wm8580-objs := wm8580.o snd-soc-wm8711-objs := wm8711.o +snd-soc-wm8727-objs := wm8727.o snd-soc-wm8728-objs := wm8728.o snd-soc-wm8731-objs := wm8731.o snd-soc-wm8750-objs := wm8750.o @@ -81,6 +82,7 @@ obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o +obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c new file mode 100644 index 00000000000..b3b60dd7bc1 --- /dev/null +++ b/sound/soc/codecs/wm8727.c @@ -0,0 +1,143 @@ +/* + * wm8727.c + * + * Created on: 15-Oct-2009 + * Author: neil.jones@imgtec.com + * + * Copyright (C) 2009 Imagination Technologies Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8727.h" +/* + * Note this is a simple chip with no configuration interface, sample rate is + * determined automatically by examining the Master clock and Bit clock ratios + */ +#define WM8727_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_192000) + + +struct snd_soc_dai wm8727_dai = { + .name = "WM8727", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM8727_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}; +EXPORT_SYMBOL_GPL(wm8727_dai); + +static int wm8727_soc_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + mutex_init(&codec->mutex); + codec->name = "WM8727"; + codec->owner = THIS_MODULE; + codec->dai = &wm8727_dai; + codec->num_dai = 1; + socdev->card->codec = codec; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "wm8727: failed to create pcms\n"); + goto pcm_err; + } + /* register card */ + ret = snd_soc_init_card(socdev); + if (ret < 0) { + printk(KERN_ERR "wm8727: failed to register card\n"); + goto register_err; + } + + return ret; + +register_err: + snd_soc_free_pcms(socdev); +pcm_err: + kfree(socdev->card->codec); + socdev->card->codec = NULL; + return ret; +} + +static int wm8727_soc_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + if (codec == NULL) + return 0; + snd_soc_free_pcms(socdev); + kfree(codec); + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm8727 = { + .probe = wm8727_soc_probe, + .remove = wm8727_soc_remove, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8727); + + +static __devinit int wm8727_platform_probe(struct platform_device *pdev) +{ + wm8727_dai.dev = &pdev->dev; + return snd_soc_register_dai(&wm8727_dai); +} + +static int __devexit wm8727_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_dai(&wm8727_dai); + return 0; +} + +struct platform_driver wm8727_codec_driver = { + .driver = { + .name = "wm8727-codec", + .owner = THIS_MODULE, + }, + + .probe = wm8727_platform_probe, + .remove = __devexit_p(wm8727_platform_remove), +}; + +static int __init wm8727_init(void) +{ + return platform_driver_register(&wm8727_codec_driver); +} +module_init(wm8727_init); + +static void __exit wm8727_exit(void) +{ + platform_driver_unregister(&wm8727_codec_driver); +} +module_exit(wm8727_exit); + +MODULE_DESCRIPTION("ASoC wm8727 driver"); +MODULE_AUTHOR("Neil Jones"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8727.h b/sound/soc/codecs/wm8727.h new file mode 100644 index 00000000000..ee19aa71bcd --- /dev/null +++ b/sound/soc/codecs/wm8727.h @@ -0,0 +1,21 @@ +/* + * wm8727.h + * + * Created on: 15-Oct-2009 + * Author: neil.jones@imgtec.com + * + * Copyright (C) 2009 Imagination Technologies Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef WM8727_H_ +#define WM8727_H_ + +extern struct snd_soc_dai wm8727_dai; +extern struct snd_soc_codec_device soc_codec_dev_wm8727; + +#endif /* WM8727_H_ */ -- cgit v1.2.3 From b3f5a272a33ef06a37cd44703c46ec916b8a1c93 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 2 Nov 2009 14:34:54 +0200 Subject: ASoC: TWL4030: Make sure, that the codec is powered on startup Set the codec->bias_level to SND_SOC_BIAS_OFF before changing the initial bias level to STANDBY. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index f9121ef7fe5..c0b47dfc332 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -2234,6 +2234,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) /* Set the defaults, and power up the codec */ twl4030_init_chip(codec); + codec->bias_level = SND_SOC_BIAS_OFF; twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); ret = snd_soc_register_codec(codec); -- cgit v1.2.3 From 529697c5463d941445db18e9526e7fc76a18e503 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 3 Nov 2009 22:13:30 +0000 Subject: ASoC: Staticise wm8727 driver structure Signed-off-by: Mark Brown --- sound/soc/codecs/wm8727.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c index b3b60dd7bc1..7df5a17eb73 100644 --- a/sound/soc/codecs/wm8727.c +++ b/sound/soc/codecs/wm8727.c @@ -116,7 +116,7 @@ static int __devexit wm8727_platform_remove(struct platform_device *pdev) return 0; } -struct platform_driver wm8727_codec_driver = { +static struct platform_driver wm8727_codec_driver = { .driver = { .name = "wm8727-codec", .owner = THIS_MODULE, -- cgit v1.2.3 From fe3e78e073d25308756f38019956061153267769 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 3 Nov 2009 22:13:13 +0000 Subject: ASoC: Factor out snd_soc_init_card() snd_soc_init_card() is always called as the last part of the CODEC probe function so we can factor it out into the core card setup rather than have each CODEC replicate the code to do the initialiastation. This will be required to support multiple CODECs per card. Signed-off-by: Mark Brown --- sound/soc/codecs/ac97.c | 3 --- sound/soc/codecs/ad1836.c | 6 ------ sound/soc/codecs/ad1938.c | 6 ------ sound/soc/codecs/ad1980.c | 5 ----- sound/soc/codecs/ad73311.c | 8 -------- sound/soc/codecs/ak4104.c | 8 -------- sound/soc/codecs/ak4535.c | 8 -------- sound/soc/codecs/ak4642.c | 9 --------- sound/soc/codecs/ak4671.c | 9 --------- sound/soc/codecs/cs4270.c | 7 ------- sound/soc/codecs/cx20442.c | 6 ------ sound/soc/codecs/pcm3008.c | 9 --------- sound/soc/codecs/ssm2602.c | 8 -------- sound/soc/codecs/stac9766.c | 3 --- sound/soc/codecs/tlv320aic23.c | 8 -------- sound/soc/codecs/tlv320aic26.c | 11 ----------- sound/soc/codecs/tlv320aic3x.c | 10 ---------- sound/soc/codecs/tlv320dac33.c | 10 +--------- sound/soc/codecs/twl4030.c | 12 ------------ sound/soc/codecs/uda134x.c | 9 --------- sound/soc/codecs/uda1380.c | 8 -------- sound/soc/codecs/wm8350.c | 11 ----------- sound/soc/codecs/wm8400.c | 6 ------ sound/soc/codecs/wm8510.c | 9 +-------- sound/soc/codecs/wm8523.c | 8 -------- sound/soc/codecs/wm8580.c | 8 -------- sound/soc/codecs/wm8711.c | 8 -------- sound/soc/codecs/wm8727.c | 8 -------- sound/soc/codecs/wm8728.c | 8 -------- sound/soc/codecs/wm8731.c | 8 -------- sound/soc/codecs/wm8750.c | 8 -------- sound/soc/codecs/wm8753.c | 9 --------- sound/soc/codecs/wm8776.c | 9 --------- sound/soc/codecs/wm8900.c | 6 ------ sound/soc/codecs/wm8903.c | 9 --------- sound/soc/codecs/wm8940.c | 6 ------ sound/soc/codecs/wm8960.c | 8 -------- sound/soc/codecs/wm8961.c | 9 --------- sound/soc/codecs/wm8971.c | 9 +-------- sound/soc/codecs/wm8974.c | 8 -------- sound/soc/codecs/wm8988.c | 9 --------- sound/soc/codecs/wm8990.c | 9 +-------- sound/soc/codecs/wm8993.c | 9 --------- sound/soc/codecs/wm9081.c | 9 --------- sound/soc/codecs/wm9705.c | 8 -------- sound/soc/codecs/wm9712.c | 8 -------- sound/soc/codecs/wm9713.c | 7 +------ 47 files changed, 5 insertions(+), 372 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 932299bb5d1..69bd0acc81c 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -117,9 +117,6 @@ static int ac97_soc_probe(struct platform_device *pdev) if (ret < 0) goto bus_err; - ret = snd_soc_init_card(socdev); - if (ret < 0) - goto bus_err; return 0; bus_err: diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index c48485f2c55..2e360c24307 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -387,12 +387,6 @@ static int ad1836_probe(struct platform_device *pdev) snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); snd_soc_dapm_new_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } - return ret; card_err: diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c index 34b30efc3cb..09c008ad147 100644 --- a/sound/soc/codecs/ad1938.c +++ b/sound/soc/codecs/ad1938.c @@ -596,12 +596,6 @@ static int ad1938_probe(struct platform_device *pdev) ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } - return ret; card_err: diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index d7440a982d2..39c0f7584e6 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -257,11 +257,6 @@ static int ad1980_soc_probe(struct platform_device *pdev) snd_soc_add_controls(codec, ad1980_snd_ac97_controls, ARRAY_SIZE(ad1980_snd_ac97_controls)); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "ad1980: failed to register card\n"); - goto reset_err; - } return 0; diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c index e61dac5e7b8..d2fcc601722 100644 --- a/sound/soc/codecs/ad73311.c +++ b/sound/soc/codecs/ad73311.c @@ -64,16 +64,8 @@ static int ad73311_soc_probe(struct platform_device *pdev) goto pcm_err; } - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "ad73311: failed to register card\n"); - goto register_err; - } - return ret; -register_err: - snd_soc_free_pcms(socdev); pcm_err: kfree(socdev->card->codec); socdev->card->codec = NULL; diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index 4d47bc4f742..3a14c6fc4f5 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -313,14 +313,6 @@ static int ak4104_probe(struct platform_device *pdev) return ret; } - /* Register the socdev */ - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card\n"); - snd_soc_free_pcms(socdev); - return ret; - } - return 0; } diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 0abec0d29a9..57a6846a9a1 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -485,17 +485,9 @@ static int ak4535_init(struct snd_soc_device *socdev) snd_soc_add_controls(codec, ak4535_snd_controls, ARRAY_SIZE(ak4535_snd_controls)); ak4535_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "ak4535: failed to register card\n"); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: kfree(codec->reg_cache); diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index e057c7b578d..b69861d5216 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -442,18 +442,9 @@ static int ak4642_probe(struct platform_device *pdev) goto pcm_err; } - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "ak4642: failed to register card\n"); - goto card_err; - } - dev_info(&pdev->dev, "AK4642 Audio Codec %s", AK4642_VERSION); return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index b61214d1c5d..364832ccd74 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -662,19 +662,10 @@ static int ak4671_probe(struct platform_device *pdev) ARRAY_SIZE(ak4671_snd_controls)); ak4671_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } - ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 565842dcfc6..ffe122d1cd7 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -599,13 +599,6 @@ static int cs4270_probe(struct platform_device *pdev) goto error_free_pcms; } - /* And finally, register the socdev */ - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card\n"); - goto error_free_pcms; - } - return 0; error_free_pcms: diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index 38eac9c866e..d7f9bf18b72 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -355,12 +355,6 @@ static int cx20442_codec_probe(struct platform_device *pdev) cx20442_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to register card\n"); - goto card_err; - } - return ret; card_err: diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c index 5cda9e6b5a7..2afcd0a8669 100644 --- a/sound/soc/codecs/pcm3008.c +++ b/sound/soc/codecs/pcm3008.c @@ -90,13 +90,6 @@ static int pcm3008_soc_probe(struct platform_device *pdev) goto pcm_err; } - /* Register Card. */ - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "pcm3008: failed to register card\n"); - goto card_err; - } - /* DEM1 DEM0 DE-EMPHASIS_MODE * Low Low De-emphasis 44.1 kHz ON * Low High De-emphasis OFF @@ -136,8 +129,6 @@ static int pcm3008_soc_probe(struct platform_device *pdev) gpio_err: pcm3008_gpio_free(setup); -card_err: - snd_soc_free_pcms(socdev); pcm_err: kfree(socdev->card->codec); diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index c550750c79c..b3130339d29 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -613,17 +613,9 @@ static int ssm2602_init(struct snd_soc_device *socdev) snd_soc_add_controls(codec, ssm2602_snd_controls, ARRAY_SIZE(ssm2602_snd_controls)); ssm2602_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - pr_err("ssm2602: failed to register card\n"); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index befc6488c39..bbc72c2ddfc 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -418,9 +418,6 @@ static int stac9766_codec_probe(struct platform_device *pdev) snd_soc_add_controls(codec, stac9766_snd_ac97_controls, ARRAY_SIZE(stac9766_snd_ac97_controls)); - ret = snd_soc_init_card(socdev); - if (ret < 0) - goto reset_err; return 0; reset_err: diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index 0b8dcb5cd72..ee8cb2c08b8 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -707,17 +707,9 @@ static int tlv320aic23_init(struct snd_soc_device *socdev) snd_soc_add_controls(codec, tlv320aic23_snd_controls, ARRAY_SIZE(tlv320aic23_snd_controls)); tlv320aic23_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "tlv320aic23: failed to register card\n"); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index 3387d9e736e..357b609196e 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -356,18 +356,7 @@ static int aic26_probe(struct platform_device *pdev) ARRAY_SIZE(aic26_snd_controls)); WARN_ON(err < 0); - /* CODEC is setup, we can register the card now */ - dev_dbg(&pdev->dev, "Registering card\n"); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "aic26: failed to register card\n"); - goto card_err; - } return 0; - - card_err: - snd_soc_free_pcms(socdev); - return ret; } static int aic26_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 3395cf945d5..03cad250f58 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1405,18 +1405,8 @@ static int aic3x_probe(struct platform_device *pdev) aic3x_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "aic3x: failed to register card\n"); - goto card_err; - } - return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); - pcm_err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 3ca8934fc26..bff476d65d0 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -960,16 +960,8 @@ static int dac33_soc_probe(struct platform_device *pdev) /* power on device */ dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card\n"); - goto card_err; - } - return 0; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); + pcm_err: dac33_hard_power(codec, 0); return ret; diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index c0b47dfc332..928257b2511 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -2155,19 +2155,7 @@ static int twl4030_soc_probe(struct platform_device *pdev) ARRAY_SIZE(twl4030_snd_controls)); twl4030_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to register card\n"); - goto card_err; - } - return 0; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); - - return ret; } static int twl4030_soc_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index c33b92edbde..aa40d985138 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -562,17 +562,8 @@ static int uda134x_soc_probe(struct platform_device *pdev) goto pcm_err; } - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "UDA134X: failed to register card\n"); - goto card_err; - } - return 0; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: kfree(codec->reg_cache); reg_err: diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 92ec0344215..a42e47d9463 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -713,17 +713,9 @@ static int uda1380_probe(struct platform_device *pdev) snd_soc_add_controls(codec, uda1380_snd_controls, ARRAY_SIZE(uda1380_snd_controls)); uda1380_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 714114b50d1..2e35a354b16 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1501,18 +1501,7 @@ static int wm8350_probe(struct platform_device *pdev) wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to register card\n"); - goto card_err; - } - return 0; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); - return ret; } static int wm8350_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index bd7eecba20f..0e30997c8db 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -1400,12 +1400,6 @@ static int wm8400_probe(struct platform_device *pdev) wm8400_add_controls(codec); wm8400_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to register card\n"); - goto card_err; - } - return ret; card_err: diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 5702435af81..e3c21ebcc08 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -604,16 +604,9 @@ static int wm8510_init(struct snd_soc_device *socdev, snd_soc_add_controls(codec, wm8510_snd_controls, ARRAY_SIZE(wm8510_snd_controls)); wm8510_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm8510: failed to register card\n"); - goto card_err; - } + return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 268cab21c2c..2e2b01d6c82 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -448,17 +448,9 @@ static int wm8523_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8523_snd_controls, ARRAY_SIZE(wm8523_snd_controls)); wm8523_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index a09b23e0366..dde50d11818 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -800,17 +800,9 @@ static int wm8580_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8580_snd_controls, ARRAY_SIZE(wm8580_snd_controls)); wm8580_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 54189fbf9e9..70e0675b5d4 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -404,17 +404,9 @@ static int wm8711_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8711_snd_controls, ARRAY_SIZE(wm8711_snd_controls)); wm8711_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c index 7df5a17eb73..d8ffbd641d7 100644 --- a/sound/soc/codecs/wm8727.c +++ b/sound/soc/codecs/wm8727.c @@ -68,17 +68,9 @@ static int wm8727_soc_probe(struct platform_device *pdev) printk(KERN_ERR "wm8727: failed to create pcms\n"); goto pcm_err; } - /* register card */ - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm8727: failed to register card\n"); - goto register_err; - } return ret; -register_err: - snd_soc_free_pcms(socdev); pcm_err: kfree(socdev->card->codec); socdev->card->codec = NULL; diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 16e969a762c..1252a8a486a 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -287,17 +287,9 @@ static int wm8728_init(struct snd_soc_device *socdev, snd_soc_add_controls(codec, wm8728_snd_controls, ARRAY_SIZE(wm8728_snd_controls)); wm8728_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm8728: failed to register card\n"); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index bb95af95097..e3675e7a981 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -495,17 +495,9 @@ static int wm8731_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8731_snd_controls, ARRAY_SIZE(wm8731_snd_controls)); wm8731_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 4ba1e7e93fb..50a3d659058 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -772,16 +772,8 @@ static int wm8750_init(struct snd_soc_device *socdev, snd_soc_add_controls(codec, wm8750_snd_controls, ARRAY_SIZE(wm8750_snd_controls)); wm8750_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm8750: failed to register card\n"); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 8f7305257d2..c652bc04cc8 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1583,18 +1583,9 @@ static int wm8753_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8753_snd_controls, ARRAY_SIZE(wm8753_snd_controls)); wm8753_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm8753: failed to register card\n"); - goto card_err; - } return 0; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); - pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index a0bbb28eed7..ab2c0da1809 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -447,17 +447,8 @@ static int wm8776_probe(struct platform_device *pdev) ARRAY_SIZE(wm8776_dapm_widgets)); snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes)); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } - return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index b48804b5cac..0d185cb6418 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1353,12 +1353,6 @@ static int wm8900_probe(struct platform_device *pdev) ARRAY_SIZE(wm8900_snd_controls)); wm8900_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to register card\n"); - goto card_err; - } - return ret; card_err: diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 94cdb813041..bfeff4ee5de 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1695,17 +1695,8 @@ static int wm8903_probe(struct platform_device *pdev) ARRAY_SIZE(wm8903_snd_controls)); wm8903_add_widgets(socdev->card->codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "wm8903: failed to register card\n"); - goto card_err; - } - return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); err: return ret; } diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 8d4fd3c08c0..fc80aa6c913 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -731,12 +731,6 @@ static int wm8940_probe(struct platform_device *pdev) if (ret) goto error_free_pcms; - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto error_free_pcms; - } - return ret; error_free_pcms: diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index b9b096a8539..40390afa75f 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -713,17 +713,9 @@ static int wm8960_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8960_snd_controls, ARRAY_SIZE(wm8960_snd_controls)); wm8960_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index b5c6f2cd5ae..07e389574db 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -988,17 +988,8 @@ static int wm8961_probe(struct platform_device *pdev) snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); snd_soc_dapm_new_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } - return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index d66efb0546e..56a66e89ab9 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -703,16 +703,9 @@ static int wm8971_init(struct snd_soc_device *socdev, snd_soc_add_controls(codec, wm8971_snd_controls, ARRAY_SIZE(wm8971_snd_controls)); wm8971_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm8971: failed to register card\n"); - goto card_err; - } + return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index eff29331235..c245f0ee0ec 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -641,17 +641,9 @@ static int wm8974_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8974_snd_controls, ARRAY_SIZE(wm8974_snd_controls)); wm8974_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index d8d8f68b81e..bee292e37d1 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -792,17 +792,8 @@ static int wm8988_probe(struct platform_device *pdev) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); snd_soc_dapm_new_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } - return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index f657e9a5fe2..e43cb2c8b91 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -1409,16 +1409,9 @@ static int wm8990_init(struct snd_soc_device *socdev) snd_soc_add_controls(codec, wm8990_snd_controls, ARRAY_SIZE(wm8990_snd_controls)); wm8990_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm8990: failed to register card\n"); - goto card_err; - } + return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index dac39771214..0d4d2be92b6 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1466,17 +1466,8 @@ static int wm8993_probe(struct platform_device *pdev) snd_soc_dapm_new_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card\n"); - goto card_err; - } - return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); err: return ret; } diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 4cb6b104b72..3f1f8442131 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -1264,17 +1264,8 @@ static int wm9081_probe(struct platform_device *pdev) snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); snd_soc_dapm_new_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } - return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index e7d2840d9e5..0e817b8705c 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -403,16 +403,8 @@ static int wm9705_soc_probe(struct platform_device *pdev) ARRAY_SIZE(wm9705_snd_ac97_controls)); wm9705_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm9705: failed to register card\n"); - goto reset_err; - } - return 0; -reset_err: - snd_soc_free_pcms(socdev); pcm_err: snd_soc_free_ac97_codec(codec); codec_err: diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 1fd4e88f50c..155cacf124e 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -695,17 +695,9 @@ static int wm9712_soc_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm9712_snd_ac97_controls, ARRAY_SIZE(wm9712_snd_ac97_controls)); wm9712_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm9712: failed to register card\n"); - goto reset_err; - } return 0; -reset_err: - snd_soc_free_pcms(socdev); - pcm_err: snd_soc_free_ac97_codec(codec); diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index ca3d449ed89..5f81ecd20a8 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1247,13 +1247,8 @@ static int wm9713_soc_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm9713_snd_ac97_controls, ARRAY_SIZE(wm9713_snd_ac97_controls)); wm9713_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) - goto reset_err; - return 0; -reset_err: - snd_soc_free_pcms(socdev); + return 0; pcm_err: snd_soc_free_ac97_codec(codec); -- cgit v1.2.3 From 2dcf9fb99d4ecadecb2685a9eb82e6b85511c960 Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Wed, 4 Nov 2009 17:49:22 +0000 Subject: ASoC: ADS117x ADC driver This patch adds support for the TI ADS117x family of multichannel ADCs and was sponsored by Shotspotter Inc. Signed-off-by: Graeme Gregory Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 ++ sound/soc/codecs/Makefile | 2 + sound/soc/codecs/ads117x.c | 127 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/ads117x.h | 13 +++++ 4 files changed, 146 insertions(+) create mode 100644 sound/soc/codecs/ads117x.c create mode 100644 sound/soc/codecs/ads117x.h (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 4a3e8dcf24d..52b005f8fed 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -15,6 +15,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_AD1836 if SPI_MASTER select SND_SOC_AD1938 if SPI_MASTER select SND_SOC_AD1980 if SND_SOC_AC97_BUS + select SND_SOC_ADS117X select SND_SOC_AD73311 if I2C select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C @@ -91,6 +92,9 @@ config SND_SOC_AD1980 config SND_SOC_AD73311 tristate + +config SND_SOC_ADS117X + tristate config SND_SOC_AK4104 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index cacfc7692d7..dbaecb133ac 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -3,6 +3,7 @@ snd-soc-ad1836-objs := ad1836.o snd-soc-ad1938-objs := ad1938.o snd-soc-ad1980-objs := ad1980.o snd-soc-ad73311-objs := ad73311.o +snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o snd-soc-ak4642-objs := ak4642.o @@ -58,6 +59,7 @@ obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o obj-$(CONFIG_SND_SOC_AD1938) += snd-soc-ad1938.o obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o +obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c new file mode 100644 index 00000000000..f3230927dc6 --- /dev/null +++ b/sound/soc/codecs/ads117x.c @@ -0,0 +1,127 @@ +/* + * ads117x.c -- Driver for ads1174/8 ADC chips + * + * Copyright 2009 ShotSpotter Inc. + * Author: Graeme Gregory + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ads117x.h" + +#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000) + +#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + +struct snd_soc_dai ads117x_dai = { +/* ADC */ + .name = "ADS117X ADC", + .id = 1, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 32, + .rates = ADS117X_RATES, + .formats = ADS117X_FORMATS,}, +}; +EXPORT_SYMBOL_GPL(ads117x_dai); + +/* + * initialise the ads117x driver + */ +static int ads117x_init(struct snd_soc_device *socdev) +{ + struct snd_soc_codec *codec = socdev->card->codec; + int ret = 0; + + codec->name = "ADS117X"; + codec->owner = THIS_MODULE; + codec->dai = &ads117x_dai; + codec->num_dai = 1; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "ads117x: failed to create pcms\n"); + return ret; + } + + ret = snd_soc_init_card(socdev); + if (ret < 0) { + printk(KERN_ERR "ads117x: failed to register card\n"); + goto card_err; + } + return ret; + +card_err: + snd_soc_free_pcms(socdev); + return ret; +} + +static int ads117x_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret; + + pr_info("ads117x ADC\n"); + + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + + socdev->card->codec = codec; + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + ret = ads117x_init(socdev); + if (ret != 0) + kfree(codec); + + return ret; +} + +static int ads117x_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + snd_soc_free_pcms(socdev); + kfree(codec); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_ads117x = { + .probe = ads117x_probe, + .remove = ads117x_remove, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_ads117x); + +static int __init ads117x_modinit(void) +{ + return snd_soc_register_dai(&ads117x_dai); +} +module_init(ads117x_modinit); + +static void __exit ads117x_exit(void) +{ + snd_soc_unregister_dai(&ads117x_dai); +} +module_exit(ads117x_exit); + +MODULE_DESCRIPTION("ASoC ads117x driver"); +MODULE_AUTHOR("Graeme Gregory"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ads117x.h b/sound/soc/codecs/ads117x.h new file mode 100644 index 00000000000..dbcf50ec9bd --- /dev/null +++ b/sound/soc/codecs/ads117x.h @@ -0,0 +1,13 @@ +/* + * ads117x.h -- Driver for ads1174/8 ADC chips + * + * Copyright 2009 ShotSpotter Inc. + * Author: Graeme Gregory + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +extern struct snd_soc_dai ads117x_dai; +extern struct snd_soc_codec_device soc_codec_dev_ads117x; -- cgit v1.2.3 From f3d0e82fe3cce0dd3ffcd9c59e6caa671a30f929 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 4 Nov 2009 21:43:27 +0000 Subject: ASoC: Update ads117x to current APIs Probe as a platform driver (ads117x) and remove the call to snd_soc_init_card(). Signed-off-by: Mark Brown --- sound/soc/codecs/ads117x.c | 76 ++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 40 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c index f3230927dc6..cc96411ca3e 100644 --- a/sound/soc/codecs/ads117x.c +++ b/sound/soc/codecs/ads117x.c @@ -37,46 +37,12 @@ struct snd_soc_dai ads117x_dai = { }; EXPORT_SYMBOL_GPL(ads117x_dai); -/* - * initialise the ads117x driver - */ -static int ads117x_init(struct snd_soc_device *socdev) -{ - struct snd_soc_codec *codec = socdev->card->codec; - int ret = 0; - - codec->name = "ADS117X"; - codec->owner = THIS_MODULE; - codec->dai = &ads117x_dai; - codec->num_dai = 1; - - /* register pcms */ - ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); - if (ret < 0) { - printk(KERN_ERR "ads117x: failed to create pcms\n"); - return ret; - } - - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "ads117x: failed to register card\n"); - goto card_err; - } - return ret; - -card_err: - snd_soc_free_pcms(socdev); - return ret; -} - static int ads117x_probe(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec; int ret; - pr_info("ads117x ADC\n"); - codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); if (codec == NULL) return -ENOMEM; @@ -85,12 +51,20 @@ static int ads117x_probe(struct platform_device *pdev) mutex_init(&codec->mutex); INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); + codec->name = "ADS117X"; + codec->owner = THIS_MODULE; + codec->dai = &ads117x_dai; + codec->num_dai = 1; - ret = ads117x_init(socdev); - if (ret != 0) + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "ads117x: failed to create pcms\n"); kfree(codec); + return ret; + } - return ret; + return 0; } static int ads117x_remove(struct platform_device *pdev) @@ -110,15 +84,37 @@ struct snd_soc_codec_device soc_codec_dev_ads117x = { }; EXPORT_SYMBOL_GPL(soc_codec_dev_ads117x); -static int __init ads117x_modinit(void) +static __devinit int ads117x_platform_probe(struct platform_device *pdev) { + ads117x_dai.dev = &pdev->dev; return snd_soc_register_dai(&ads117x_dai); } -module_init(ads117x_modinit); -static void __exit ads117x_exit(void) +static int __devexit ads117x_platform_remove(struct platform_device *pdev) { snd_soc_unregister_dai(&ads117x_dai); + return 0; +} + +static struct platform_driver ads117x_codec_driver = { + .driver = { + .name = "ads117x", + .owner = THIS_MODULE, + }, + + .probe = ads117x_platform_probe, + .remove = __devexit_p(ads117x_platform_remove), +}; + +static int __init ads117x_init(void) +{ + return platform_driver_register(&ads117x_codec_driver); +} +module_init(ads117x_init); + +static void __exit ads117x_exit(void) +{ + platform_driver_unregister(&ads117x_codec_driver); } module_exit(ads117x_exit); -- cgit v1.2.3 From 4cae37fa98f4d50778161ec033122444e3c10a01 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 7 Nov 2009 10:18:22 +0100 Subject: ASoC: Remove dead code and labels Remove the dead code and labels "card_err" in the error paths of some codec drivers. Signed-off-by: Takashi Iwai --- sound/soc/codecs/ad1836.c | 5 ----- sound/soc/codecs/ad1938.c | 5 ----- sound/soc/codecs/cx20442.c | 5 ----- sound/soc/codecs/wm8400.c | 5 ----- sound/soc/codecs/wm8900.c | 5 ----- 5 files changed, 25 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 2e360c24307..b4be96decf3 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -387,11 +387,6 @@ static int ad1836_probe(struct platform_device *pdev) snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); snd_soc_dapm_new_widgets(codec); - return ret; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c index 09c008ad147..3b2222a0c80 100644 --- a/sound/soc/codecs/ad1938.c +++ b/sound/soc/codecs/ad1938.c @@ -596,11 +596,6 @@ static int ad1938_probe(struct platform_device *pdev) ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return ret; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index d7f9bf18b72..dda751c885c 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -355,11 +355,6 @@ static int cx20442_codec_probe(struct platform_device *pdev) cx20442_add_widgets(codec); - return ret; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 0e30997c8db..584af68af22 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -1400,11 +1400,6 @@ static int wm8400_probe(struct platform_device *pdev) wm8400_add_controls(codec); wm8400_add_widgets(codec); - return ret; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 0d185cb6418..85f67dbe211 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1353,11 +1353,6 @@ static int wm8900_probe(struct platform_device *pdev) ARRAY_SIZE(wm8900_snd_controls)); wm8900_add_widgets(codec); - return ret; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } -- cgit v1.2.3 From 68d019553b8cc4ddac7f861e23efbe48a1367490 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 4 Nov 2009 09:58:20 +0200 Subject: ASoC: TWL4030: Do not modify the APLL_CTL register APLL_CTL register is configured by the twl4030-codec MFD driver. Remove code, which makes changes in the APLL_CTL register, and replace those with checks against the configured audio_mclk configuration done in the MFD driver. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 76 +++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 45 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 928257b2511..510b8b226f9 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -214,7 +214,8 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) /* set all audio section registers to reasonable defaults */ for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) - twl4030_write(codec, i, cache[i]); + if (i != TWL4030_REG_APLL_CTL) + twl4030_write(codec, i, cache[i]); } @@ -1753,30 +1754,23 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; struct twl4030_priv *twl4030 = codec->private_data; - u8 apll_ctrl; - apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); - apll_ctrl &= ~TWL4030_APLL_INFREQ; switch (freq) { case 19200000: - apll_ctrl |= TWL4030_APLL_INFREQ_19200KHZ; - twl4030->sysclk = 19200; - break; case 26000000: - apll_ctrl |= TWL4030_APLL_INFREQ_26000KHZ; - twl4030->sysclk = 26000; - break; case 38400000: - apll_ctrl |= TWL4030_APLL_INFREQ_38400KHZ; - twl4030->sysclk = 38400; break; default: - printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n", - freq); + dev_err(codec->dev, "Unsupported APLL mclk: %u\n", freq); return -EINVAL; } - twl4030_write(codec, TWL4030_REG_APLL_CTL, apll_ctrl); + if ((freq / 1000) != twl4030->sysclk) { + dev_err(codec->dev, + "Mismatch in APLL mclk: %u (configured: %u)\n", + freq, twl4030->sysclk * 1000); + return -EINVAL; + } return 0; } @@ -1874,18 +1868,16 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_codec *codec = socdev->card->codec; - u8 infreq; + struct twl4030_priv *twl4030 = codec->private_data; u8 mode; /* If the system master clock is not 26MHz, the voice PCM interface is * not avilable. */ - infreq = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL) - & TWL4030_APLL_INFREQ; - - if (infreq != TWL4030_APLL_INFREQ_26000KHZ) { - printk(KERN_ERR "TWL4030 voice startup: " - "MCLK is not 26MHz, call set_sysclk() on init\n"); + if (twl4030->sysclk != 26000) { + dev_err(codec->dev, "The board is configured for %u Hz, while" + "the Voice interface needs 26MHz APLL mclk\n", + twl4030->sysclk * 1000); return -EINVAL; } @@ -1958,22 +1950,19 @@ static int twl4030_voice_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; - u8 apll_ctrl; + struct twl4030_priv *twl4030 = codec->private_data; - apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); - apll_ctrl &= ~TWL4030_APLL_INFREQ; - switch (freq) { - case 26000000: - apll_ctrl |= TWL4030_APLL_INFREQ_26000KHZ; - break; - default: - printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n", - freq); + if (freq != 26000000) { + dev_err(codec->dev, "Unsupported APLL mclk: %u, the Voice" + "interface needs 26MHz APLL mclk\n", freq); + return -EINVAL; + } + if ((freq / 1000) != twl4030->sysclk) { + dev_err(codec->dev, + "Mismatch in APLL mclk: %u (configured: %u)\n", + freq, twl4030->sysclk * 1000); return -EINVAL; } - - twl4030_write(codec, TWL4030_REG_APLL_CTL, apll_ctrl); - return 0; } @@ -2131,17 +2120,15 @@ static int twl4030_soc_probe(struct platform_device *pdev) if (setup) { unsigned char hs_pop; - if (setup->sysclk) - twl4030->sysclk = setup->sysclk; - else - twl4030->sysclk = 26000; + if (setup->sysclk != twl4030->sysclk) + dev_warn(&pdev->dev, + "Mismatch in APLL mclk: %u (configured: %u)\n", + setup->sysclk, twl4030->sysclk); hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); hs_pop &= ~TWL4030_RAMP_DELAY; hs_pop |= (setup->ramp_delay_value << 2); twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop); - } else { - twl4030->sysclk = 26000; } /* register pcms */ @@ -2179,10 +2166,8 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) struct twl4030_priv *twl4030; int ret; - if (!pdata || !(pdata->audio_mclk == 19200000 || - pdata->audio_mclk == 26000000 || - pdata->audio_mclk == 38400000)) { - dev_err(&pdev->dev, "Invalid platform_data\n"); + if (!pdata) { + dev_err(&pdev->dev, "platform_data is missing\n"); return -EINVAL; } @@ -2221,6 +2206,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) twl4030_codec = codec; /* Set the defaults, and power up the codec */ + twl4030->sysclk = twl4030_codec_get_mclk() / 1000; twl4030_init_chip(codec); codec->bias_level = SND_SOC_BIAS_OFF; twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); -- cgit v1.2.3 From 0a3f5e35aae43b20fef09fd800cf52cc9a2d61a8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 12 Nov 2009 23:15:08 +0000 Subject: ASoC: Remove redundant snd_soc_dapm_new_widgets() calls The DAPM widgets are now insntantiated by the core when creating the card so there is no need for the individual CODEC drivers to do so. Signed-off-by: Mark Brown --- sound/soc/codecs/ad1836.c | 1 - sound/soc/codecs/ad1938.c | 1 - sound/soc/codecs/ak4535.c | 1 - sound/soc/codecs/ak4671.c | 1 - sound/soc/codecs/cx20442.c | 1 - sound/soc/codecs/ssm2602.c | 1 - sound/soc/codecs/tlv320aic23.c | 1 - sound/soc/codecs/tlv320aic3x.c | 1 - sound/soc/codecs/tlv320dac33.c | 1 - sound/soc/codecs/twl4030.c | 1 - sound/soc/codecs/uda1380.c | 1 - sound/soc/codecs/wm8350.c | 2 +- sound/soc/codecs/wm8400.c | 1 - sound/soc/codecs/wm8510.c | 1 - sound/soc/codecs/wm8523.c | 1 - sound/soc/codecs/wm8580.c | 1 - sound/soc/codecs/wm8711.c | 1 - sound/soc/codecs/wm8728.c | 2 -- sound/soc/codecs/wm8731.c | 1 - sound/soc/codecs/wm8750.c | 1 - sound/soc/codecs/wm8753.c | 1 - sound/soc/codecs/wm8900.c | 2 -- sound/soc/codecs/wm8903.c | 2 -- sound/soc/codecs/wm8940.c | 1 - sound/soc/codecs/wm8960.c | 1 - sound/soc/codecs/wm8961.c | 1 - sound/soc/codecs/wm8971.c | 2 -- sound/soc/codecs/wm8974.c | 1 - sound/soc/codecs/wm8988.c | 1 - sound/soc/codecs/wm8990.c | 1 - sound/soc/codecs/wm8993.c | 2 -- sound/soc/codecs/wm9081.c | 1 - sound/soc/codecs/wm9705.c | 1 - sound/soc/codecs/wm9712.c | 1 - sound/soc/codecs/wm9713.c | 1 - 35 files changed, 1 insertion(+), 40 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index b4be96decf3..2c18e3d1b71 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -385,7 +385,6 @@ static int ad1836_probe(struct platform_device *pdev) snd_soc_dapm_new_controls(codec, ad1836_dapm_widgets, ARRAY_SIZE(ad1836_dapm_widgets)); snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); - snd_soc_dapm_new_widgets(codec); pcm_err: return ret; diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c index 3b2222a0c80..5d489186c05 100644 --- a/sound/soc/codecs/ad1938.c +++ b/sound/soc/codecs/ad1938.c @@ -592,7 +592,6 @@ static int ad1938_probe(struct platform_device *pdev) snd_soc_dapm_new_controls(codec, ad1938_dapm_widgets, ARRAY_SIZE(ad1938_dapm_widgets)); snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); - snd_soc_dapm_new_widgets(codec); ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY); diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 57a6846a9a1..ff966567e2b 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -294,7 +294,6 @@ static int ak4535_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index 364832ccd74..82fca284d00 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -441,7 +441,6 @@ static int ak4671_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index dda751c885c..e000cdfec1e 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -93,7 +93,6 @@ static int cx20442_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, cx20442_audio_map, ARRAY_SIZE(cx20442_audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index b3130339d29..d2ff1cde688 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -210,7 +210,6 @@ static int ssm2602_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_conn, ARRAY_SIZE(audio_conn)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index ee8cb2c08b8..1709e3f614a 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -395,7 +395,6 @@ static int tlv320aic23_add_widgets(struct snd_soc_codec *codec) /* set up audio path interconnects */ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 03cad250f58..2b4dc2b0b01 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -753,7 +753,6 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec) /* set up audio path interconnects */ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index bff476d65d0..2a013e46ae1 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -462,7 +462,6 @@ static int dac33_add_widgets(struct snd_soc_codec *codec) /* set up audio path interconnects */ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 510b8b226f9..5f1681f6ca7 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -1493,7 +1493,6 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index a42e47d9463..a2763c2e734 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -378,7 +378,6 @@ static int uda1380_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 2e35a354b16..f82125d9e85 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -800,7 +800,7 @@ static int wm8350_add_widgets(struct snd_soc_codec *codec) return ret; } - return snd_soc_dapm_new_widgets(codec); + return 0; } static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai, diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 584af68af22..b432f4d4a32 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -915,7 +915,6 @@ static int wm8400_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index e3c21ebcc08..265e68c75df 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -219,7 +219,6 @@ static int wm8510_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 2e2b01d6c82..d3a61d7ea0c 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -117,7 +117,6 @@ static int wm8523_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index dde50d11818..d077df6f5e7 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -315,7 +315,6 @@ static int wm8580_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 70e0675b5d4..24a35603bcf 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -99,7 +99,6 @@ static int wm8711_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 1252a8a486a..3fb653ba363 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -74,8 +74,6 @@ static int wm8728_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); - return 0; } diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index e3675e7a981..3a497810f93 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -159,7 +159,6 @@ static int wm8731_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 50a3d659058..475c67ac781 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -403,7 +403,6 @@ static int wm8750_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index c652bc04cc8..d6850dacda2 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -673,7 +673,6 @@ static int wm8753_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 85f67dbe211..c9438dd62df 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -618,8 +618,6 @@ static int wm8900_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); - return 0; } diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index bfeff4ee5de..b8cae175864 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -919,8 +919,6 @@ static int wm8903_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); - return 0; } diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index fc80aa6c913..3d850b97037 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -298,7 +298,6 @@ static int wm8940_add_widgets(struct snd_soc_codec *codec) ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); if (ret) goto error_ret; - ret = snd_soc_dapm_new_widgets(codec); error_ret: return ret; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 40390afa75f..d07bcc1e1c6 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -307,7 +307,6 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index 07e389574db..a8007d58813 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -986,7 +986,6 @@ static int wm8961_probe(struct platform_device *pdev) snd_soc_dapm_new_controls(codec, wm8961_dapm_widgets, ARRAY_SIZE(wm8961_dapm_widgets)); snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); - snd_soc_dapm_new_widgets(codec); return ret; diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 56a66e89ab9..d9540d55fc8 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -338,8 +338,6 @@ static int wm8971_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); - return 0; } diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index c245f0ee0ec..81c57b5c591 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -276,7 +276,6 @@ static int wm8974_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index bee292e37d1..2862e4dced2 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -790,7 +790,6 @@ static int wm8988_probe(struct platform_device *pdev) snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets, ARRAY_SIZE(wm8988_dapm_widgets)); snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return ret; diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index e43cb2c8b91..341481e0e83 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -920,7 +920,6 @@ static int wm8990_add_widgets(struct snd_soc_codec *codec) /* set up the WM8990 audio map */ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 0d4d2be92b6..5e32f2ed5fc 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1464,8 +1464,6 @@ static int wm8993_probe(struct platform_device *pdev) wm_hubs_add_analogue_routes(codec, wm8993->pdata.lineout1_diff, wm8993->pdata.lineout2_diff); - snd_soc_dapm_new_widgets(codec); - return ret; err: diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 3f1f8442131..c468497314b 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -1262,7 +1262,6 @@ static int wm9081_probe(struct platform_device *pdev) snd_soc_dapm_new_controls(codec, wm9081_dapm_widgets, ARRAY_SIZE(wm9081_dapm_widgets)); snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); - snd_soc_dapm_new_widgets(codec); return ret; diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index 0e817b8705c..dfffc6c778c 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -205,7 +205,6 @@ static int wm9705_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_new_controls(codec, wm9705_dapm_widgets, ARRAY_SIZE(wm9705_dapm_widgets)); snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 155cacf124e..2a087227300 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -436,7 +436,6 @@ static int wm9712_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 5f81ecd20a8..00bac315fb3 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -625,7 +625,6 @@ static int wm9713_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } -- cgit v1.2.3 From 74ea23aa6c9a8bece71b35ddeeb7ad6ae6782cd9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 26 Nov 2009 13:55:11 +0200 Subject: ASoC: tlv320dac33: Change RT wq to singlethread wq RT workqueue is going away in the near future, replace it with singlethread wq for now, which is still supported. Signed-off-by: Peter Ujfalusi Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320dac33.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 2a013e46ae1..9c8903dbe64 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1118,7 +1118,8 @@ static int dac33_i2c_probe(struct i2c_client *client, } if (dac33->irq != -1) { /* Setup work queue */ - dac33->dac33_wq = create_rt_workqueue("tlv320dac33"); + dac33->dac33_wq = + create_singlethread_workqueue("tlv320dac33"); if (dac33->dac33_wq == NULL) { free_irq(dac33->irq, &dac33->codec); ret = -ENOMEM; -- cgit v1.2.3 From a22eaf4ce106404f6c5283da30b4d514ede964c1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 27 Nov 2009 15:14:09 +0100 Subject: ASoC: Revert missing reset_err in wm97*.c The commit fe3e78e073d25308756f38019956061153267769 ASoC: Factor out snd_soc_init_card() removed the error paths that are still valid for wm97* codecs, causing the compile errors like sound/soc/codecs/wm9705.c:399: error: label 'reset_err' used but not defined sound/soc/codecs/wm9712.c:687: error: label 'reset_err' used but not defined sound/soc/codecs/wm9713.c:1237: error: label 'reset_err' used but not defined Revert the removed error path codes. Signed-off-by: Takashi Iwai --- sound/soc/codecs/wm9705.c | 2 ++ sound/soc/codecs/wm9712.c | 2 ++ sound/soc/codecs/wm9713.c | 2 ++ 3 files changed, 6 insertions(+) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index dfffc6c778c..ec54c6da985 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -404,6 +404,8 @@ static int wm9705_soc_probe(struct platform_device *pdev) return 0; +reset_err: + snd_soc_free_pcms(socdev); pcm_err: snd_soc_free_ac97_codec(codec); codec_err: diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 2a087227300..0ac1215dcd9 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -697,6 +697,8 @@ static int wm9712_soc_probe(struct platform_device *pdev) return 0; +reset_err: + snd_soc_free_pcms(socdev); pcm_err: snd_soc_free_ac97_codec(codec); diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 00bac315fb3..4d74ecb0e56 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1249,6 +1249,8 @@ static int wm9713_soc_probe(struct platform_device *pdev) return 0; +reset_err: + snd_soc_free_pcms(socdev); pcm_err: snd_soc_free_ac97_codec(codec); -- cgit v1.2.3 From 3482594802d80a595ca50b16d3a25bcc1eb480c8 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 4 Dec 2009 15:12:10 +0900 Subject: ASoC: Rename controls with a / in wm_hubs This renames from a character / to : of controls. A / occurs below error messages. ASoC: Failed to create IN2RP/VXRP debugfs file ASoC: Failed to create IN2LP/VXRN debugfs file Signed-off-by: Joonyoung Shim Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/codecs/wm_hubs.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 810a563d0eb..d73c30536a2 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -438,11 +438,11 @@ static const struct snd_soc_dapm_widget analogue_dapm_widgets[] = { SND_SOC_DAPM_INPUT("IN1LN"), SND_SOC_DAPM_INPUT("IN1LP"), SND_SOC_DAPM_INPUT("IN2LN"), -SND_SOC_DAPM_INPUT("IN2LP/VXRN"), +SND_SOC_DAPM_INPUT("IN2LP:VXRN"), SND_SOC_DAPM_INPUT("IN1RN"), SND_SOC_DAPM_INPUT("IN1RP"), SND_SOC_DAPM_INPUT("IN2RN"), -SND_SOC_DAPM_INPUT("IN2RP/VXRP"), +SND_SOC_DAPM_INPUT("IN2RP:VXRP"), SND_SOC_DAPM_MICBIAS("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0), SND_SOC_DAPM_MICBIAS("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0), @@ -537,14 +537,14 @@ static const struct snd_soc_dapm_route analogue_routes[] = { { "IN1R PGA", "IN1RP Switch", "IN1RP" }, { "IN1R PGA", "IN1RN Switch", "IN1RN" }, - { "IN2L PGA", "IN2LP Switch", "IN2LP/VXRN" }, + { "IN2L PGA", "IN2LP Switch", "IN2LP:VXRN" }, { "IN2L PGA", "IN2LN Switch", "IN2LN" }, - { "IN2R PGA", "IN2RP Switch", "IN2RP/VXRP" }, + { "IN2R PGA", "IN2RP Switch", "IN2RP:VXRP" }, { "IN2R PGA", "IN2RN Switch", "IN2RN" }, - { "Direct Voice", NULL, "IN2LP/VXRN" }, - { "Direct Voice", NULL, "IN2RP/VXRP" }, + { "Direct Voice", NULL, "IN2LP:VXRN" }, + { "Direct Voice", NULL, "IN2RP:VXRP" }, { "MIXINL", "IN1L Switch", "IN1L PGA" }, { "MIXINL", "IN2L Switch", "IN2L PGA" }, @@ -565,7 +565,7 @@ static const struct snd_soc_dapm_route analogue_routes[] = { { "Left Output Mixer", "Right Input Switch", "MIXINR" }, { "Left Output Mixer", "IN2RN Switch", "IN2RN" }, { "Left Output Mixer", "IN2LN Switch", "IN2LN" }, - { "Left Output Mixer", "IN2LP Switch", "IN2LP/VXRN" }, + { "Left Output Mixer", "IN2LP Switch", "IN2LP:VXRN" }, { "Left Output Mixer", "IN1L Switch", "IN1L PGA" }, { "Left Output Mixer", "IN1R Switch", "IN1R PGA" }, @@ -573,7 +573,7 @@ static const struct snd_soc_dapm_route analogue_routes[] = { { "Right Output Mixer", "Right Input Switch", "MIXINR" }, { "Right Output Mixer", "IN2LN Switch", "IN2LN" }, { "Right Output Mixer", "IN2RN Switch", "IN2RN" }, - { "Right Output Mixer", "IN2RP Switch", "IN2RP/VXRP" }, + { "Right Output Mixer", "IN2RP Switch", "IN2RP:VXRP" }, { "Right Output Mixer", "IN1L Switch", "IN1L PGA" }, { "Right Output Mixer", "IN1R Switch", "IN1R PGA" }, -- cgit v1.2.3