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 diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b6c7f7a..663840e 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 f265380..19950e9 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 0000000..84ead3f --- /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 0000000..381e84a --- /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 v0.10.2 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 diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 84ead3f..812283e 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 v0.10.2 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 diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 812283e..c7b1af8 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 v0.10.2 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 diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a5cfa78..20ebf74 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 c7b1af8..1a7fca7 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) @@ -591,6 +647,13 @@ static int __init wm8711_modinit(void) 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; } module_init(wm8711_modinit); @@ -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 v0.10.2 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 diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 1a7fca7..f98c2bc 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 v0.10.2 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 diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index f98c2bc..ae083eb 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 v0.10.2 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 diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 97ca9af..16963d4 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -106,7 +106,7 @@ int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div); int snd_soc_dai_set_pll(struct snd_soc_dai *dai, - int pll_id, unsigned int freq_in, unsigned int freq_out); + int pll_id, int source, unsigned int freq_in, unsigned int freq_out); /* Digital Audio interface formatting */ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt); @@ -136,8 +136,8 @@ struct snd_soc_dai_ops { */ int (*set_sysclk)(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir); - int (*set_pll)(struct snd_soc_dai *dai, - int pll_id, unsigned int freq_in, unsigned int freq_out); + int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out); int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div); /* diff --git a/sound/soc/atmel/playpaq_wm8510.c b/sound/soc/atmel/playpaq_wm8510.c index 9eb610c..9df4c68 100644 --- a/sound/soc/atmel/playpaq_wm8510.c +++ b/sound/soc/atmel/playpaq_wm8510.c @@ -268,7 +268,7 @@ static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream, #endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */ - ret = snd_soc_dai_set_pll(codec_dai, 0, + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, clk_get_rate(CODEC_CLK), pll_out); if (ret < 0) { pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n", diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 71c9c4b..0ebd99b 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 b9ef4d9..9cb8e50 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 060d5d0..5702435 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 6bded8c..3be5c0b 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 d80d414..f60f3a0 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 5e9c855..882604e 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 da97aae..914d788 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 f59703b..416fb3c 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 d8a013a..fa4d85b 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 2d702db..f657e9a 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 d998799..6b32a28 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 abed37a..ca3d449 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); diff --git a/sound/soc/imx/mx27vis_wm8974.c b/sound/soc/imx/mx27vis_wm8974.c index e4dcb53..0267d2d 100644 --- a/sound/soc/imx/mx27vis_wm8974.c +++ b/sound/soc/imx/mx27vis_wm8974.c @@ -157,7 +157,7 @@ static int mx27vis_hifi_hw_params(struct snd_pcm_substream *substream, /* codec PLL input is 25 MHz */ - ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, + ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, IGNORED_ARG, 25000000, pll_out); if (ret < 0) { printk(KERN_ERR "Error when setting PLL input\n"); diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index 9f7c61e..4c8d99a 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -213,7 +213,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, return ret; /* set SSP audio pll clock */ - ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, acps); + ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, acps); if (ret < 0) return ret; diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 5b9ed64..57f201c 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -305,8 +305,8 @@ static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, /* * Configure the PLL frequency pxa27x and (afaik - pxa320 only) */ -static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, - int pll_id, unsigned int freq_in, unsigned int freq_out) +static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) { struct ssp_priv *priv = cpu_dai->private_data; struct ssp_device *ssp = priv->dev.ssp; diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index 9a386b4..dd678ae 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -74,7 +74,8 @@ static const struct snd_soc_dapm_route audio_map[] = { static int zylonite_wm9713_init(struct snd_soc_codec *codec) { if (clk_pout) - snd_soc_dai_set_pll(&codec->dai[0], 0, clk_get_rate(pout), 0); + snd_soc_dai_set_pll(&codec->dai[0], 0, 0, + clk_get_rate(pout), 0); snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets, ARRAY_SIZE(zylonite_dapm_widgets)); @@ -128,7 +129,7 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out); + ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, pll_out); if (ret < 0) return ret; diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c index 0c52e36..6ddd1b3 100644 --- a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c @@ -119,7 +119,7 @@ static int neo1973_gta02_hifi_hw_params(struct snd_pcm_substream *substream, return ret; /* codec PLL input is PCLK/4 */ - ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, + ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, iis_clkrate / 4, pll_out); if (ret < 0) return ret; diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 906709e..16009eb 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -137,7 +137,7 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, return ret; /* codec PLL input is PCLK/4 */ - ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, + ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, iis_clkrate / 4, pll_out); if (ret < 0) return ret; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 7ff04ad..05fdc80 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2197,16 +2197,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv); * snd_soc_dai_set_pll - configure DAI PLL. * @dai: DAI * @pll_id: DAI specific PLL ID + * @source: DAI specific source for the PLL * @freq_in: PLL input clock frequency in Hz * @freq_out: requested PLL output clock frequency in Hz * * Configures and enables PLL to generate output clock based on input clock. */ -int snd_soc_dai_set_pll(struct snd_soc_dai *dai, - int pll_id, unsigned int freq_in, unsigned int freq_out) +int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) { if (dai->ops && dai->ops->set_pll) - return dai->ops->set_pll(dai, pll_id, freq_in, freq_out); + return dai->ops->set_pll(dai, pll_id, source, + freq_in, freq_out); else return -EINVAL; } -- cgit v0.10.2 From 341c9b84bc01040bd5c75140303e32f6b10098f3 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Mon, 7 Sep 2009 12:04:37 +0900 Subject: ASoC: Factor out I2C 8 bit address 8 bit data I/O This patch is for the AK4671 codec driver using this format. Signed-off-by: Joonyoung Shim Signed-off-by: Mark Brown diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index c8ceddc..404231e 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -77,6 +77,35 @@ static int snd_soc_7_9_spi_write(void *control_data, const char *data, #define snd_soc_7_9_spi_write NULL #endif +static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 *cache = codec->reg_cache; + u8 data[2]; + + BUG_ON(codec->volatile_register); + + data[0] = reg & 0xff; + data[1] = value & 0xff; + + if (reg < codec->reg_cache_size) + cache[reg] = value; + + if (codec->hw_write(codec->control_data, data, 2) == 2) + return 0; + else + return -EIO; +} + +static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + u8 *cache = codec->reg_cache; + if (reg >= codec->reg_cache_size) + return -1; + return cache[reg]; +} + static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { @@ -151,6 +180,7 @@ static struct { unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int); } io_types[] = { { 7, 9, snd_soc_7_9_write, snd_soc_7_9_spi_write, snd_soc_7_9_read }, + { 8, 8, snd_soc_8_8_write, NULL, snd_soc_8_8_read, NULL }, { 8, 16, snd_soc_8_16_write, NULL, snd_soc_8_16_read, snd_soc_8_16_read_i2c }, }; -- cgit v0.10.2 From 215edda3adf502ccdf3a358ab35b616e7abd25ff Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Sep 2009 18:59:05 +0100 Subject: ASoC: Allow per-route connectedness checks for supplies Some chips with complex internal supply (particularly clocking) arragements may have multiple options for some of the supply connections. Since these don't affect user-visible audio routing the expectation would be that they would be managed automatically by one of the drivers. Support these users by allowing routes to have a connected function which is queried before the connectedness of the path is checked as normal. Currently this is only done for supplies, other widgets could be supported but are not currently since the expectation for them is that audio routing will be under the control of userspace. Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index c1410e3..67224db 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -333,6 +333,10 @@ struct snd_soc_dapm_route { const char *sink; const char *control; const char *source; + + /* Note: currently only supported for links where source is a supply */ + int (*connected)(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink); }; /* dapm audio path between two widgets */ @@ -349,6 +353,9 @@ struct snd_soc_dapm_path { u32 connect:1; /* source and sink widgets are connected */ u32 walked:1; /* path has been walked */ + int (*connected)(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink); + struct list_head list_source; struct list_head list_sink; struct list_head list; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 0d8b08e..37f7ade 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -718,6 +718,10 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) /* Check if one of our outputs is connected */ list_for_each_entry(path, &w->sinks, list_source) { + if (path->connected && + !path->connected(path->source, path->sink)) + continue; + if (path->sink && path->sink->power_check && path->sink->power_check(path->sink)) { power = 1; @@ -1136,6 +1140,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file, w->sname); list_for_each_entry(p, &w->sources, list_sink) { + if (p->connected && !p->connected(w, p->sink)) + continue; + if (p->connect) ret += snprintf(buf + ret, PAGE_SIZE - ret, " in %s %s\n", @@ -1143,6 +1150,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file, p->source->name); } list_for_each_entry(p, &w->sinks, list_source) { + if (p->connected && !p->connected(w, p->sink)) + continue; + if (p->connect) ret += snprintf(buf + ret, PAGE_SIZE - ret, " out %s %s\n", @@ -1385,10 +1395,13 @@ int snd_soc_dapm_sync(struct snd_soc_codec *codec) EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, - const char *sink, const char *control, const char *source) + const struct snd_soc_dapm_route *route) { struct snd_soc_dapm_path *path; struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; + const char *sink = route->sink; + const char *control = route->control; + const char *source = route->source; int ret = 0; /* find src and dest widgets */ @@ -1412,6 +1425,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, path->source = wsource; path->sink = wsink; + path->connected = route->connected; INIT_LIST_HEAD(&path->list); INIT_LIST_HEAD(&path->list_source); INIT_LIST_HEAD(&path->list_sink); @@ -1512,8 +1526,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec, int i, ret; for (i = 0; i < num; i++) { - ret = snd_soc_dapm_add_route(codec, route->sink, - route->control, route->source); + ret = snd_soc_dapm_add_route(codec, route); if (ret < 0) { printk(KERN_ERR "Failed to add route %s->%s\n", route->source, -- cgit v0.10.2 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 diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0edca93..a2bb659 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 fb4af28..13f7b4f 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 0000000..b61214d --- /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 0000000..e2fad96 --- /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 v0.10.2 From 472df3cbae8da6a949f1392a11958b8d21383735 Mon Sep 17 00:00:00 2001 From: Barry Song <21cnbao@gmail.com> Date: Sat, 12 Sep 2009 01:16:29 +0800 Subject: ASoC: Provide API for reordering channels The patch adds an interface to set the relationship between audio channel number and slot number. The interface should be really useful because audio channel n doesn't always use slot n in all platforms. And for some devices, the relationship even can change with sound mode switch in 2.1,3.1,4.1,5.1,6.1,7.1 etc. Signed-off-by: Barry Song <21cnbao@gmail.com> Signed-off-by: Mark Brown diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 16963d4..e0c7fa7 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -114,6 +114,10 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt); int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); +int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot); + int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate); /* Digital Audio Interface mute */ @@ -148,6 +152,9 @@ struct snd_soc_dai_ops { int (*set_tdm_slot)(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); + int (*set_channel_map)(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot); int (*set_tristate)(struct snd_soc_dai *dai, int tristate); /* diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 05fdc80..f5b356f 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2253,6 +2253,30 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); /** + * snd_soc_dai_set_channel_map - configure DAI audio channel map + * @dai: DAI + * @tx_num: how many TX channels + * @tx_slot: pointer to an array which imply the TX slot number channel + * 0~num-1 uses + * @rx_num: how many RX channels + * @rx_slot: pointer to an array which imply the RX slot number channel + * 0~num-1 uses + * + * configure the relationship between channel number and TDM slot number. + */ +int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + if (dai->ops && dai->ops->set_channel_map) + return dai->ops->set_channel_map(dai, tx_num, tx_slot, + rx_num, rx_slot); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); + +/** * snd_soc_dai_set_tristate - configure DAI system or master clock. * @dai: DAI * @tristate: tristate enable -- cgit v0.10.2 From fd5ad654e665b5c30c8d755a106309c8ea9f3e7b Mon Sep 17 00:00:00 2001 From: Jassi Date: Tue, 15 Sep 2009 19:02:38 +0900 Subject: ASoC: S3C I2S LRCLK polarity option. 1) Explicitly set LRCLK polarity for I2S Vs LSM/MSB modes. 2) Convert from numerical to bit-field values for BCLK selection. 3) Use proper error checking for return value from clk_get Signed-off-by: Jassi Signed-off-by: Mark Brown diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c index aa7af0b..819c3c0 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.c +++ b/sound/soc/s3c24xx/s3c-i2s-v2.c @@ -308,12 +308,15 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_RIGHT_J: + iismod |= S3C2412_IISMOD_LR_RLOW; iismod |= S3C2412_IISMOD_SDF_MSB; break; case SND_SOC_DAIFMT_LEFT_J: + iismod |= S3C2412_IISMOD_LR_RLOW; iismod |= S3C2412_IISMOD_SDF_LSB; break; case SND_SOC_DAIFMT_I2S: + iismod &= ~S3C2412_IISMOD_LR_RLOW; iismod |= S3C2412_IISMOD_SDF_IIS; break; default: @@ -463,6 +466,31 @@ static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, switch (div_id) { case S3C_I2SV2_DIV_BCLK: + if (div > 3) { + /* convert value to bit field */ + + switch (div) { + case 16: + div = S3C2412_IISMOD_BCLK_16FS; + break; + + case 32: + div = S3C2412_IISMOD_BCLK_32FS; + break; + + case 24: + div = S3C2412_IISMOD_BCLK_24FS; + break; + + case 48: + div = S3C2412_IISMOD_BCLK_48FS; + break; + + default: + return -EINVAL; + } + } + reg = readl(i2s->regs + S3C2412_IISMOD); reg &= ~S3C2412_IISMOD_BCLK_MASK; writel(reg | div, i2s->regs + S3C2412_IISMOD); @@ -622,7 +650,7 @@ int s3c_i2sv2_probe(struct platform_device *pdev, } i2s->iis_pclk = clk_get(dev, "iis"); - if (i2s->iis_pclk == NULL) { + if (IS_ERR(i2s->iis_pclk)) { dev_err(dev, "failed to get iis_clock\n"); iounmap(i2s->regs); return -ENOENT; -- cgit v0.10.2 From 08db48f1ee1adf8919484f731d4ad6b264cfc564 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Tue, 15 Sep 2009 11:24:52 +0800 Subject: ASoC: use set_channel_map api to reorder channels for AD1938 and AD1836 Signed-off-by: Barry Song Signed-off-by: Mark Brown diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c index cd361e3..0f45a3f 100644 --- a/sound/soc/blackfin/bf5xx-ad1836.c +++ b/sound/soc/blackfin/bf5xx-ad1836.c @@ -52,6 +52,7 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7}; int ret = 0; /* set cpu DAI configuration */ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | @@ -65,6 +66,12 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; + /* set cpu DAI channel mapping */ + ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map), + channel_map, ARRAY_SIZE(channel_map), channel_map); + if (ret < 0) + return ret; + return 0; } diff --git a/sound/soc/blackfin/bf5xx-ad1938.c b/sound/soc/blackfin/bf5xx-ad1938.c index 08269e9..2ef1e50 100644 --- a/sound/soc/blackfin/bf5xx-ad1938.c +++ b/sound/soc/blackfin/bf5xx-ad1938.c @@ -61,6 +61,7 @@ static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7}; int ret = 0; /* set cpu DAI configuration */ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | @@ -75,7 +76,13 @@ static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream, return ret; /* set codec DAI slots, 8 channels, all channels are enabled */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 8); + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32); + if (ret < 0) + return ret; + + /* set cpu DAI channel mapping */ + ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map), + channel_map, ARRAY_SIZE(channel_map), channel_map); if (ret < 0) return ret; diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c index ccb5e82..a8c73cb 100644 --- a/sound/soc/blackfin/bf5xx-tdm-pcm.c +++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c @@ -43,7 +43,7 @@ #include "bf5xx-tdm.h" #include "bf5xx-sport.h" -#define PCM_BUFFER_MAX 0x10000 +#define PCM_BUFFER_MAX 0x8000 #define FRAGMENT_SIZE_MIN (4*1024) #define FRAGMENTS_MIN 2 #define FRAGMENTS_MAX 32 @@ -177,6 +177,9 @@ out: static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count) { + struct snd_pcm_runtime *runtime = substream->runtime; + struct sport_device *sport = runtime->private_data; + struct bf5xx_tdm_port *tdm_port = sport->private_data; unsigned int *src; unsigned int *dst; int i; @@ -188,7 +191,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, dst += pos * 8; while (count--) { for (i = 0; i < substream->runtime->channels; i++) - *(dst + i) = *src++; + *(dst + tdm_port->tx_map[i]) = *src++; dst += 8; } } else { @@ -198,7 +201,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, src += pos * 8; while (count--) { for (i = 0; i < substream->runtime->channels; i++) - *dst++ = *(src+i); + *dst++ = *(src + tdm_port->rx_map[i]); src += 8; } } diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c index 3096bad..600987d 100644 --- a/sound/soc/blackfin/bf5xx-tdm.c +++ b/sound/soc/blackfin/bf5xx-tdm.c @@ -46,14 +46,6 @@ #include "bf5xx-sport.h" #include "bf5xx-tdm.h" -struct bf5xx_tdm_port { - u16 tcr1; - u16 rcr1; - u16 tcr2; - u16 rcr2; - int configured; -}; - static struct bf5xx_tdm_port bf5xx_tdm; static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; @@ -181,6 +173,40 @@ static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream, bf5xx_tdm.configured = 0; } +static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + int i; + unsigned int slot; + unsigned int tx_mapped = 0, rx_mapped = 0; + + if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) || + (rx_num > BFIN_TDM_DAI_MAX_SLOTS)) + return -EINVAL; + + for (i = 0; i < tx_num; i++) { + slot = tx_slot[i]; + if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && + (!(tx_mapped & (1 << slot)))) { + bf5xx_tdm.tx_map[i] = slot; + tx_mapped |= 1 << slot; + } else + return -EINVAL; + } + for (i = 0; i < rx_num; i++) { + slot = rx_slot[i]; + if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && + (!(rx_mapped & (1 << slot)))) { + bf5xx_tdm.rx_map[i] = slot; + rx_mapped |= 1 << slot; + } else + return -EINVAL; + } + + return 0; +} + #ifdef CONFIG_PM static int bf5xx_tdm_suspend(struct snd_soc_dai *dai) { @@ -235,6 +261,7 @@ static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = { .hw_params = bf5xx_tdm_hw_params, .set_fmt = bf5xx_tdm_set_dai_fmt, .shutdown = bf5xx_tdm_shutdown, + .set_channel_map = bf5xx_tdm_set_channel_map, }; struct snd_soc_dai bf5xx_tdm_dai = { @@ -300,6 +327,8 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev) pr_err("Failed to register DAI: %d\n", ret); goto sport_config_err; } + + sport_handle->private_data = &bf5xx_tdm; return 0; sport_config_err: diff --git a/sound/soc/blackfin/bf5xx-tdm.h b/sound/soc/blackfin/bf5xx-tdm.h index 618ec3d..04189a1 100644 --- a/sound/soc/blackfin/bf5xx-tdm.h +++ b/sound/soc/blackfin/bf5xx-tdm.h @@ -9,6 +9,17 @@ #ifndef _BF5XX_TDM_H #define _BF5XX_TDM_H +#define BFIN_TDM_DAI_MAX_SLOTS 8 +struct bf5xx_tdm_port { + u16 tcr1; + u16 rcr1; + u16 tcr2; + u16 rcr2; + unsigned int tx_map[BFIN_TDM_DAI_MAX_SLOTS]; + unsigned int rx_map[BFIN_TDM_DAI_MAX_SLOTS]; + int configured; +}; + extern struct snd_soc_dai bf5xx_tdm_dai; #endif -- cgit v0.10.2 From 9b95b166789d3ec57cea8cca0d42e602b8643ab0 Mon Sep 17 00:00:00 2001 From: Miguel Aguilar Date: Wed, 2 Sep 2009 15:33:59 -0600 Subject: ASoC: Davinci: Add audio codec support for DM365 EVM This patch enables tlv320aic3101 support on DM365 EVM and it was tested on DM365 EVM rev c. Note: this patch was created based on temp/asoc branch. Signed-off-by: Miguel Aguilar Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index 4dfd4ad..047ee39 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -13,9 +13,9 @@ config SND_DAVINCI_SOC_MCASP tristate config SND_DAVINCI_SOC_EVM - tristate "SoC Audio support for DaVinci DM6446 or DM355 EVM" + tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM" depends on SND_DAVINCI_SOC - depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM + depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM select SND_DAVINCI_SOC_I2S select SND_SOC_TLV320AIC3X help diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 67414f6..7ccbe66 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -45,7 +45,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream, unsigned sysclk; /* ASP1 on DM355 EVM is clocked by an external oscillator */ - if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm()) + if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() || + machine_is_davinci_dm365_evm()) sysclk = 27000000; /* ASP0 in DM6446 EVM is clocked by U55, as configured by @@ -176,7 +177,7 @@ static struct snd_soc_dai_link da8xx_evm_dai = { .ops = &evm_ops, }; -/* davinci-evm audio machine driver */ +/* davinci dm6446, dm355 or dm365 evm audio machine driver */ static struct snd_soc_card snd_soc_card_evm = { .name = "DaVinci EVM", .platform = &davinci_soc_platform, @@ -243,7 +244,7 @@ static int __init evm_init(void) int index; int ret; - if (machine_is_davinci_evm()) { + if (machine_is_davinci_evm() || machine_is_davinci_dm365_evm()) { evm_snd_dev_data = &evm_snd_devdata; index = 0; } else if (machine_is_davinci_dm355_evm()) { -- cgit v0.10.2 From 8bb014895547eeeb9aa61a654f24e41e15919304 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 16 Sep 2009 19:38:53 +0100 Subject: ASoC: Add S3C64xx IIS CDCLK source selection CDCLK can either be an output generated by the CPU, intended for use as the CODEC master clock, or an input (probably from the CODEC) providing a master clock for the IIS block. Signed-off-by: Mark Brown diff --git a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h index 07659da..abf2fbc 100644 --- a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h +++ b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h @@ -67,6 +67,8 @@ #define S3C2412_IISMOD_BCLK_MASK (3 << 1) #define S3C2412_IISMOD_8BIT (1 << 0) +#define S3C64XX_IISMOD_CDCLKCON (1 << 12) + #define S3C2412_IISPSR_PSREN (1 << 15) #define S3C2412_IISFIC_TXFLUSH (1 << 15) diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c index 3c06c40..aaf4520 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.c +++ b/sound/soc/s3c24xx/s3c64xx-i2s.c @@ -99,6 +99,19 @@ static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, iismod |= S3C64XX_IISMOD_IMS_SYSMUX; break; + case S3C64XX_CLKSRC_CDCLK: + switch (dir) { + case SND_SOC_CLOCK_IN: + iismod |= S3C64XX_IISMOD_CDCLKCON; + break; + case SND_SOC_CLOCK_OUT: + iismod &= ~S3C64XX_IISMOD_CDCLKCON; + break; + default: + return -EINVAL; + } + break; + default: return -EINVAL; } diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.h b/sound/soc/s3c24xx/s3c64xx-i2s.h index 02148ce..abe7253 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.h +++ b/sound/soc/s3c24xx/s3c64xx-i2s.h @@ -25,6 +25,7 @@ struct clk; #define S3C64XX_CLKSRC_PCLK (0) #define S3C64XX_CLKSRC_MUX (1) +#define S3C64XX_CLKSRC_CDCLK (2) extern struct snd_soc_dai s3c64xx_i2s_dai[]; -- cgit v0.10.2 From b1cd6b9ec7c749ddfad628c8c12659591ae195e6 Mon Sep 17 00:00:00 2001 From: Jassi Date: Fri, 18 Sep 2009 15:22:27 +0900 Subject: ASoC: Return correct codec clock in s3c64xx-i2s Instead of always returnig pointer to the 'audio-bus' clock, check which clock is used to generate internal clocks and then return it's pointer. Signed-off-by: Jassi Signed-off-by: Mark Brown diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c index aaf4520..43fb253 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.c +++ b/sound/soc/s3c24xx/s3c64xx-i2s.c @@ -124,8 +124,12 @@ static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai) { struct s3c_i2sv2_info *i2s = to_info(dai); + u32 iismod = readl(i2s->regs + S3C2412_IISMOD); - return i2s->iis_cclk; + if (iismod & S3C64XX_IISMOD_IMS_SYSMUX) + return i2s->iis_cclk; + else + return i2s->iis_pclk; } EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clock); -- cgit v0.10.2 From d0f5fa17aa63262685e43b798ca0830d89786235 Mon Sep 17 00:00:00 2001 From: jassi brar Date: Sat, 19 Sep 2009 09:46:06 +0900 Subject: ASoC: Support WM8580 based audio subsystem on SMDK64xx machines New machine driver for WM8580 I2S i/f on SMDK64XX. By default SoC-Slave is set and WM8580 is configured to use it's PLLA to generate clocks from a 12MHz crystal attached to WM8580. [Added dependency on BROKEN since the IISv4 interface hasn't been merged yet, fixed the PLL API usage and removed the disabling of the PLL in the hw_free function since that'll break simultaneous playback and record -- broonie.] Signed-off-by: Jassi Signed-off-by: Mark Brown diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index 923428f..d7912f1 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig @@ -56,6 +56,15 @@ config SND_S3C24XX_SOC_JIVE_WM8750 help Sat Y if you want to add support for SoC audio on the Jive. +config SND_S3C64XX_SOC_WM8580 + tristate "SoC I2S Audio support for WM8580 on SMDK64XX" + depends on SND_S3C24XX_SOC && (MACH_SMDK6400 || MACH_SMDK6410) + depends on BROKEN + select SND_SOC_WM8580 + select SND_S3C64XX_SOC_I2S + help + Sat Y if you want to add support for SoC audio on the SMDK64XX. + config SND_S3C24XX_SOC_SMDK2443_WM9710 tristate "SoC AC97 Audio support for SMDK2443 - WM9710" depends on SND_S3C24XX_SOC && MACH_SMDK2443 diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index 99f5a7d..7790406 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile @@ -23,6 +23,7 @@ snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o +snd-soc-smdk64xx-wm8580-objs := smdk64xx_wm8580.o obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o @@ -33,4 +34,5 @@ obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o +obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/s3c24xx/smdk64xx_wm8580.c new file mode 100644 index 0000000..482aaf1 --- /dev/null +++ b/sound/soc/s3c24xx/smdk64xx_wm8580.c @@ -0,0 +1,273 @@ +/* + * smdk64xx_wm8580.c + * + * Copyright (c) 2009 Samsung Electronics Co. Ltd + * Author: Jaswinder Singh + * + * 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 "../codecs/wm8580.h" +#include "s3c24xx-pcm.h" +#include "s3c64xx-i2s.h" + +#define S3C64XX_I2S_V4 2 + +/* SMDK64XX has a 12MHZ crystal attached to WM8580 */ +#define SMDK64XX_WM8580_FREQ 12000000 + +static int smdk64xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + unsigned int pll_out; + int bfs, rfs, ret; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_U8: + case SNDRV_PCM_FORMAT_S8: + bfs = 16; + break; + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_S16_LE: + bfs = 32; + break; + default: + return -EINVAL; + } + + /* The Fvco for WM8580 PLLs must fall within [90,100]MHz. + * This criterion can't be met if we request PLL output + * as {8000x256, 64000x256, 11025x256}Hz. + * As a wayout, we rather change rfs to a minimum value that + * results in (params_rate(params) * rfs), and itself, acceptable + * to both - the CODEC and the CPU. + */ + switch (params_rate(params)) { + case 16000: + case 22050: + case 32000: + case 44100: + case 48000: + case 88200: + case 96000: + rfs = 256; + break; + case 64000: + rfs = 384; + break; + case 8000: + case 11025: + rfs = 512; + break; + default: + return -EINVAL; + } + pll_out = params_rate(params) * rfs; + + /* Set the Codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + /* Set the AP DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_CDCLK, + 0, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* We use PCLK for basic ops in SoC-Slave mode */ + ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_PCLK, + 0, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* Set WM8580 to drive MCLK from it's PLLA */ + ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK, + WM8580_CLKSRC_PLLA); + if (ret < 0) + return ret; + + /* Explicitly set WM8580-DAC to source from MCLK */ + ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_DAC_CLKSEL, + WM8580_CLKSRC_MCLK); + if (ret < 0) + return ret; + + /* Assuming the CODEC driver evaluates it's rfs too from this call */ + ret = snd_soc_dai_set_pll(codec_dai, 0, WM8580_PLLA, + SMDK64XX_WM8580_FREQ, pll_out); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_BCLK, bfs); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_RCLK, rfs); + if (ret < 0) + return ret; + + return 0; +} + +/* + * SMDK64XX WM8580 DAI operations. + */ +static struct snd_soc_ops smdk64xx_ops = { + .hw_params = smdk64xx_hw_params, +}; + +/* SMDK64xx Playback widgets */ +static const struct snd_soc_dapm_widget wm8580_dapm_widgets_pbk[] = { + SND_SOC_DAPM_HP("Front-L/R", NULL), + SND_SOC_DAPM_HP("Center/Sub", NULL), + SND_SOC_DAPM_HP("Rear-L/R", NULL), +}; + +/* SMDK64xx Capture widgets */ +static const struct snd_soc_dapm_widget wm8580_dapm_widgets_cpt[] = { + SND_SOC_DAPM_MIC("MicIn", NULL), + SND_SOC_DAPM_LINE("LineIn", NULL), +}; + +/* SMDK-PAIFTX connections */ +static const struct snd_soc_dapm_route audio_map_tx[] = { + /* MicIn feeds AINL */ + {"AINL", NULL, "MicIn"}, + + /* LineIn feeds AINL/R */ + {"AINL", NULL, "LineIn"}, + {"AINR", NULL, "LineIn"}, +}; + +/* SMDK-PAIFRX connections */ +static const struct snd_soc_dapm_route audio_map_rx[] = { + /* Front Left/Right are fed VOUT1L/R */ + {"Front-L/R", NULL, "VOUT1L"}, + {"Front-L/R", NULL, "VOUT1R"}, + + /* Center/Sub are fed VOUT2L/R */ + {"Center/Sub", NULL, "VOUT2L"}, + {"Center/Sub", NULL, "VOUT2R"}, + + /* Rear Left/Right are fed VOUT3L/R */ + {"Rear-L/R", NULL, "VOUT3L"}, + {"Rear-L/R", NULL, "VOUT3R"}, +}; + +static int smdk64xx_wm8580_init_paiftx(struct snd_soc_codec *codec) +{ + /* Add smdk64xx specific Capture widgets */ + snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_cpt, + ARRAY_SIZE(wm8580_dapm_widgets_cpt)); + + /* Set up PAIFTX audio path */ + snd_soc_dapm_add_routes(codec, audio_map_tx, ARRAY_SIZE(audio_map_tx)); + + /* All enabled by default */ + snd_soc_dapm_enable_pin(codec, "MicIn"); + snd_soc_dapm_enable_pin(codec, "LineIn"); + + /* signal a DAPM event */ + snd_soc_dapm_sync(codec); + + return 0; +} + +static int smdk64xx_wm8580_init_paifrx(struct snd_soc_codec *codec) +{ + /* Add smdk64xx specific Playback widgets */ + snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_pbk, + ARRAY_SIZE(wm8580_dapm_widgets_pbk)); + + /* Set up PAIFRX audio path */ + snd_soc_dapm_add_routes(codec, audio_map_rx, ARRAY_SIZE(audio_map_rx)); + + /* All enabled by default */ + snd_soc_dapm_enable_pin(codec, "Front-L/R"); + snd_soc_dapm_enable_pin(codec, "Center/Sub"); + snd_soc_dapm_enable_pin(codec, "Rear-L/R"); + + /* signal a DAPM event */ + snd_soc_dapm_sync(codec); + + return 0; +} + +static struct snd_soc_dai_link smdk64xx_dai[] = { +{ /* Primary Playback i/f */ + .name = "WM8580 PAIF RX", + .stream_name = "Playback", + .cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4], + .codec_dai = &wm8580_dai[WM8580_DAI_PAIFRX], + .init = smdk64xx_wm8580_init_paifrx, + .ops = &smdk64xx_ops, +}, +{ /* Primary Capture i/f */ + .name = "WM8580 PAIF TX", + .stream_name = "Capture", + .cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4], + .codec_dai = &wm8580_dai[WM8580_DAI_PAIFTX], + .init = smdk64xx_wm8580_init_paiftx, + .ops = &smdk64xx_ops, +}, +}; + +static struct snd_soc_card smdk64xx = { + .name = "smdk64xx", + .platform = &s3c24xx_soc_platform, + .dai_link = smdk64xx_dai, + .num_links = ARRAY_SIZE(smdk64xx_dai), +}; + +static struct snd_soc_device smdk64xx_snd_devdata = { + .card = &smdk64xx, + .codec_dev = &soc_codec_dev_wm8580, +}; + +static struct platform_device *smdk64xx_snd_device; + +static int __init smdk64xx_audio_init(void) +{ + int ret; + + smdk64xx_snd_device = platform_device_alloc("soc-audio", -1); + if (!smdk64xx_snd_device) + return -ENOMEM; + + platform_set_drvdata(smdk64xx_snd_device, &smdk64xx_snd_devdata); + smdk64xx_snd_devdata.dev = &smdk64xx_snd_device->dev; + ret = platform_device_add(smdk64xx_snd_device); + + if (ret) + platform_device_put(smdk64xx_snd_device); + + return ret; +} +module_init(smdk64xx_audio_init); + +MODULE_AUTHOR("Jaswinder Singh, jassi.brar@samsung.com"); +MODULE_DESCRIPTION("ALSA SoC SMDK64XX WM8580"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From d62ab3589462d406e98731799361f46095467882 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 21 Sep 2009 04:21:47 -0700 Subject: ASoC: Convert soc-cache to use C99 style initialisers for the table Signed-off-by: Mark Brown diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 404231e..d2505e8 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -179,10 +179,20 @@ static struct { unsigned int (*read)(struct snd_soc_codec *, unsigned int); unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int); } io_types[] = { - { 7, 9, snd_soc_7_9_write, snd_soc_7_9_spi_write, snd_soc_7_9_read }, - { 8, 8, snd_soc_8_8_write, NULL, snd_soc_8_8_read, NULL }, - { 8, 16, snd_soc_8_16_write, NULL, snd_soc_8_16_read, - snd_soc_8_16_read_i2c }, + { + .addr_bits = 7, .data_bits = 9, + .write = snd_soc_7_9_write, .read = snd_soc_7_9_read, + .spi_write = snd_soc_7_9_spi_write + }, + { + .addr_bits = 8, .data_bits = 8, + .write = snd_soc_8_8_write, .read = snd_soc_8_8_read, + }, + { + .addr_bits = 8, .data_bits = 16, + .write = snd_soc_8_16_write, .read = snd_soc_8_16_read, + .i2c_read = snd_soc_8_16_read_i2c, + }, }; /** -- cgit v0.10.2 From 766df6d98f9c28dfc6f72c23a010819719e4c3e0 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Wed, 23 Sep 2009 11:51:04 -0400 Subject: ASoC: Blackfin I2S: use dai state rather than local counter Since the active field of the dai already tells us the stream activity, the local counter variable is redundant and can be replaced. Signed-off-by: Barry Song Signed-off-by: Mike Frysinger Signed-off-by: Mark Brown diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c index 1e9d161..fe2c35d 100644 --- a/sound/soc/blackfin/bf5xx-i2s.c +++ b/sound/soc/blackfin/bf5xx-i2s.c @@ -49,7 +49,6 @@ struct bf5xx_i2s_port { u16 rcr1; u16 tcr2; u16 rcr2; - int counter; int configured; }; @@ -133,16 +132,6 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, return ret; } -static int bf5xx_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - pr_debug("%s enter\n", __func__); - - /*this counter is used for counting how many pcm streams are opened*/ - bf5xx_i2s.counter++; - return 0; -} - static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -201,9 +190,8 @@ static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { pr_debug("%s enter\n", __func__); - bf5xx_i2s.counter--; /* No active stream, SPORT is allowed to be configured again. */ - if (!bf5xx_i2s.counter) + if (!dai->active) bf5xx_i2s.configured = 0; } @@ -284,7 +272,6 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai) SNDRV_PCM_FMTBIT_S32_LE) static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = { - .startup = bf5xx_i2s_startup, .shutdown = bf5xx_i2s_shutdown, .hw_params = bf5xx_i2s_hw_params, .set_fmt = bf5xx_i2s_set_dai_fmt, -- cgit v0.10.2 From f34762b64704814838619c1d258bebf19004f5cd Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Fri, 25 Sep 2009 13:30:26 +0100 Subject: ASoC: pxa-ssp increase max_channels to 8 When running in TDM mode there can be more than 2 channels used. Datasheet has figures for upto 8 channels so increase max_channels on all SSP interfaces to this figure. Signed-off-by: Graeme Gregory Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 57f201c..a2b1e8f 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -760,13 +760,13 @@ struct snd_soc_dai pxa_ssp_dai[] = { .resume = pxa_ssp_resume, .playback = { .channels_min = 1, - .channels_max = 2, + .channels_max = 8, .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, .capture = { .channels_min = 1, - .channels_max = 2, + .channels_max = 8, .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, @@ -780,13 +780,13 @@ struct snd_soc_dai pxa_ssp_dai[] = { .resume = pxa_ssp_resume, .playback = { .channels_min = 1, - .channels_max = 2, + .channels_max = 8, .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, .capture = { .channels_min = 1, - .channels_max = 2, + .channels_max = 8, .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, @@ -801,13 +801,13 @@ struct snd_soc_dai pxa_ssp_dai[] = { .resume = pxa_ssp_resume, .playback = { .channels_min = 1, - .channels_max = 2, + .channels_max = 8, .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, .capture = { .channels_min = 1, - .channels_max = 2, + .channels_max = 8, .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, @@ -822,13 +822,13 @@ struct snd_soc_dai pxa_ssp_dai[] = { .resume = pxa_ssp_resume, .playback = { .channels_min = 1, - .channels_max = 2, + .channels_max = 8, .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, .capture = { .channels_min = 1, - .channels_max = 2, + .channels_max = 8, .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, -- cgit v0.10.2 From be2500b8353d41463399e997fe8562f772dcaaba Mon Sep 17 00:00:00 2001 From: "Lopez Cruz, Misael" Date: Fri, 25 Sep 2009 21:02:49 -0500 Subject: ASoC: Add PDM DAI format definition Add DAI format definition for PDM interfaces. Signed-off-by: Misael Lopez Cruz Signed-off-by: Mark Brown diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index e0c7fa7..ca24e7f 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -30,6 +30,7 @@ struct snd_pcm_substream; #define SND_SOC_DAIFMT_DSP_A 3 /* L data MSB after FRM LRC */ #define SND_SOC_DAIFMT_DSP_B 4 /* L data MSB during FRM LRC */ #define SND_SOC_DAIFMT_AC97 5 /* AC97 */ +#define SND_SOC_DAIFMT_PDM 6 /* Pulse density modulation */ /* left and right justified also known as MSB and LSB respectively */ #define SND_SOC_DAIFMT_MSB SND_SOC_DAIFMT_LEFT_J -- cgit v0.10.2 From 4fa9c1a5953441e06dbde7b6a655cbf6618e61dd Mon Sep 17 00:00:00 2001 From: Chaithrika U S Date: Wed, 30 Sep 2009 17:32:27 -0400 Subject: ASoC: DaVinci: McASP FIFO related updates The DMA params for McASP with FIFO has been updated so that it works for various FIFO levels. A member- 'fifo_level' has been added to the DMA params data structure. The fifo_level can be adjusted by the tx[rx]_numevt platform data. This is relevant only for DA8xx/OMAP-L1xx platforms. This implementation has been tested for numevt values 1, 2, 4, 8. Signed-off-by: Chaithrika U S Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 4ae7070..2ab8093 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -397,6 +397,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, } dma_params->acnt = dma_params->data_type; + dma_params->fifo_level = 0; + rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1); xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1); diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 5d1f98a..50ad051 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -714,16 +714,13 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, struct davinci_pcm_dma_params *dma_params = &dev->dma_params[substream->stream]; int word_length; - u8 numevt; + u8 fifo_level; davinci_hw_common_param(dev, substream->stream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - numevt = dev->txnumevt; + fifo_level = dev->txnumevt; else - numevt = dev->rxnumevt; - - if (!numevt) - numevt = 1; + fifo_level = dev->rxnumevt; if (dev->op_mode == DAVINCI_MCASP_DIT_MODE) davinci_hw_dit_param(dev); @@ -751,12 +748,12 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - if (dev->version == MCASP_VERSION_2) { - dma_params->data_type *= numevt; - dma_params->acnt = 4 * numevt; - } else + if (dev->version == MCASP_VERSION_2 && !fifo_level) + dma_params->acnt = 4; + else dma_params->acnt = dma_params->data_type; + dma_params->fifo_level = fifo_level; davinci_config_channel_size(dev, word_length); return 0; diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 359e99e..1152d8b 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -66,38 +66,53 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) dma_addr_t dma_pos; dma_addr_t src, dst; unsigned short src_bidx, dst_bidx; + unsigned short src_cidx, dst_cidx; unsigned int data_type; unsigned short acnt; unsigned int count; + unsigned int fifo_level; period_size = snd_pcm_lib_period_bytes(substream); dma_offset = prtd->period * period_size; dma_pos = runtime->dma_addr + dma_offset; + fifo_level = prtd->params->fifo_level; pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d " "dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size); data_type = prtd->params->data_type; count = period_size / data_type; + if (fifo_level) + count /= fifo_level; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { src = dma_pos; dst = prtd->params->dma_addr; src_bidx = data_type; dst_bidx = 0; + src_cidx = data_type * fifo_level; + dst_cidx = 0; } else { src = prtd->params->dma_addr; dst = dma_pos; src_bidx = 0; dst_bidx = data_type; + src_cidx = 0; + dst_cidx = data_type * fifo_level; } acnt = prtd->params->acnt; edma_set_src(lch, src, INCR, W8BIT); edma_set_dest(lch, dst, INCR, W8BIT); - edma_set_src_index(lch, src_bidx, 0); - edma_set_dest_index(lch, dst_bidx, 0); - edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC); + + edma_set_src_index(lch, src_bidx, src_cidx); + edma_set_dest_index(lch, dst_bidx, dst_cidx); + + if (!fifo_level) + edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC); + else + edma_set_transfer_params(lch, acnt, fifo_level, count, + fifo_level, ABSYNC); prtd->period++; if (unlikely(prtd->period >= runtime->periods)) diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h index 8746606..c8b0d2b 100644 --- a/sound/soc/davinci/davinci-pcm.h +++ b/sound/soc/davinci/davinci-pcm.h @@ -23,6 +23,7 @@ struct davinci_pcm_dma_params { enum dma_event_q eventq_no; /* event queue number */ unsigned char data_type; /* xfer data type */ unsigned char convert_mono_stereo; + unsigned int fifo_level; }; -- cgit v0.10.2 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 diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index 5104c8a..f30f86b 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 v0.10.2 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 diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 6b32a28..dac3977 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 e542027..810a563 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 ec09cb6..36d3fba 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 v0.10.2 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 diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index ae083eb..90ec8c5 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 v0.10.2 From 140318aaa924ce9664ff59366993228cf1547f1d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 1 Oct 2009 08:40:32 +0200 Subject: ASoC: Fix snd_soc_dai_set_pll() calls in neo1973_*.c Fix the missing argument of snd_soc_dai_set_pll() in neo1973_*.c, which was forgotten in the commit 85488037bb. Signed-off-by: Takashi Iwai diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c index 6ddd1b3..26409a9 100644 --- a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c @@ -133,7 +133,7 @@ static int neo1973_gta02_hifi_hw_free(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; /* disable the PLL */ - return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0); + return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0); } /* @@ -183,7 +183,7 @@ static int neo1973_gta02_voice_hw_params( return ret; /* configue and enable PLL for 12.288MHz output */ - ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, + ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, iis_clkrate / 4, 12288000); if (ret < 0) return ret; @@ -197,7 +197,7 @@ static int neo1973_gta02_voice_hw_free(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; /* disable the PLL */ - return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0); + return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0); } static struct snd_soc_ops neo1973_gta02_voice_ops = { diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 16009eb..c9b7948 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -153,7 +153,7 @@ static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream) pr_debug("Entered %s\n", __func__); /* disable the PLL */ - return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0); + return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0); } /* @@ -203,7 +203,7 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, return ret; /* configue and enable PLL for 12.288MHz output */ - ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, + ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, iis_clkrate / 4, 12288000); if (ret < 0) return ret; @@ -219,7 +219,7 @@ static int neo1973_voice_hw_free(struct snd_pcm_substream *substream) pr_debug("Entered %s\n", __func__); /* disable the PLL */ - return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0); + return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0); } static struct snd_soc_ops neo1973_voice_ops = { -- cgit v0.10.2 From 88439ac793934a47f47ad285656b63d09f5937c8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 1 Oct 2009 10:32:47 +0300 Subject: ASoC: add support for multiple cards/codecs in debugfs In order to support multiple codecs on the same system in the debugfs the directory hierarchy need to be changed by adding directory per codec under the asoc direcorty: debugfs/asoc/{dev_name(socdev->dev)}-{codec->name}/codec_reg /dapm_pop_time /dapm/{widgets} With the original implementation only the debugfs files are only created for the first codec, other codecs loaded later would fail to create the debugfs files (since they are already exist). Furthermore in this situation any of the codecs has been removed, would cause the debugfs entries to disappear, regardless if the codec, which created them are still loaded (the one which loaded first). Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 475cb7e..0b1f917a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -413,6 +413,7 @@ struct snd_soc_codec { unsigned int num_dai; #ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_codec_root; struct dentry *debugfs_reg; struct dentry *debugfs_pop_time; struct dentry *debugfs_dapm; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f5b356f..e4ab36d 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1254,21 +1254,35 @@ static const struct file_operations codec_reg_fops = { static void soc_init_codec_debugfs(struct snd_soc_codec *codec) { + char codec_root[128]; + + snprintf(codec_root, sizeof(codec_root), + "%s-%s", dev_name(codec->socdev->dev), codec->name); + + codec->debugfs_codec_root = debugfs_create_dir(codec_root, + debugfs_root); + if (!codec->debugfs_codec_root) { + printk(KERN_WARNING + "ASoC: Failed to create codec debugfs directory\n"); + return; + } + codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, - debugfs_root, codec, - &codec_reg_fops); + codec->debugfs_codec_root, + codec, &codec_reg_fops); if (!codec->debugfs_reg) printk(KERN_WARNING "ASoC: Failed to create codec register debugfs file\n"); codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744, - debugfs_root, + codec->debugfs_codec_root, &codec->pop_time); if (!codec->debugfs_pop_time) printk(KERN_WARNING "Failed to create pop time debugfs file\n"); - codec->debugfs_dapm = debugfs_create_dir("dapm", debugfs_root); + codec->debugfs_dapm = debugfs_create_dir("dapm", + codec->debugfs_codec_root); if (!codec->debugfs_dapm) printk(KERN_WARNING "Failed to create DAPM debugfs directory\n"); @@ -1278,9 +1292,7 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec) static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) { - debugfs_remove_recursive(codec->debugfs_dapm); - debugfs_remove(codec->debugfs_pop_time); - debugfs_remove(codec->debugfs_reg); + debugfs_remove_recursive(codec->debugfs_codec_root); } #else -- cgit v0.10.2 From ce3e3737a3361e0c7030f8598eec36bb82050de6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 2 Oct 2009 09:17:37 +0300 Subject: ASoC: Improve the debugfs hierarchy Change the way the debugfs entries are created: If the codec->dev is valid, than use: debugfs/asoc/{codec->name}.{dev_name(codec->dev)}/ if the codec->dev is NULL: debugfs/asoc/{codec->name}/ as root for the debugfs entries. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e4ab36d..1dec9d2 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1256,8 +1256,12 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec) { char codec_root[128]; - snprintf(codec_root, sizeof(codec_root), - "%s-%s", dev_name(codec->socdev->dev), codec->name); + if (codec->dev) + snprintf(codec_root, sizeof(codec_root), + "%s.%s", codec->name, dev_name(codec->dev)); + else + snprintf(codec_root, sizeof(codec_root), + "%s", codec->name); codec->debugfs_codec_root = debugfs_create_dir(codec_root, debugfs_root); -- cgit v0.10.2 From 1642e3d42a062221e4df18df260d4703d18ca519 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 5 Oct 2009 16:24:26 +0100 Subject: ASoC: Simplify code for DAPM widget updates We don't need to check for an event callback since we also check for an appropriate event flag when applying mux status changes. Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8eaf1b6..6137646 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1786,19 +1786,19 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mutex_lock(&widget->codec->mutex); widget->value = val; dapm_mux_update_power(widget, kcontrol, mask, mux, val, e); - if (widget->event) { - if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { - ret = widget->event(widget, - kcontrol, SND_SOC_DAPM_PRE_REG); - if (ret < 0) - goto out; - } - ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); - if (widget->event_flags & SND_SOC_DAPM_POST_REG) - ret = widget->event(widget, - kcontrol, SND_SOC_DAPM_POST_REG); - } else - ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); + + if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { + ret = widget->event(widget, + kcontrol, SND_SOC_DAPM_PRE_REG); + if (ret < 0) + goto out; + } + + ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); + + if (widget->event_flags & SND_SOC_DAPM_POST_REG) + ret = widget->event(widget, + kcontrol, SND_SOC_DAPM_POST_REG); out: mutex_unlock(&widget->codec->mutex); @@ -1883,19 +1883,19 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, mutex_lock(&widget->codec->mutex); widget->value = val; dapm_mux_update_power(widget, kcontrol, mask, mux, val, e); - if (widget->event) { - if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { - ret = widget->event(widget, - kcontrol, SND_SOC_DAPM_PRE_REG); - if (ret < 0) - goto out; - } - ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); - if (widget->event_flags & SND_SOC_DAPM_POST_REG) - ret = widget->event(widget, - kcontrol, SND_SOC_DAPM_POST_REG); - } else - ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); + + if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { + ret = widget->event(widget, + kcontrol, SND_SOC_DAPM_PRE_REG); + if (ret < 0) + goto out; + } + + ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); + + if (widget->event_flags & SND_SOC_DAPM_POST_REG) + ret = widget->event(widget, + kcontrol, SND_SOC_DAPM_POST_REG); out: mutex_unlock(&widget->codec->mutex); -- cgit v0.10.2 From 3a65577d2199a7b33c85fd32838020c39da200f3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 5 Oct 2009 17:23:30 +0100 Subject: ASoC: Push DAPM enumeration register change test out Don't assume that enumerations are backed by registers when updating mux power. Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6137646..311467b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1202,8 +1202,8 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec) /* test and update the power status of a mux widget */ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol, int mask, - int mux, int val, struct soc_enum *e) + struct snd_kcontrol *kcontrol, int change, + int mux, struct soc_enum *e) { struct snd_soc_dapm_path *path; int found = 0; @@ -1212,7 +1212,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, widget->id != snd_soc_dapm_value_mux) return -ENODEV; - if (!snd_soc_test_bits(widget->codec, e->reg, mask, val)) + if (!change) return 0; /* find dapm widget path assoc with kcontrol */ @@ -1765,7 +1765,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val, mux; + unsigned int val, mux, change; unsigned int mask, bitmask; int ret = 0; @@ -1785,7 +1785,8 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mutex_lock(&widget->codec->mutex); widget->value = val; - dapm_mux_update_power(widget, kcontrol, mask, mux, val, e); + change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + dapm_mux_update_power(widget, kcontrol, change, mux, e); if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { ret = widget->event(widget, @@ -1864,7 +1865,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val, mux; + unsigned int val, mux, change; unsigned int mask; int ret = 0; @@ -1882,7 +1883,8 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, mutex_lock(&widget->codec->mutex); widget->value = val; - dapm_mux_update_power(widget, kcontrol, mask, mux, val, e); + change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + dapm_mux_update_power(widget, kcontrol, change, mux, e); if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { ret = widget->event(widget, -- cgit v0.10.2 From d2b247a8be57647d1745535acd58169fbcbe431a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 6 Oct 2009 15:21:04 +0100 Subject: ASoC: Add virtual enumeration support for DAPM muxes Sometimes it is desirable to have a mux which does not reflect any direct register configuration but which will instead only have an effect implicitly (for example, as a result of changing which parts of the device are powered up). Provide a virtual mux for this purpose. Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 67224db..c5c95e1d 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -206,6 +206,12 @@ .get = snd_soc_dapm_get_enum_double, \ .put = snd_soc_dapm_put_enum_double, \ .private_value = (unsigned long)&xenum } +#define SOC_DAPM_ENUM_VIRT(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_dapm_get_enum_virt, \ + .put = snd_soc_dapm_put_enum_virt, \ + .private_value = (unsigned long)&xenum } #define SOC_DAPM_VALUE_ENUM(xname, xenum) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ @@ -260,6 +266,10 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 311467b..d2af872 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1808,6 +1808,54 @@ out: EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); /** + * snd_soc_dapm_get_enum_virt - Get virtual DAPM mux + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Returns 0 for success. + */ +int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = widget->value; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); + +/** + * snd_soc_dapm_put_enum_virt - Set virtual DAPM mux + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Returns 0 for success. + */ +int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = + (struct soc_enum *)kcontrol->private_value; + int change; + int ret = 0; + + if (ucontrol->value.enumerated.item[0] >= e->max) + return -EINVAL; + + mutex_lock(&widget->codec->mutex); + + change = widget->value != ucontrol->value.enumerated.item[0]; + widget->value = ucontrol->value.enumerated.item[0]; + dapm_mux_update_power(widget, kcontrol, change, widget->value, e); + + mutex_unlock(&widget->codec->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); + +/** * snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get * callback * @kcontrol: mixer control -- cgit v0.10.2 From 69d2c2ae1dffac5fcd6130e459f250ae035b678f Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 8 Oct 2009 18:19:49 +0200 Subject: ASoC: at91sam9g20ek_2mmc board uses same audio connexion as at91sam9g20ek The modified revision of at91sam9g20 Evaluation Kit rev. C and onwards share with previous ones its audio connexion to Wolfson wm8731. Modify the SoC file to extend the machine ID checking. Signed-off-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 885ba01..e028744 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -207,7 +207,7 @@ static int __init at91sam9g20ek_init(void) struct clk *pllb; int ret; - if (!machine_is_at91sam9g20ek()) + if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc())) return -ENODEV; /* -- cgit v0.10.2 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 diff --git a/include/sound/tpa6130a2-plat.h b/include/sound/tpa6130a2-plat.h new file mode 100644 index 0000000..e8c901e --- /dev/null +++ b/include/sound/tpa6130a2-plat.h @@ -0,0 +1,30 @@ +/* + * TPA6130A2 driver platform header + * + * Copyright (C) Nokia Corporation + * + * Written by 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_PLAT_H +#define TPA6130A2_PLAT_H + +struct tpa6130a2_platform_data { + int power_gpio; +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3c46f34..fab01c9 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 fc1c458..2f14391 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 0000000..1b77c95 --- /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 0000000..6a794f1 --- /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 v0.10.2 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 diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 1b77c95..0a6e7b4 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 v0.10.2 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 diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 0a6e7b4..6b650c1 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 6a794f1..57e867f 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 v0.10.2 From ed9d040d40942e9c48167f9f37f86fab8e0e5e17 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 12 Oct 2009 21:17:09 +0100 Subject: ASoC: S3C: Remove Remove the include from arch/arm/plat-s3c/include/plat/audio.h as it provides nothing to the current kernel and is not in any future plans for the system. Signed-off-by: Ben Dooks Signed-off-by: Simtec Linux Team Signed-off-by: Mark Brown diff --git a/arch/arm/plat-s3c/include/plat/audio.h b/arch/arm/plat-s3c/include/plat/audio.h deleted file mode 100644 index de0e8da..0000000 --- a/arch/arm/plat-s3c/include/plat/audio.h +++ /dev/null @@ -1,45 +0,0 @@ -/* arch/arm/mach-s3c2410/include/mach/audio.h - * - * Copyright (c) 2004-2005 Simtec Electronics - * http://www.simtec.co.uk/products/SWLINUX/ - * Ben Dooks - * - * S3C24XX - Audio platfrom_device info - * - * 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 __ASM_ARCH_AUDIO_H -#define __ASM_ARCH_AUDIO_H __FILE__ - -/* struct s3c24xx_iis_ops - * - * called from the s3c24xx audio core to deal with the architecture - * or the codec's setup and control. - * - * the pointer to itself is passed through in case the caller wants to - * embed this in an larger structure for easy reference to it's context. -*/ - -struct s3c24xx_iis_ops { - struct module *owner; - - int (*startup)(struct s3c24xx_iis_ops *me); - void (*shutdown)(struct s3c24xx_iis_ops *me); - int (*suspend)(struct s3c24xx_iis_ops *me); - int (*resume)(struct s3c24xx_iis_ops *me); - - int (*open)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm); - int (*close)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm); - int (*prepare)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm, struct snd_pcm_runtime *rt); -}; - -struct s3c24xx_platdata_iis { - const char *codec_clk; - struct s3c24xx_iis_ops *ops; - int (*match_dev)(struct device *dev); -}; - -#endif /* __ASM_ARCH_AUDIO_H */ diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index c9b7948..77de6c5 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c index 11c45a3..28b0ab2 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.c +++ b/sound/soc/s3c24xx/s3c-i2s-v2.c @@ -32,7 +32,6 @@ #include -#include #include #include "s3c-i2s-v2.h" diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c index a587ec4..ac5e47b 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.c +++ b/sound/soc/s3c24xx/s3c2412-i2s.c @@ -34,7 +34,6 @@ #include -#include #include #include diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c index fc1beb0..b25e9f9 100644 --- a/sound/soc/s3c24xx/s3c2443-ac97.c +++ b/sound/soc/s3c24xx/s3c2443-ac97.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c index 40e2c47..c76b8bb 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/s3c24xx/s3c24xx-i2s.c @@ -32,7 +32,7 @@ #include #include #include -#include + #include #include diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c index 5cbbdc8..27cf097 100644 --- a/sound/soc/s3c24xx/s3c24xx-pcm.c +++ b/sound/soc/s3c24xx/s3c24xx-pcm.c @@ -29,7 +29,6 @@ #include #include #include -#include #include "s3c24xx-pcm.h" diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c index 43fb253..b67eed5 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.c +++ b/sound/soc/s3c24xx/s3c64xx-i2s.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include -- cgit v0.10.2 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 diff --git a/include/sound/soc.h b/include/sound/soc.h index 0b1f917a..b1245e3 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -223,11 +223,6 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, int addr_bits, int data_bits, enum snd_soc_control_type control); -#ifdef CONFIG_PM -int snd_soc_suspend_device(struct device *dev); -int snd_soc_resume_device(struct device *dev); -#endif - /* pcm <-> DAI connect */ void snd_soc_free_pcms(struct snd_soc_device *socdev); int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid); diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index ca1e24a..59bb16d 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 72abc5a..714114b 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 9cb8e50..bd7eecb 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 25870a4..268cab2 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 3be5c0b..a09b23e 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 90ec8c5..54189fb 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 d3fd4f2..0e59219 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 9b27efb..8f73052 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 a9829aa..a0bbb28 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 882604e..b48804b 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 fe1307b..94cdb81 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 1685cfb..8d4fd3c 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 416fb3c..b9b096a 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 5030320..b5c6f2c 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 3f530f8..d8d8f68 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 686e5aa..4cb6b10 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, }; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1dec9d2..fa0da3c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -790,45 +790,6 @@ static int soc_resume(struct device *dev) return 0; } - -/** - * snd_soc_suspend_device: Notify core of device suspend - * - * @dev: Device being suspended. - * - * In order to ensure that the entire audio subsystem is suspended in a - * coordinated fashion ASoC devices should suspend themselves when - * called by ASoC. When the standard kernel suspend process asks the - * device to suspend it should call this function to initiate a suspend - * of the entire ASoC card. - * - * \note Currently this function is stubbed out. - */ -int snd_soc_suspend_device(struct device *dev) -{ - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_suspend_device); - -/** - * snd_soc_resume_device: Notify core of device resume - * - * @dev: Device being resumed. - * - * In order to ensure that the entire audio subsystem is resumed in a - * coordinated fashion ASoC devices should resume themselves when called - * by ASoC. When the standard kernel resume process asks the device - * to resume it should call this function. Once all the components of - * the card have notified that they are ready to be resumed the card - * will be resumed. - * - * \note Currently this function is stubbed out. - */ -int snd_soc_resume_device(struct device *dev) -{ - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_resume_device); #else #define soc_suspend NULL #define soc_resume NULL -- cgit v0.10.2 From 640fb39e386a0dac9014e5b8a512de0950e30288 Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Wed, 14 Oct 2009 09:20:26 +0200 Subject: ASoC: finally enable support for eXeda and CM-X300 Signed-off-by: Igor Grinberg Signed-off-by: Mike Rapoport CC: Mark Brown CC: alsa-devel@alsa-project.org Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index dcb3181..d4f4031 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -90,7 +90,8 @@ config SND_PXA2XX_SOC_E800 config SND_PXA2XX_SOC_EM_X270 tristate "SoC Audio support for CompuLab EM-x270, eXeda and CM-X300" - depends on SND_PXA2XX_SOC && MACH_EM_X270 + depends on SND_PXA2XX_SOC && (MACH_EM_X270 || MACH_EXEDA || \ + MACH_CM_X300) select SND_PXA2XX_SOC_AC97 select SND_SOC_WM9712 help -- cgit v0.10.2 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 diff --git a/include/sound/tlv320dac33-plat.h b/include/sound/tlv320dac33-plat.h new file mode 100644 index 0000000..5858d06 --- /dev/null +++ b/include/sound/tlv320dac33-plat.h @@ -0,0 +1,20 @@ +/* + * Platform header for 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. + */ + +#ifndef __TLV320DAC33_PLAT_H +#define __TLV320DAC33_PLAT_H + +struct tlv320dac33_platform_data { + int power_gpio; +}; + +#endif /* __TLV320DAC33_PLAT_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index fab01c9..d30fce7 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 2f14391..8f519ee 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 0000000..3ca8934 --- /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 0000000..0fedd70 --- /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 v0.10.2 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 diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 4df7c6c..559e9b2 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 v0.10.2 From e697cd410a0c3aaea697c9915837e99933d8935b Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Mon, 19 Oct 2009 16:10:58 +0200 Subject: ASoC: au1x: psc-ac97: verify correct codec register was read Verify that the correct register has been received from the codec. Signed-off-by: Manuel Lauss Signed-off-by: Mark Brown diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index a521aa9..efe2afd 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -61,7 +61,8 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97, { /* FIXME */ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; - unsigned short data, retry, tmo; + unsigned short retry, tmo; + unsigned long data; au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); au_sync(); @@ -79,15 +80,19 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97, && --tmo) udelay(2); - data = au_readl(AC97_CDC(pscdata)) & 0xffff; + data = au_readl(AC97_CDC(pscdata)); au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); au_sync(); mutex_unlock(&pscdata->lock); + + if (reg != ((data >> 16) & 0x7f)) + tmo = 1; /* wrong register, try again */ + } while (--retry && !tmo); - return retry ? data : 0xffff; + return retry ? data & 0xffff : 0xffff; } /* AC97 controller writes to codec register */ -- cgit v0.10.2 From 8d567b6b441bfcc20e8cbebc0dc376b2e280cd88 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Mon, 19 Oct 2009 16:10:59 +0200 Subject: ASoC: au1x: psc-ac97: reorganize timeouts Codec read/write functions: wait 21us between the pokings of hardware. Add timeouts to unbounded loops waiting for bits to change. Signed-off-by: Manuel Lauss Signed-off-by: Mark Brown diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index efe2afd..2a06a9c 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -75,10 +75,12 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97, AC97_CDC(pscdata)); au_sync(); - tmo = 2000; - while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) - && --tmo) - udelay(2); + tmo = 20; + do { + udelay(21); + if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD) + break; + } while (--tmo); data = au_readl(AC97_CDC(pscdata)); @@ -114,10 +116,12 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg, AC97_CDC(pscdata)); au_sync(); - tmo = 2000; - while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) - && --tmo) - udelay(2); + tmo = 20; + do { + udelay(21); + if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD) + break; + } while (--tmo); au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); au_sync(); @@ -200,7 +204,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, /* FIXME */ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; unsigned long r, ro, stat; - int chans, stype = SUBSTREAM_TYPE(substream); + int chans, t, stype = SUBSTREAM_TYPE(substream); chans = params_channels(params); @@ -242,8 +246,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, au_sync(); /* ...wait for it... */ - while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) - asm volatile ("nop"); + t = 100; + while ((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) && --t) + msleep(1); + + if (!t) + printk(KERN_ERR "PSC-AC97: can't disable!\n"); /* ...write config... */ au_writel(r, AC97_CFG(pscdata)); @@ -254,8 +262,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, au_sync(); /* ...and wait for ready bit */ - while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) - asm volatile ("nop"); + t = 100; + while ((!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && --t) + msleep(1); + + if (!t) + printk(KERN_ERR "PSC-AC97: can't enable!\n"); mutex_unlock(&pscdata->lock); -- cgit v0.10.2 From 4f066173fe8deb8874f41917e5d26ea2e0c46e3b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 17 Oct 2009 08:32:56 +0200 Subject: ASoC: Move dereference after NULL test If the NULL test on jack is needed, then the derefernce should be after the NULL test. A simplified version of the semantic match that detects this problem is as follows (http://coccinelle.lip6.fr/): // @match exists@ expression x, E; identifier fld; @@ * x->fld ... when != \(x = E\|&x\) * x == NULL // Signed-off-by: Julia Lawall Signed-off-by: Mark Brown diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 1d455ab7..1212414 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -58,7 +58,7 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_new); */ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) { - struct snd_soc_codec *codec = jack->card->codec; + struct snd_soc_codec *codec; struct snd_soc_jack_pin *pin; int enable; int oldstatus; @@ -67,6 +67,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) WARN_ON_ONCE(!jack); return; } + codec = jack->card->codec; mutex_lock(&codec->mutex); -- cgit v0.10.2 From 02624621a58d7030e0e56f1e3df490202e59056c Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 21 Oct 2009 04:40:55 +0200 Subject: ASoC: Amstrad Delta minor cleanups Hi Mark, Here is a patch that corrects small omissions I have found in my code. Signed-off-by: Janusz Krzysztofik Signed-off-by: Mark Brown diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 5a5166a..ae0fc9b 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -40,7 +40,7 @@ /* Board specific DAPM widgets */ - const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = { +static const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = { /* Handset */ SND_SOC_DAPM_MIC("Mouthpiece", NULL), SND_SOC_DAPM_HP("Earpiece", NULL), @@ -81,7 +81,7 @@ static const char *ams_delta_audio_mode[] = (1 << AMS_DELTA_SPEAKER)) #define AMS_DELTA_SPEAKERPHONE (AMS_DELTA_HANDSFREE | (1 << AMS_DELTA_AGC)) -unsigned short ams_delta_audio_mode_pins[] = { +static const unsigned short ams_delta_audio_mode_pins[] = { AMS_DELTA_MIXED, AMS_DELTA_HANDSET, AMS_DELTA_HANDSFREE, -- cgit v0.10.2 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 diff --git a/sound/soc/codecs/tlv320dac33.h b/sound/soc/codecs/tlv320dac33.h index 0fedd70..eb8ae07 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 v0.10.2 From 0ffc11800cb2a74b05c2f5b28966ebd50b27f70c Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 21 Oct 2009 23:10:03 +0200 Subject: ASoC: OMAP: Don't try to set unsupported OMAP_DMA_DATA_BURST_16 on OMAP1 After DMA burst mode has been introduced in sound/soc/omap/omap-pcm.c, omap_pcm_prepare() unconditionally calls: omap_set_dma_src_burst_mode(prtd->dma_ch, OMAP_DMA_DATA_BURST_16); omap_set_dma_dest_burst_mode(prtd->dma_ch, OMAP_DMA_DATA_BURST_16); Current implementation of those two functions found in arch/arm/plat-ompa/dma.c doesn't support OMAP_DMA_DATA_BURST_16 on OMAP1 at all, so they both end with BUG() on that machine. That results in ASoC being completely unusable, at least on my OMAP5910 based Amstrad Delta. The patch corrects the problem by not calling those two functions when run on OMAP1 class based machines. Created against linux-2.6.32-rc5. Tested on Amstrad Delta. Signed-off-by: Janusz Krzysztofik Acked-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 5735945..6a829ee 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -195,8 +195,12 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) else omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ); - omap_set_dma_src_burst_mode(prtd->dma_ch, OMAP_DMA_DATA_BURST_16); - omap_set_dma_dest_burst_mode(prtd->dma_ch, OMAP_DMA_DATA_BURST_16); + if (!(cpu_class_is_omap1())) { + omap_set_dma_src_burst_mode(prtd->dma_ch, + OMAP_DMA_DATA_BURST_16); + omap_set_dma_dest_burst_mode(prtd->dma_ch, + OMAP_DMA_DATA_BURST_16); + } return 0; } -- cgit v0.10.2 From 0b83ddebc6e884dc0221358cf68c461520fbdd8e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 22 Oct 2009 13:26:45 +0300 Subject: MFD: twl4030: add twl4030_codec MFD as a new child to the core New MFD child to twl4030 MFD device. Reason for the twl4030_codec MFD: the vibra control is actually in the codec part of the twl4030. If both the vibra and the audio functionality is needed from the twl4030 at the same time, than they need to control the codec power and APLL at the same time without breaking the other driver. Also these two has to be able to work without the need for the other driver. This MFD device will be used by the drivers, which needs resources from the twl4030 codec like audio and vibra. The platform specific configuration data is passed along to the child drivers (audio, vibra). Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz Signed-off-by: Mark Brown diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 570be13..08f2d07 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -121,6 +121,12 @@ config TWL4030_POWER and load scripts controling which resources are switched off/on or reset when a sleep, wakeup or warm reset event occurs. +config TWL4030_CODEC + bool + depends on TWL4030_CORE + select MFD_CORE + default n + config MFD_TMIO bool default n diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f3b277b..af0fc90 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o +obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o obj-$(CONFIG_MFD_MC13783) += mc13783-core.o diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c new file mode 100644 index 0000000..9710307 --- /dev/null +++ b/drivers/mfd/twl4030-codec.c @@ -0,0 +1,241 @@ +/* + * MFD driver for twl4030 codec submodule + * + * 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 + +#define TWL4030_CODEC_CELLS 2 + +static struct platform_device *twl4030_codec_dev; + +struct twl4030_codec_resource { + int request_count; + u8 reg; + u8 mask; +}; + +struct twl4030_codec { + struct mutex mutex; + struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX]; + struct mfd_cell cells[TWL4030_CODEC_CELLS]; +}; + +/* + * Modify the resource, the function returns the content of the register + * after the modification. + */ +static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable) +{ + struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + u8 val; + + twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, + codec->resource[id].reg); + + if (enable) + val |= codec->resource[id].mask; + else + val &= ~codec->resource[id].mask; + + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + val, codec->resource[id].reg); + + return val; +} + +static inline int twl4030_codec_get_resource(enum twl4030_codec_res id) +{ + struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + u8 val; + + twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, + codec->resource[id].reg); + + return val; +} + +/* + * Enable the resource. + * The function returns with error or the content of the register + */ +int twl4030_codec_enable_resource(enum twl4030_codec_res id) +{ + struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + int val; + + if (id >= TWL4030_CODEC_RES_MAX) { + dev_err(&twl4030_codec_dev->dev, + "Invalid resource ID (%u)\n", id); + return -EINVAL; + } + + mutex_lock(&codec->mutex); + if (!codec->resource[id].request_count) + /* Resource was disabled, enable it */ + val = twl4030_codec_set_resource(id, 1); + else + val = twl4030_codec_get_resource(id); + + codec->resource[id].request_count++; + mutex_unlock(&codec->mutex); + + return val; +} +EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource); + +/* + * Disable the resource. + * The function returns with error or the content of the register + */ +int twl4030_codec_disable_resource(unsigned id) +{ + struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + int val; + + if (id >= TWL4030_CODEC_RES_MAX) { + dev_err(&twl4030_codec_dev->dev, + "Invalid resource ID (%u)\n", id); + return -EINVAL; + } + + mutex_lock(&codec->mutex); + if (!codec->resource[id].request_count) { + dev_err(&twl4030_codec_dev->dev, + "Resource has been disabled already (%u)\n", id); + mutex_unlock(&codec->mutex); + return -EPERM; + } + codec->resource[id].request_count--; + + if (!codec->resource[id].request_count) + /* Resource can be disabled now */ + val = twl4030_codec_set_resource(id, 0); + else + val = twl4030_codec_get_resource(id); + + mutex_unlock(&codec->mutex); + + return val; +} +EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource); + +static int __devinit twl4030_codec_probe(struct platform_device *pdev) +{ + struct twl4030_codec *codec; + struct twl4030_codec_data *pdata = pdev->dev.platform_data; + struct mfd_cell *cell = NULL; + int ret, childs = 0; + + codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + platform_set_drvdata(pdev, codec); + + twl4030_codec_dev = pdev; + mutex_init(&codec->mutex); + + /* Codec power */ + codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE; + codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ; + + /* PLL */ + codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL; + codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN; + + if (pdata->audio) { + cell = &codec->cells[childs]; + cell->name = "twl4030_codec_audio"; + cell->platform_data = pdata->audio; + cell->data_size = sizeof(*pdata->audio); + childs++; + } + if (pdata->vibra) { + cell = &codec->cells[childs]; + cell->name = "twl4030_codec_vibra"; + cell->platform_data = pdata->vibra; + cell->data_size = sizeof(*pdata->vibra); + childs++; + } + + if (childs) + ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells, + childs, NULL, 0); + else { + dev_err(&pdev->dev, "No platform data found for childs\n"); + ret = -ENODEV; + } + + if (!ret) + return 0; + + platform_set_drvdata(pdev, NULL); + kfree(codec); + twl4030_codec_dev = NULL; + return ret; +} + +static int __devexit twl4030_codec_remove(struct platform_device *pdev) +{ + struct twl4030_codec *codec = platform_get_drvdata(pdev); + + mfd_remove_devices(&pdev->dev); + platform_set_drvdata(pdev, NULL); + kfree(codec); + twl4030_codec_dev = NULL; + + return 0; +} + +MODULE_ALIAS("platform:twl4030_codec"); + +static struct platform_driver twl4030_codec_driver = { + .probe = twl4030_codec_probe, + .remove = __devexit_p(twl4030_codec_remove), + .driver = { + .owner = THIS_MODULE, + .name = "twl4030_codec", + }, +}; + +static int __devinit twl4030_codec_init(void) +{ + return platform_driver_register(&twl4030_codec_driver); +} +module_init(twl4030_codec_init); + +static void __devexit twl4030_codec_exit(void) +{ + platform_driver_unregister(&twl4030_codec_driver); +} +module_exit(twl4030_codec_exit); + +MODULE_AUTHOR("Peter Ujfalusi "); +MODULE_LICENSE("GPL"); + diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index e424cf6..0ee81e4 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c @@ -114,6 +114,12 @@ #define twl_has_watchdog() false #endif +#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) +#define twl_has_codec() true +#else +#define twl_has_codec() false +#endif + /* Triton Core internal information (BEGIN) */ /* Last - for index max*/ @@ -557,6 +563,14 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) return PTR_ERR(child); } + if (twl_has_codec() && pdata->codec) { + child = add_child(1, "twl4030_codec", + pdata->codec, sizeof(*pdata->codec), + false, 0, 0); + if (IS_ERR(child)) + return PTR_ERR(child); + } + if (twl_has_regulator()) { /* child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1); diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h index 2d02dfd..42d6c72 100644 --- a/include/linux/i2c/twl4030.h +++ b/include/linux/i2c/twl4030.h @@ -401,6 +401,23 @@ struct twl4030_power_data { extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts); +struct twl4030_codec_audio_data { + unsigned int audio_mclk; + unsigned int ramp_delay_value; + unsigned int hs_extmute:1; + void (*set_hs_extmute)(int mute); +}; + +struct twl4030_codec_vibra_data { + unsigned int audio_mclk; + unsigned int coexist; +}; + +struct twl4030_codec_data { + struct twl4030_codec_audio_data *audio; + struct twl4030_codec_vibra_data *vibra; +}; + struct twl4030_platform_data { unsigned irq_base, irq_end; struct twl4030_bci_platform_data *bci; @@ -409,6 +426,7 @@ struct twl4030_platform_data { struct twl4030_keypad_data *keypad; struct twl4030_usb_data *usb; struct twl4030_power_data *power; + struct twl4030_codec_data *codec; /* LDO regulators */ struct regulator_init_data *vdac; diff --git a/include/linux/mfd/twl4030-codec.h b/include/linux/mfd/twl4030-codec.h new file mode 100644 index 0000000..ef0a304 --- /dev/null +++ b/include/linux/mfd/twl4030-codec.h @@ -0,0 +1,271 @@ +/* + * MFD driver for twl4030 codec submodule + * + * 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 __TWL4030_CODEC_H__ +#define __TWL4030_CODEC_H__ + +/* Codec registers */ +#define TWL4030_REG_CODEC_MODE 0x01 +#define TWL4030_REG_OPTION 0x02 +#define TWL4030_REG_UNKNOWN 0x03 +#define TWL4030_REG_MICBIAS_CTL 0x04 +#define TWL4030_REG_ANAMICL 0x05 +#define TWL4030_REG_ANAMICR 0x06 +#define TWL4030_REG_AVADC_CTL 0x07 +#define TWL4030_REG_ADCMICSEL 0x08 +#define TWL4030_REG_DIGMIXING 0x09 +#define TWL4030_REG_ATXL1PGA 0x0A +#define TWL4030_REG_ATXR1PGA 0x0B +#define TWL4030_REG_AVTXL2PGA 0x0C +#define TWL4030_REG_AVTXR2PGA 0x0D +#define TWL4030_REG_AUDIO_IF 0x0E +#define TWL4030_REG_VOICE_IF 0x0F +#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 + +/* 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 + +/* VIBRA_CTL (0x45) */ +#define TWL4030_VIBRA_EN 0x01 +#define TWL4030_VIBRA_DIR 0x02 +#define TWL4030_VIBRA_AUDIO_SEL_L1 (0x00 << 2) +#define TWL4030_VIBRA_AUDIO_SEL_R1 (0x01 << 2) +#define TWL4030_VIBRA_AUDIO_SEL_L2 (0x02 << 2) +#define TWL4030_VIBRA_AUDIO_SEL_R2 (0x03 << 2) +#define TWL4030_VIBRA_SEL 0x10 +#define TWL4030_VIBRA_DIR_SEL 0x20 + +/* TWL4030 codec resource IDs */ +enum twl4030_codec_res { + TWL4030_CODEC_RES_POWER = 0, + TWL4030_CODEC_RES_APLL, + TWL4030_CODEC_RES_MAX, +}; + +int twl4030_codec_disable_resource(enum twl4030_codec_res id); +int twl4030_codec_enable_resource(enum twl4030_codec_res id); + +#endif /* End of __TWL4030_CODEC_H__ */ -- cgit v0.10.2 From f8d9aad96d0d7b57d0bf2e4de21fdda3a42f4449 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 22 Oct 2009 13:26:46 +0300 Subject: OMAP: Platform support for twl4030_codec MFD Add needed platform data for the twl4030_codec MFD on boards, where the audio part of the twl4030 codec is used. Signed-off-by: Peter Ujfalusi Acked-by: Tony Lindgren Signed-off-by: Mark Brown diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c index efaf053..4f91f7a0 100644 --- a/arch/arm/mach-omap2/board-3430sdp.c +++ b/arch/arm/mach-omap2/board-3430sdp.c @@ -410,6 +410,14 @@ static struct regulator_init_data sdp3430_vpll2 = { .consumer_supplies = &sdp3430_vdvi_supply, }; +static struct twl4030_codec_audio_data sdp3430_audio = { + .audio_mclk = 26000000, +}; + +static struct twl4030_codec_data sdp3430_codec = { + .audio = &sdp3430_audio, +}; + static struct twl4030_platform_data sdp3430_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, @@ -420,6 +428,7 @@ static struct twl4030_platform_data sdp3430_twldata = { .madc = &sdp3430_madc_data, .keypad = &sdp3430_kp_data, .usb = &sdp3430_usb_data, + .codec = &sdp3430_codec, .vaux1 = &sdp3430_vaux1, .vaux2 = &sdp3430_vaux2, diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index 70df6b4..2161d85 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -254,6 +254,14 @@ static struct twl4030_usb_data beagle_usb_data = { .usb_mode = T2_USB_MODE_ULPI, }; +static struct twl4030_codec_audio_data beagle_audio_data = { + .audio_mclk = 26000000, +}; + +static struct twl4030_codec_data beagle_codec_data = { + .audio = &beagle_audio_data, +}; + static struct twl4030_platform_data beagle_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, @@ -261,6 +269,7 @@ static struct twl4030_platform_data beagle_twldata = { /* platform_data for children goes here */ .usb = &beagle_usb_data, .gpio = &beagle_gpio_data, + .codec = &beagle_codec_data, .vmmc1 = &beagle_vmmc1, .vsim = &beagle_vsim, .vdac = &beagle_vdac, diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index e4ec0c5..d9a6103 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -194,6 +194,14 @@ static struct twl4030_madc_platform_data omap3evm_madc_data = { .irq_line = 1, }; +static struct twl4030_codec_audio_data omap3evm_audio_data = { + .audio_mclk = 26000000, +}; + +static struct twl4030_codec_data omap3evm_codec_data = { + .audio = &omap3evm_audio_data, +}; + static struct twl4030_platform_data omap3evm_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, @@ -203,6 +211,7 @@ static struct twl4030_platform_data omap3evm_twldata = { .madc = &omap3evm_madc_data, .usb = &omap3evm_usb_data, .gpio = &omap3evm_gpio_data, + .codec = &omap3evm_codec_data, }; static struct i2c_board_info __initdata omap3evm_i2c_boardinfo[] = { diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index 7f6bf87..5036b56 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -281,11 +281,20 @@ static struct twl4030_usb_data omap3pandora_usb_data = { .usb_mode = T2_USB_MODE_ULPI, }; +static struct twl4030_codec_audio_data omap3pandora_audio_data = { + .audio_mclk = 26000000, +}; + +static struct twl4030_codec_data omap3pandora_codec_data = { + .audio = &omap3pandora_audio_data, +}; + static struct twl4030_platform_data omap3pandora_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, .gpio = &omap3pandora_gpio_data, .usb = &omap3pandora_usb_data, + .codec = &omap3pandora_codec_data, .vmmc1 = &pandora_vmmc1, .vmmc2 = &pandora_vmmc2, .keypad = &pandora_kp_data, diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c index 9917d2f..dc55008 100644 --- a/arch/arm/mach-omap2/board-overo.c +++ b/arch/arm/mach-omap2/board-overo.c @@ -329,6 +329,14 @@ static struct regulator_init_data overo_vmmc1 = { .consumer_supplies = &overo_vmmc1_supply, }; +static struct twl4030_codec_audio_data overo_audio_data = { + .audio_mclk = 26000000, +}; + +static struct twl4030_codec_data overo_codec_data = { + .audio = &overo_audio_data, +}; + /* mmc2 (WLAN) and Bluetooth don't use twl4030 regulators */ static struct twl4030_platform_data overo_twldata = { @@ -336,6 +344,7 @@ static struct twl4030_platform_data overo_twldata = { .irq_end = TWL4030_IRQ_END, .gpio = &overo_gpio_data, .usb = &overo_usb_data, + .codec = &overo_codec_data, .vmmc1 = &overo_vmmc1, }; diff --git a/arch/arm/mach-omap2/board-zoom2.c b/arch/arm/mach-omap2/board-zoom2.c index b7b3220..f1b4e7c 100644 --- a/arch/arm/mach-omap2/board-zoom2.c +++ b/arch/arm/mach-omap2/board-zoom2.c @@ -229,6 +229,14 @@ static struct twl4030_madc_platform_data zoom2_madc_data = { .irq_line = 1, }; +static struct twl4030_codec_audio_data zoom2_audio_data = { + .audio_mclk = 26000000, +}; + +static struct twl4030_codec_data zoom2_codec_data = { + .audio = &zoom2_audio_data, +}; + static struct twl4030_platform_data zoom2_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, @@ -239,6 +247,7 @@ static struct twl4030_platform_data zoom2_twldata = { .usb = &zoom2_usb_data, .gpio = &zoom2_gpio_data, .keypad = &zoom2_kp_twl4030_data, + .codec = &zoom2_codec_data, .vmmc1 = &zoom2_vmmc1, .vmmc2 = &zoom2_vmmc2, .vsim = &zoom2_vsim, -- cgit v0.10.2 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 diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h index 2b4bfa2..dd6396e 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 v0.10.2 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 diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index d30fce7..3df3497 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 559e9b2..5c5a4c0 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 v0.10.2 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 diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 0e59219..bb95af9 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 v0.10.2 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 diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 5c5a4c0..2400226 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 v0.10.2 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 diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 2400226..9163713 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 v0.10.2 From 26d95b6e300c4847be6ec8bfe817dbd531e94d9a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 28 Oct 2009 15:47:48 +0000 Subject: ASoC: Minor SMDK64xx WM8580 cleanups Fix up some comments, remove all enable_pin() calls (edge widgets are all enabled by default) and mark the microphone as disabled by default since it requires a resistor fit to connect it. Signed-off-by: Mark Brown diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/s3c24xx/smdk64xx_wm8580.c index 482aaf1..cb8a916 100644 --- a/sound/soc/s3c24xx/smdk64xx_wm8580.c +++ b/sound/soc/s3c24xx/smdk64xx_wm8580.c @@ -103,7 +103,7 @@ static int smdk64xx_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - /* Set WM8580 to drive MCLK from it's PLLA */ + /* Set WM8580 to drive MCLK from its PLLA */ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK, WM8580_CLKSRC_PLLA); if (ret < 0) @@ -115,7 +115,6 @@ static int smdk64xx_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - /* Assuming the CODEC driver evaluates it's rfs too from this call */ ret = snd_soc_dai_set_pll(codec_dai, 0, WM8580_PLLA, SMDK64XX_WM8580_FREQ, pll_out); if (ret < 0) @@ -186,9 +185,10 @@ static int smdk64xx_wm8580_init_paiftx(struct snd_soc_codec *codec) /* Set up PAIFTX audio path */ snd_soc_dapm_add_routes(codec, audio_map_tx, ARRAY_SIZE(audio_map_tx)); - /* All enabled by default */ - snd_soc_dapm_enable_pin(codec, "MicIn"); - snd_soc_dapm_enable_pin(codec, "LineIn"); + /* Enabling the microphone requires the fitting of a 0R + * resistor to connect the line from the microphone jack. + */ + snd_soc_dapm_disable_pin(codec, "MicIn"); /* signal a DAPM event */ snd_soc_dapm_sync(codec); @@ -205,11 +205,6 @@ static int smdk64xx_wm8580_init_paifrx(struct snd_soc_codec *codec) /* Set up PAIFRX audio path */ snd_soc_dapm_add_routes(codec, audio_map_rx, ARRAY_SIZE(audio_map_rx)); - /* All enabled by default */ - snd_soc_dapm_enable_pin(codec, "Front-L/R"); - snd_soc_dapm_enable_pin(codec, "Center/Sub"); - snd_soc_dapm_enable_pin(codec, "Rear-L/R"); - /* signal a DAPM event */ snd_soc_dapm_sync(codec); -- cgit v0.10.2 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 diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 59bb16d..565842d 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 v0.10.2 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 diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 9163713..ccaeb36 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 v0.10.2 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 diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index ccaeb36..277e99c 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 v0.10.2 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 diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 277e99c..f9121ef 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 v0.10.2 From ed146aeb68b6b240a015f3c24c9eea9266d845ec Mon Sep 17 00:00:00 2001 From: Anuj Aggarwal Date: Wed, 23 Sep 2009 12:40:31 +0530 Subject: ASoC: OMAP3EVM: Use the twl4030_setup_data for headset pop-removal The pop-removal specific values are configured for TWL4030 codec for OMAP3EVM through this patch. Signed-off-by: Anuj Aggarwal Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/omap/omap3evm.c b/sound/soc/omap/omap3evm.c index 9114c26..8deb59b 100644 --- a/sound/soc/omap/omap3evm.c +++ b/sound/soc/omap/omap3evm.c @@ -93,10 +93,17 @@ static struct snd_soc_card snd_soc_omap3evm = { .num_links = 1, }; +/* twl4030 setup */ +static struct twl4030_setup_data twl4030_setup = { + .ramp_delay_value = 4, + .sysclk = 26000, +}; + /* Audio subsystem */ static struct snd_soc_device omap3evm_snd_devdata = { .card = &snd_soc_omap3evm, .codec_dev = &soc_codec_dev_twl4030, + .codec_data = &twl4030_setup, }; static struct platform_device *omap3evm_snd_device; -- cgit v0.10.2 From 89e9abe78151de4d62fefe3976f6ef9f1f086e53 Mon Sep 17 00:00:00 2001 From: Anuj Aggarwal Date: Fri, 30 Oct 2009 00:22:30 +0530 Subject: ASoC: Adding OMAP3517 / AM3517 EVM support in ASOC Adding support for OMAP3517 / AM3517 EVM in Alsa SoC. Signed-off-by: Anuj Aggarwal Signed-off-by: Mark Brown diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c new file mode 100644 index 0000000..135901b --- /dev/null +++ b/sound/soc/omap/am3517evm.c @@ -0,0 +1,202 @@ +/* + * am3517evm.c -- ALSA SoC support for OMAP3517 / AM3517 EVM + * + * Author: Anuj Aggarwal + * + * Based on sound/soc/omap/beagle.c by Steve Sakoman + * + * Copyright (C) 2009 Texas Instruments Incorporated + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "omap-mcbsp.h" +#include "omap-pcm.h" + +#include "../codecs/tlv320aic23.h" + +#define CODEC_CLOCK 12000000 + +static int am3517evm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int ret; + + /* Set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + printk(KERN_ERR "can't set codec DAI configuration\n"); + return ret; + } + + /* Set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + printk(KERN_ERR "can't set cpu DAI configuration\n"); + return ret; + } + + /* Set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, 0, + CODEC_CLOCK, SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set codec system clock\n"); + return ret; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_CLKR_SRC_CLKX, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_CLKR_SRC_CLKX\n"); + return ret; + } + + snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_FSR_SRC_FSX, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_FSR_SRC_FSX\n"); + return ret; + } + + return 0; +} + +static struct snd_soc_ops am3517evm_ops = { + .hw_params = am3517evm_hw_params, +}; + +/* am3517evm machine dapm widgets */ +static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { + SND_SOC_DAPM_HP("Line Out", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), + SND_SOC_DAPM_MIC("Mic In", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Line Out connected to LLOUT, RLOUT */ + {"Line Out", NULL, "LOUT"}, + {"Line Out", NULL, "ROUT"}, + + {"LLINEIN", NULL, "Line In"}, + {"RLINEIN", NULL, "Line In"}, + + {"MICIN", NULL, "Mic In"}, +}; + +static int am3517evm_aic23_init(struct snd_soc_codec *codec) +{ + /* Add am3517-evm specific widgets */ + snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, + ARRAY_SIZE(tlv320aic23_dapm_widgets)); + + /* Set up davinci-evm specific audio path audio_map */ + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + /* always connected */ + snd_soc_dapm_enable_pin(codec, "Line Out"); + snd_soc_dapm_enable_pin(codec, "Line In"); + snd_soc_dapm_enable_pin(codec, "Mic In"); + + snd_soc_dapm_sync(codec); + + return 0; +} + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link am3517evm_dai = { + .name = "TLV320AIC23", + .stream_name = "AIC23", + .cpu_dai = &omap_mcbsp_dai[0], + .codec_dai = &tlv320aic23_dai, + .init = am3517evm_aic23_init, + .ops = &am3517evm_ops, +}; + +/* Audio machine driver */ +static struct snd_soc_card snd_soc_am3517evm = { + .name = "am3517evm", + .platform = &omap_soc_platform, + .dai_link = &am3517evm_dai, + .num_links = 1, +}; + +/* Audio subsystem */ +static struct snd_soc_device am3517evm_snd_devdata = { + .card = &snd_soc_am3517evm, + .codec_dev = &soc_codec_dev_tlv320aic23, +}; + +static struct platform_device *am3517evm_snd_device; + +static int __init am3517evm_soc_init(void) +{ + int ret; + + if (!machine_is_omap3517evm()) { + pr_err("Not OMAP3517 / AM3517 EVM!\n"); + return -ENODEV; + } + pr_info("OMAP3517 / AM3517 EVM SoC init\n"); + + am3517evm_snd_device = platform_device_alloc("soc-audio", -1); + if (!am3517evm_snd_device) { + printk(KERN_ERR "Platform device allocation failed\n"); + return -ENOMEM; + } + + platform_set_drvdata(am3517evm_snd_device, &am3517evm_snd_devdata); + am3517evm_snd_devdata.dev = &am3517evm_snd_device->dev; + *(unsigned int *)am3517evm_dai.cpu_dai->private_data = 0; /* McBSP1 */ + + ret = platform_device_add(am3517evm_snd_device); + if (ret) + goto err1; + + return 0; + +err1: + printk(KERN_ERR "Unable to add platform device\n"); + platform_device_put(am3517evm_snd_device); + + return ret; +} + +static void __exit am3517evm_soc_exit(void) +{ + platform_device_unregister(am3517evm_snd_device); +} + +module_init(am3517evm_soc_init); +module_exit(am3517evm_soc_exit); + +MODULE_AUTHOR("Anuj Aggarwal "); +MODULE_DESCRIPTION("ALSA SoC OMAP3517 / AM3517 EVM"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 67e646cd7b51e1d5847fb506d4419d436ea25fda Mon Sep 17 00:00:00 2001 From: Anuj Aggarwal Date: Fri, 30 Oct 2009 00:22:39 +0530 Subject: ASoC: Modifying Kconfig/Makefile for AM3517 EVM Modifying the Kconfig and Makefile in sound/soc/omap folder to add support for OMAP3517 / AM3517 EVM in Alsa SoC. Signed-off-by: Anuj Aggarwal Signed-off-by: Mark Brown diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 2dee983..6344456 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -55,6 +55,15 @@ config SND_OMAP_SOC_OMAP3EVM help Say Y if you want to add support for SoC audio on the omap3evm board. +config SND_OMAP_SOC_AM3517EVM + tristate "SoC Audio support for OMAP3517 / AM3517 EVM" + depends on SND_OMAP_SOC && MACH_OMAP3517EVM && I2C + select SND_OMAP_SOC_MCBSP + select SND_SOC_TLV320AIC23 + help + Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517 + EVM. + config SND_OMAP_SOC_SDP3430 tristate "SoC Audio support for Texas Instruments SDP3430" depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 02d6947..0c78ae4 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -12,6 +12,7 @@ snd-soc-osk5912-objs := osk5912.o snd-soc-overo-objs := overo.o snd-soc-omap2evm-objs := omap2evm.o snd-soc-omap3evm-objs := omap3evm.o +snd-soc-am3517evm-objs := am3517evm.o snd-soc-sdp3430-objs := sdp3430.o snd-soc-omap3pandora-objs := omap3pandora.o snd-soc-omap3beagle-objs := omap3beagle.o @@ -23,6 +24,7 @@ obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o +obj-$(CONFIG_MACH_OMAP3517EVM) += snd-soc-am3517evm.o obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o -- cgit v0.10.2 From 9ddc9aa910687a8787dbbdc53dcd48e738b197d9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 30 Oct 2009 12:02:39 +0900 Subject: ASoC: sh: FSI: Remove DMA support SuperH FSI device have the hardware limitation to use DMA. If DMA is used, LCD output will be broken. Maybe there are some solution. But I don't know how to do it now. This patch remove DMA support and use soft transfer. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 9154b43..9e69765 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -23,7 +23,6 @@ config SND_SOC_SH4_SSI config SND_SOC_SH4_FSI tristate "SH4 FSI support" depends on CPU_SUBTYPE_SH7724 - select SH_DMA help This option enables FSI sound support diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 4412324..9742a28 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -26,8 +26,6 @@ #include #include #include -#include -#include #define DO_FMT 0x0000 #define DOFF_CTL 0x0004 @@ -97,7 +95,6 @@ struct fsi_priv { int fifo_max; int chan; - int dma_chan; int byte_offset; int period_len; @@ -308,62 +305,6 @@ static int fsi_get_fifo_residue(struct fsi_priv *fsi, int is_play) return residue; } -static int fsi_get_residue(struct fsi_priv *fsi, int is_play) -{ - int residue; - int width; - struct snd_pcm_runtime *runtime; - - runtime = fsi->substream->runtime; - - /* get 1 channel data width */ - width = frames_to_bytes(runtime, 1) / fsi->chan; - - if (2 == width) - residue = fsi_get_fifo_residue(fsi, is_play); - else - residue = get_dma_residue(fsi->dma_chan); - - return residue; -} - -/************************************************************************ - - - basic dma function - - -************************************************************************/ -#define PORTA_DMA 0 -#define PORTB_DMA 1 - -static int fsi_get_dma_chan(void) -{ - if (0 != request_dma(PORTA_DMA, "fsia")) - return -EIO; - - if (0 != request_dma(PORTB_DMA, "fsib")) { - free_dma(PORTA_DMA); - return -EIO; - } - - master->fsia.dma_chan = PORTA_DMA; - master->fsib.dma_chan = PORTB_DMA; - - return 0; -} - -static void fsi_free_dma_chan(void) -{ - dma_wait_for_completion(PORTA_DMA); - dma_wait_for_completion(PORTB_DMA); - free_dma(PORTA_DMA); - free_dma(PORTB_DMA); - - master->fsia.dma_chan = -1; - master->fsib.dma_chan = -1; -} - /************************************************************************ @@ -435,44 +376,6 @@ static void fsi_soft_all_reset(void) mdelay(10); } -static void fsi_16data_push(struct fsi_priv *fsi, - struct snd_pcm_runtime *runtime, - int send) -{ - u16 *dma_start; - u32 snd; - int i; - - /* get dma start position for FSI */ - dma_start = (u16 *)runtime->dma_area; - dma_start += fsi->byte_offset / 2; - - /* - * soft dma - * FSI can not use DMA when 16bpp - */ - for (i = 0; i < send; i++) { - snd = (u32)dma_start[i]; - fsi_reg_write(fsi, DODT, snd << 8); - } -} - -static void fsi_32data_push(struct fsi_priv *fsi, - struct snd_pcm_runtime *runtime, - int send) -{ - u32 *dma_start; - - /* get dma start position for FSI */ - dma_start = (u32 *)runtime->dma_area; - dma_start += fsi->byte_offset / 4; - - dma_wait_for_completion(fsi->dma_chan); - dma_configure_channel(fsi->dma_chan, (SM_INC|0x400|TS_32|TM_BUR)); - dma_write(fsi->dma_chan, (u32)dma_start, - (u32)(fsi->base + DODT), send * 4); -} - /* playback interrupt */ static int fsi_data_push(struct fsi_priv *fsi) { @@ -481,6 +384,8 @@ static int fsi_data_push(struct fsi_priv *fsi) int send; int fifo_free; int width; + u8 *start; + int i; if (!fsi || !fsi->substream || @@ -515,12 +420,22 @@ static int fsi_data_push(struct fsi_priv *fsi) if (fifo_free < send) send = fifo_free; - if (2 == width) - fsi_16data_push(fsi, runtime, send); - else if (4 == width) - fsi_32data_push(fsi, runtime, send); - else + start = runtime->dma_area; + start += fsi->byte_offset; + + switch (width) { + case 2: + for (i = 0; i < send; i++) + fsi_reg_write(fsi, DODT, + ((u32)*((u16 *)start + i) << 8)); + break; + case 4: + for (i = 0; i < send; i++) + fsi_reg_write(fsi, DODT, *((u32 *)start + i)); + break; + default: return -EINVAL; + } fsi->byte_offset += send * width; @@ -664,8 +579,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, } fsi_reg_write(fsi, reg, data); - dev_dbg(dai->dev, "use %s format (%d channel) use %d DMAC\n", - msg, fsi->chan, fsi->dma_chan); /* * clear clk reset if master mode @@ -780,10 +693,9 @@ static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct fsi_priv *fsi = fsi_get(substream); - int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; long location; - location = (fsi->byte_offset - 1) - fsi_get_residue(fsi, is_play); + location = (fsi->byte_offset - 1); if (location < 0) location = 0; @@ -912,22 +824,13 @@ static int fsi_probe(struct platform_device *pdev) master->fsia.base = master->base; master->fsib.base = master->base + 0x40; - master->fsia.dma_chan = -1; - master->fsib.dma_chan = -1; - - ret = fsi_get_dma_chan(); - if (ret < 0) { - dev_err(&pdev->dev, "cannot get dma api\n"); - goto exit_iounmap; - } - /* FSI is based on SPU mstp */ snprintf(clk_name, sizeof(clk_name), "spu%d", pdev->id); master->clk = clk_get(NULL, clk_name); if (IS_ERR(master->clk)) { dev_err(&pdev->dev, "cannot get %s mstp\n", clk_name); ret = -EIO; - goto exit_free_dma; + goto exit_iounmap; } fsi_soc_dai[0].dev = &pdev->dev; @@ -938,7 +841,7 @@ static int fsi_probe(struct platform_device *pdev) ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master); if (ret) { dev_err(&pdev->dev, "irq request err\n"); - goto exit_free_dma; + goto exit_iounmap; } ret = snd_soc_register_platform(&fsi_soc_platform); @@ -951,8 +854,6 @@ static int fsi_probe(struct platform_device *pdev) exit_free_irq: free_irq(irq, master); -exit_free_dma: - fsi_free_dma_chan(); exit_iounmap: iounmap(master->base); exit_kfree: @@ -969,8 +870,6 @@ static int fsi_remove(struct platform_device *pdev) clk_put(master->clk); - fsi_free_dma_chan(); - free_irq(master->irq, master); iounmap(master->base); -- cgit v0.10.2 From 07102f3cefc93aa742af91186830e282c0347e41 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 30 Oct 2009 12:02:44 +0900 Subject: ASoC: sh: FSI: Add capture support Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 9742a28..e1a3d1a 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -447,6 +447,75 @@ static int fsi_data_push(struct fsi_priv *fsi) return 0; } +static int fsi_data_pop(struct fsi_priv *fsi) +{ + struct snd_pcm_runtime *runtime; + struct snd_pcm_substream *substream = NULL; + int free; + int fifo_fill; + int width; + u8 *start; + int i; + + if (!fsi || + !fsi->substream || + !fsi->substream->runtime) + return -EINVAL; + + runtime = fsi->substream->runtime; + + /* FSI FIFO has limit. + * So, this driver can not send periods data at a time + */ + if (fsi->byte_offset >= + fsi->period_len * (fsi->periods + 1)) { + + substream = fsi->substream; + fsi->periods = (fsi->periods + 1) % runtime->periods; + + if (0 == fsi->periods) + fsi->byte_offset = 0; + } + + /* get 1 channel data width */ + width = frames_to_bytes(runtime, 1) / fsi->chan; + + /* get free space for alsa */ + free = (fsi->buffer_len - fsi->byte_offset) / width; + + /* get recv size */ + fifo_fill = fsi_get_fifo_residue(fsi, 0); + + if (free < fifo_fill) + fifo_fill = free; + + start = runtime->dma_area; + start += fsi->byte_offset; + + switch (width) { + case 2: + for (i = 0; i < fifo_fill; i++) + *((u16 *)start + i) = + (u16)(fsi_reg_read(fsi, DIDT) >> 8); + break; + case 4: + for (i = 0; i < fifo_fill; i++) + *((u32 *)start + i) = fsi_reg_read(fsi, DIDT); + break; + default: + return -EINVAL; + } + + fsi->byte_offset += fifo_fill * width; + + fsi_irq_enable(fsi, 0); + + if (substream) + snd_pcm_period_elapsed(substream); + + return 0; +} + static irqreturn_t fsi_interrupt(int irq, void *data) { u32 status = fsi_master_read(SOFT_RST) & ~0x00000010; @@ -460,6 +529,10 @@ static irqreturn_t fsi_interrupt(int irq, void *data) fsi_data_push(&master->fsia); if (int_st & INT_B_OUT) fsi_data_push(&master->fsib); + if (int_st & INT_A_IN) + fsi_data_pop(&master->fsia); + if (int_st & INT_B_IN) + fsi_data_pop(&master->fsib); fsi_master_write(INT_ST, 0x0000000); @@ -612,16 +685,12 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int ret = 0; - /* capture not supported */ - if (!is_play) - return -ENODEV; - switch (cmd) { case SNDRV_PCM_TRIGGER_START: fsi_stream_push(fsi, substream, frames_to_bytes(runtime, runtime->buffer_size), frames_to_bytes(runtime, runtime->period_size)); - ret = fsi_data_push(fsi); + ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi); break; case SNDRV_PCM_TRIGGER_STOP: fsi_irq_disable(fsi, is_play); @@ -757,7 +826,12 @@ struct snd_soc_dai fsi_soc_dai[] = { .channels_min = 1, .channels_max = 8, }, - /* capture not supported */ + .capture = { + .rates = FSI_RATES, + .formats = FSI_FMTS, + .channels_min = 1, + .channels_max = 8, + }, .ops = &fsi_dai_ops, }, { @@ -769,7 +843,12 @@ struct snd_soc_dai fsi_soc_dai[] = { .channels_min = 1, .channels_max = 8, }, - /* capture not supported */ + .capture = { + .rates = FSI_RATES, + .formats = FSI_FMTS, + .channels_min = 1, + .channels_max = 8, + }, .ops = &fsi_dai_ops, }, }; -- cgit v0.10.2 From 8538a119bfb9031c402a33fc65c276ab9bfafdd5 Mon Sep 17 00:00:00 2001 From: Eero Nurkkala Date: Fri, 30 Oct 2009 13:34:02 +0200 Subject: ASoC: remove io_mutex Remove the io_mutex. It has a drawback of serializing all accesses to snd_soc_update_bits() even when multiple codecs are in use. In addition, it fails to actually do its task - during snd_soc_update_bits(), dapm_update_bits() may also be accessing the same register which may result in an outdated register value. Signed-off-by: Eero Nurkkala Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2d190df..025c5a7 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -37,7 +37,6 @@ #include static DEFINE_MUTEX(pcm_mutex); -static DEFINE_MUTEX(io_mutex); static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); #ifdef CONFIG_DEBUG_FS @@ -1346,14 +1345,12 @@ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, int change; unsigned int old, new; - mutex_lock(&io_mutex); old = snd_soc_read(codec, reg); new = (old & ~mask) | value; change = old != new; if (change) snd_soc_write(codec, reg, new); - mutex_unlock(&io_mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_update_bits); @@ -1376,11 +1373,9 @@ int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, int change; unsigned int old, new; - mutex_lock(&io_mutex); old = snd_soc_read(codec, reg); new = (old & ~mask) | value; change = old != new; - mutex_unlock(&io_mutex); return change; } -- cgit v0.10.2 From 6c508c62f90240ef58300a5e12093ee769a44364 Mon Sep 17 00:00:00 2001 From: Eero Nurkkala Date: Fri, 30 Oct 2009 13:34:03 +0200 Subject: ASoC: refactor snd_soc_update_bits() Introduce a wrapper call snd_soc_update_bits_locked() that will take the codec mutex. This call is used when the codec mutex is not already taken. Drivers calling snd_soc_update_bits() may wish to make sure the codec mutex is taken from the driver. Signed-off-by: Eero Nurkkala Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 025c5a7..6e24654 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1356,6 +1356,30 @@ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, EXPORT_SYMBOL_GPL(snd_soc_update_bits); /** + * snd_soc_update_bits_locked - update codec register bits + * @codec: audio codec + * @reg: codec register + * @mask: register mask + * @value: new value + * + * Writes new register value, and takes the codec mutex. + * + * Returns 1 for change else 0. + */ +static int snd_soc_update_bits_locked(struct snd_soc_codec *codec, + unsigned short reg, unsigned int mask, + unsigned int value) +{ + int change; + + mutex_lock(&codec->mutex); + change = snd_soc_update_bits(codec, reg, mask, value); + mutex_unlock(&codec->mutex); + + return change; +} + +/** * snd_soc_test_bits - test register for change * @codec: audio codec * @reg: codec register @@ -1706,7 +1730,7 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, mask |= (bitmask - 1) << e->shift_r; } - return snd_soc_update_bits(codec, e->reg, mask, val); + return snd_soc_update_bits_locked(codec, e->reg, mask, val); } EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); @@ -1780,7 +1804,7 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, mask |= e->mask << e->shift_r; } - return snd_soc_update_bits(codec, e->reg, mask, val); + return snd_soc_update_bits_locked(codec, e->reg, mask, val); } EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double); @@ -1941,7 +1965,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, val_mask |= mask << rshift; val |= val2 << rshift; } - return snd_soc_update_bits(codec, reg, val_mask, val); + return snd_soc_update_bits_locked(codec, reg, val_mask, val); } EXPORT_SYMBOL_GPL(snd_soc_put_volsw); @@ -2047,11 +2071,11 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, val = val << shift; val2 = val2 << shift; - err = snd_soc_update_bits(codec, reg, val_mask, val); + err = snd_soc_update_bits_locked(codec, reg, val_mask, val); if (err < 0) return err; - err = snd_soc_update_bits(codec, reg2, val_mask, val2); + err = snd_soc_update_bits_locked(codec, reg2, val_mask, val2); return err; } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r); @@ -2130,7 +2154,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, val = (ucontrol->value.integer.value[0]+min) & 0xff; val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8; - return snd_soc_update_bits(codec, reg, 0xffff, val); + return snd_soc_update_bits_locked(codec, reg, 0xffff, val); } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); -- cgit v0.10.2 From 0f83d639d84c99a775c60696dbde77372c2cf4ac Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Sat, 31 Oct 2009 20:15:08 +0100 Subject: ASoC: au1x: convert to platform drivers. Convert psc-ac97,i2s to platform drivers similar to the davinci ones. Signed-off-by: Manuel Lauss Signed-off-by: Mark Brown diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index 594c6c5..fe9f465 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -2,7 +2,7 @@ * Au12x0/Au1550 PSC ALSA ASoC audio support. * * (c) 2007-2008 MSC Vertriebsges.m.b.H., - * Manuel Lauss + * Manuel Lauss * * 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 @@ -333,6 +333,30 @@ static int au1xpsc_pcm_new(struct snd_card *card, static int au1xpsc_pcm_probe(struct platform_device *pdev) { + if (!au1xpsc_audio_pcmdma[PCM_TX] || !au1xpsc_audio_pcmdma[PCM_RX]) + return -ENODEV; + + return 0; +} + +static int au1xpsc_pcm_remove(struct platform_device *pdev) +{ + return 0; +} + +/* au1xpsc audio platform */ +struct snd_soc_platform au1xpsc_soc_platform = { + .name = "au1xpsc-pcm-dbdma", + .probe = au1xpsc_pcm_probe, + .remove = au1xpsc_pcm_remove, + .pcm_ops = &au1xpsc_pcm_ops, + .pcm_new = au1xpsc_pcm_new, + .pcm_free = au1xpsc_pcm_free_dma_buffers, +}; +EXPORT_SYMBOL_GPL(au1xpsc_soc_platform); + +static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev) +{ struct resource *r; int ret; @@ -365,7 +389,9 @@ static int au1xpsc_pcm_probe(struct platform_device *pdev) } (au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start; - return 0; + ret = snd_soc_register_platform(&au1xpsc_soc_platform); + if (!ret) + return ret; out2: kfree(au1xpsc_audio_pcmdma[PCM_RX]); @@ -376,10 +402,12 @@ out1: return ret; } -static int au1xpsc_pcm_remove(struct platform_device *pdev) +static int __devexit au1xpsc_pcm_drvremove(struct platform_device *pdev) { int i; + snd_soc_unregister_platform(&au1xpsc_soc_platform); + for (i = 0; i < 2; i++) { if (au1xpsc_audio_pcmdma[i]) { au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]); @@ -391,32 +419,83 @@ static int au1xpsc_pcm_remove(struct platform_device *pdev) return 0; } -/* au1xpsc audio platform */ -struct snd_soc_platform au1xpsc_soc_platform = { - .name = "au1xpsc-pcm-dbdma", - .probe = au1xpsc_pcm_probe, - .remove = au1xpsc_pcm_remove, - .pcm_ops = &au1xpsc_pcm_ops, - .pcm_new = au1xpsc_pcm_new, - .pcm_free = au1xpsc_pcm_free_dma_buffers, +static struct platform_driver au1xpsc_pcm_driver = { + .driver = { + .name = "au1xpsc-pcm", + .owner = THIS_MODULE, + }, + .probe = au1xpsc_pcm_drvprobe, + .remove = __devexit_p(au1xpsc_pcm_drvremove), }; -EXPORT_SYMBOL_GPL(au1xpsc_soc_platform); -static int __init au1xpsc_audio_dbdma_init(void) +static int __init au1xpsc_audio_dbdma_load(void) { au1xpsc_audio_pcmdma[PCM_TX] = NULL; au1xpsc_audio_pcmdma[PCM_RX] = NULL; - return snd_soc_register_platform(&au1xpsc_soc_platform); + return platform_driver_register(&au1xpsc_pcm_driver); } -static void __exit au1xpsc_audio_dbdma_exit(void) +static void __exit au1xpsc_audio_dbdma_unload(void) { - snd_soc_unregister_platform(&au1xpsc_soc_platform); + platform_driver_unregister(&au1xpsc_pcm_driver); +} + +module_init(au1xpsc_audio_dbdma_load); +module_exit(au1xpsc_audio_dbdma_unload); + + +struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev) +{ + struct resource *res, *r; + struct platform_device *pd; + int id[2]; + int ret; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!r) + return NULL; + id[0] = r->start; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!r) + return NULL; + id[1] = r->start; + + res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL); + if (!res) + return NULL; + + res[0].start = res[0].end = id[0]; + res[1].start = res[1].end = id[1]; + res[0].flags = res[1].flags = IORESOURCE_DMA; + + pd = platform_device_alloc("au1xpsc-pcm", -1); + if (!pd) + goto out; + + pd->resource = res; + pd->num_resources = 2; + + ret = platform_device_add(pd); + if (!ret) + return pd; + +out: + kfree(res); + return NULL; } +EXPORT_SYMBOL_GPL(au1xpsc_pcm_add); -module_init(au1xpsc_audio_dbdma_init); -module_exit(au1xpsc_audio_dbdma_exit); +void au1xpsc_pcm_destroy(struct platform_device *dmapd) +{ + if (dmapd) { + kfree(dmapd->resource); + dmapd->resource = NULL; + platform_device_unregister(dmapd); + } +} +EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver"); -MODULE_AUTHOR("Manuel Lauss "); +MODULE_AUTHOR("Manuel Lauss"); diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 2a06a9c..340311d 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -317,19 +317,55 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream, static int au1xpsc_ac97_probe(struct platform_device *pdev, struct snd_soc_dai *dai) { + return au1xpsc_ac97_workdata ? 0 : -ENODEV; +} + +static void au1xpsc_ac97_remove(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ +} + +static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { + .trigger = au1xpsc_ac97_trigger, + .hw_params = au1xpsc_ac97_hw_params, +}; + +struct snd_soc_dai au1xpsc_ac97_dai = { + .name = "au1xpsc_ac97", + .ac97_control = 1, + .probe = au1xpsc_ac97_probe, + .remove = au1xpsc_ac97_remove, + .playback = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &au1xpsc_ac97_dai_ops, +}; +EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai); + +static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev) +{ int ret; struct resource *r; unsigned long sel; + struct au1xpsc_audio_data *wd; if (au1xpsc_ac97_workdata) return -EBUSY; - au1xpsc_ac97_workdata = - kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); - if (!au1xpsc_ac97_workdata) + wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); + if (!wd) return -ENOMEM; - mutex_init(&au1xpsc_ac97_workdata->lock); + mutex_init(&wd->lock); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { @@ -338,81 +374,95 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev, } ret = -EBUSY; - au1xpsc_ac97_workdata->ioarea = - request_mem_region(r->start, r->end - r->start + 1, + wd->ioarea = request_mem_region(r->start, r->end - r->start + 1, "au1xpsc_ac97"); - if (!au1xpsc_ac97_workdata->ioarea) + if (!wd->ioarea) goto out0; - au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff); - if (!au1xpsc_ac97_workdata->mmio) + wd->mmio = ioremap(r->start, 0xffff); + if (!wd->mmio) goto out1; /* configuration: max dma trigger threshold, enable ac97 */ - au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 | - PSC_AC97CFG_TT_FIFO8 | - PSC_AC97CFG_DE_ENABLE; + wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 | + PSC_AC97CFG_DE_ENABLE; - /* preserve PSC clock source set up by platform (dev.platform_data - * is already occupied by soc layer) - */ - sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK; - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); + /* preserve PSC clock source set up by platform */ + sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK; + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); - au_writel(0, PSC_SEL(au1xpsc_ac97_workdata)); + au_writel(0, PSC_SEL(wd)); au_sync(); - au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata)); + au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd)); au_sync(); - /* next up: cold reset. Dont check for PSC-ready now since - * there may not be any codec clock yet. - */ - return 0; + ret = snd_soc_register_dai(&au1xpsc_ac97_dai); + if (ret) + goto out1; + + wd->dmapd = au1xpsc_pcm_add(pdev); + if (wd->dmapd) { + platform_set_drvdata(pdev, wd); + au1xpsc_ac97_workdata = wd; /* MDEV */ + return 0; + } + snd_soc_unregister_dai(&au1xpsc_ac97_dai); out1: - release_resource(au1xpsc_ac97_workdata->ioarea); - kfree(au1xpsc_ac97_workdata->ioarea); + release_resource(wd->ioarea); + kfree(wd->ioarea); out0: - kfree(au1xpsc_ac97_workdata); - au1xpsc_ac97_workdata = NULL; + kfree(wd); return ret; } -static void au1xpsc_ac97_remove(struct platform_device *pdev, - struct snd_soc_dai *dai) +static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev) { + struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); + + if (wd->dmapd) + au1xpsc_pcm_destroy(wd->dmapd); + + snd_soc_unregister_dai(&au1xpsc_ac97_dai); + /* disable PSC completely */ - au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); + au_writel(0, AC97_CFG(wd)); au_sync(); - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); - iounmap(au1xpsc_ac97_workdata->mmio); - release_resource(au1xpsc_ac97_workdata->ioarea); - kfree(au1xpsc_ac97_workdata->ioarea); - kfree(au1xpsc_ac97_workdata); - au1xpsc_ac97_workdata = NULL; + iounmap(wd->mmio); + release_resource(wd->ioarea); + kfree(wd->ioarea); + kfree(wd); + + au1xpsc_ac97_workdata = NULL; /* MDEV */ + + return 0; } -static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai) +#ifdef CONFIG_PM +static int au1xpsc_ac97_drvsuspend(struct device *dev) { + struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); + /* save interesting registers and disable PSC */ - au1xpsc_ac97_workdata->pm[0] = - au_readl(PSC_SEL(au1xpsc_ac97_workdata)); + wd->pm[0] = au_readl(PSC_SEL(wd)); - au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); + au_writel(0, AC97_CFG(wd)); au_sync(); - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); return 0; } -static int au1xpsc_ac97_resume(struct snd_soc_dai *dai) +static int au1xpsc_ac97_drvresume(struct device *dev) { + struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); + /* restore PSC clock config */ - au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE, - PSC_SEL(au1xpsc_ac97_workdata)); + au_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd)); au_sync(); /* after this point the ac97 core will cold-reset the codec. @@ -422,48 +472,44 @@ static int au1xpsc_ac97_resume(struct snd_soc_dai *dai) return 0; } -static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { - .trigger = au1xpsc_ac97_trigger, - .hw_params = au1xpsc_ac97_hw_params, +static struct dev_pm_ops au1xpscac97_pmops = { + .suspend = au1xpsc_ac97_drvsuspend, + .resume = au1xpsc_ac97_drvresume, }; -struct snd_soc_dai au1xpsc_ac97_dai = { - .name = "au1xpsc_ac97", - .ac97_control = 1, - .probe = au1xpsc_ac97_probe, - .remove = au1xpsc_ac97_remove, - .suspend = au1xpsc_ac97_suspend, - .resume = au1xpsc_ac97_resume, - .playback = { - .rates = AC97_RATES, - .formats = AC97_FMTS, - .channels_min = 2, - .channels_max = 2, - }, - .capture = { - .rates = AC97_RATES, - .formats = AC97_FMTS, - .channels_min = 2, - .channels_max = 2, +#define AU1XPSCAC97_PMOPS &au1xpscac97_pmops + +#else + +#define AU1XPSCAC97_PMOPS NULL + +#endif + +static struct platform_driver au1xpsc_ac97_driver = { + .driver = { + .name = "au1xpsc_ac97", + .owner = THIS_MODULE, + .pm = AU1XPSCAC97_PMOPS, }, - .ops = &au1xpsc_ac97_dai_ops, + .probe = au1xpsc_ac97_drvprobe, + .remove = __devexit_p(au1xpsc_ac97_drvremove), }; -EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai); -static int __init au1xpsc_ac97_init(void) +static int __init au1xpsc_ac97_load(void) { au1xpsc_ac97_workdata = NULL; - return snd_soc_register_dai(&au1xpsc_ac97_dai); + return platform_driver_register(&au1xpsc_ac97_driver); } -static void __exit au1xpsc_ac97_exit(void) +static void __exit au1xpsc_ac97_unload(void) { - snd_soc_unregister_dai(&au1xpsc_ac97_dai); + platform_driver_unregister(&au1xpsc_ac97_driver); } -module_init(au1xpsc_ac97_init); -module_exit(au1xpsc_ac97_exit); +module_init(au1xpsc_ac97_load); +module_exit(au1xpsc_ac97_unload); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver"); -MODULE_AUTHOR("Manuel Lauss "); +MODULE_AUTHOR("Manuel Lauss"); + diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index bb58932..0cf2ca6 100644 --- a/sound/soc/au1x/psc-i2s.c +++ b/sound/soc/au1x/psc-i2s.c @@ -2,7 +2,7 @@ * Au12x0/Au1550 PSC ALSA ASoC audio support. * * (c) 2007-2008 MSC Vertriebsges.m.b.H., - * Manuel Lauss + * Manuel Lauss * * 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 @@ -265,16 +265,52 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, static int au1xpsc_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai) { + return au1xpsc_i2s_workdata ? 0 : -ENODEV; +} + +static void au1xpsc_i2s_remove(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ +} + +static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { + .trigger = au1xpsc_i2s_trigger, + .hw_params = au1xpsc_i2s_hw_params, + .set_fmt = au1xpsc_i2s_set_fmt, +}; + +struct snd_soc_dai au1xpsc_i2s_dai = { + .name = "au1xpsc_i2s", + .probe = au1xpsc_i2s_probe, + .remove = au1xpsc_i2s_remove, + .playback = { + .rates = AU1XPSC_I2S_RATES, + .formats = AU1XPSC_I2S_FMTS, + .channels_min = 2, + .channels_max = 8, /* 2 without external help */ + }, + .capture = { + .rates = AU1XPSC_I2S_RATES, + .formats = AU1XPSC_I2S_FMTS, + .channels_min = 2, + .channels_max = 8, /* 2 without external help */ + }, + .ops = &au1xpsc_i2s_dai_ops, +}; +EXPORT_SYMBOL(au1xpsc_i2s_dai); + +static int __init au1xpsc_i2s_drvprobe(struct platform_device *pdev) +{ struct resource *r; unsigned long sel; int ret; + struct au1xpsc_audio_data *wd; if (au1xpsc_i2s_workdata) return -EBUSY; - au1xpsc_i2s_workdata = - kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); - if (!au1xpsc_i2s_workdata) + wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); + if (!wd) return -ENOMEM; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -284,131 +320,146 @@ static int au1xpsc_i2s_probe(struct platform_device *pdev, } ret = -EBUSY; - au1xpsc_i2s_workdata->ioarea = - request_mem_region(r->start, r->end - r->start + 1, + wd->ioarea = request_mem_region(r->start, r->end - r->start + 1, "au1xpsc_i2s"); - if (!au1xpsc_i2s_workdata->ioarea) + if (!wd->ioarea) goto out0; - au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff); - if (!au1xpsc_i2s_workdata->mmio) + wd->mmio = ioremap(r->start, 0xffff); + if (!wd->mmio) goto out1; /* preserve PSC clock source set up by platform (dev.platform_data * is already occupied by soc layer) */ - sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK; - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); + sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK; + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); - au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata)); - au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); + au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(wd)); + au_writel(0, I2S_CFG(wd)); au_sync(); /* preconfigure: set max rx/tx fifo depths */ - au1xpsc_i2s_workdata->cfg |= - PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8; + wd->cfg |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8; /* don't wait for I2S core to become ready now; clocks may not * be running yet; depending on clock input for PSC a wait might * time out. */ - return 0; + ret = snd_soc_register_dai(&au1xpsc_i2s_dai); + if (ret) + goto out1; + /* finally add the DMA device for this PSC */ + wd->dmapd = au1xpsc_pcm_add(pdev); + if (wd->dmapd) { + platform_set_drvdata(pdev, wd); + au1xpsc_i2s_workdata = wd; + return 0; + } + + snd_soc_unregister_dai(&au1xpsc_i2s_dai); out1: - release_resource(au1xpsc_i2s_workdata->ioarea); - kfree(au1xpsc_i2s_workdata->ioarea); + release_resource(wd->ioarea); + kfree(wd->ioarea); out0: - kfree(au1xpsc_i2s_workdata); - au1xpsc_i2s_workdata = NULL; + kfree(wd); return ret; } -static void au1xpsc_i2s_remove(struct platform_device *pdev, - struct snd_soc_dai *dai) +static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev) { - au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); + struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); + + if (wd->dmapd) + au1xpsc_pcm_destroy(wd->dmapd); + + snd_soc_unregister_dai(&au1xpsc_i2s_dai); + + au_writel(0, I2S_CFG(wd)); au_sync(); - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); - iounmap(au1xpsc_i2s_workdata->mmio); - release_resource(au1xpsc_i2s_workdata->ioarea); - kfree(au1xpsc_i2s_workdata->ioarea); - kfree(au1xpsc_i2s_workdata); - au1xpsc_i2s_workdata = NULL; + iounmap(wd->mmio); + release_resource(wd->ioarea); + kfree(wd->ioarea); + kfree(wd); + + au1xpsc_i2s_workdata = NULL; /* MDEV */ + + return 0; } -static int au1xpsc_i2s_suspend(struct snd_soc_dai *cpu_dai) +#ifdef CONFIG_PM +static int au1xpsc_i2s_drvsuspend(struct device *dev) { + struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); + /* save interesting register and disable PSC */ - au1xpsc_i2s_workdata->pm[0] = - au_readl(PSC_SEL(au1xpsc_i2s_workdata)); + wd->pm[0] = au_readl(PSC_SEL(wd)); - au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); + au_writel(0, I2S_CFG(wd)); au_sync(); - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); return 0; } -static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai) +static int au1xpsc_i2s_drvresume(struct device *dev) { + struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); + /* select I2S mode and PSC clock */ - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); - au_writel(0, PSC_SEL(au1xpsc_i2s_workdata)); + au_writel(0, PSC_SEL(wd)); au_sync(); - au_writel(au1xpsc_i2s_workdata->pm[0], - PSC_SEL(au1xpsc_i2s_workdata)); + au_writel(wd->pm[0], PSC_SEL(wd)); au_sync(); return 0; } -static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { - .trigger = au1xpsc_i2s_trigger, - .hw_params = au1xpsc_i2s_hw_params, - .set_fmt = au1xpsc_i2s_set_fmt, +static struct dev_pm_ops au1xpsci2s_pmops = { + .suspend = au1xpsc_i2s_drvsuspend, + .resume = au1xpsc_i2s_drvresume, }; -struct snd_soc_dai au1xpsc_i2s_dai = { - .name = "au1xpsc_i2s", - .probe = au1xpsc_i2s_probe, - .remove = au1xpsc_i2s_remove, - .suspend = au1xpsc_i2s_suspend, - .resume = au1xpsc_i2s_resume, - .playback = { - .rates = AU1XPSC_I2S_RATES, - .formats = AU1XPSC_I2S_FMTS, - .channels_min = 2, - .channels_max = 8, /* 2 without external help */ - }, - .capture = { - .rates = AU1XPSC_I2S_RATES, - .formats = AU1XPSC_I2S_FMTS, - .channels_min = 2, - .channels_max = 8, /* 2 without external help */ +#define AU1XPSCI2S_PMOPS &au1xpsci2s_pmops + +#else + +#define AU1XPSCI2S_PMOPS NULL + +#endif + +static struct platform_driver au1xpsc_i2s_driver = { + .driver = { + .name = "au1xpsc_i2s", + .owner = THIS_MODULE, + .pm = AU1XPSCI2S_PMOPS, }, - .ops = &au1xpsc_i2s_dai_ops, + .probe = au1xpsc_i2s_drvprobe, + .remove = __devexit_p(au1xpsc_i2s_drvremove), }; -EXPORT_SYMBOL(au1xpsc_i2s_dai); -static int __init au1xpsc_i2s_init(void) +static int __init au1xpsc_i2s_load(void) { au1xpsc_i2s_workdata = NULL; - return snd_soc_register_dai(&au1xpsc_i2s_dai); + return platform_driver_register(&au1xpsc_i2s_driver); } -static void __exit au1xpsc_i2s_exit(void) +static void __exit au1xpsc_i2s_unload(void) { - snd_soc_unregister_dai(&au1xpsc_i2s_dai); + platform_driver_unregister(&au1xpsc_i2s_driver); } -module_init(au1xpsc_i2s_init); -module_exit(au1xpsc_i2s_exit); +module_init(au1xpsc_i2s_load); +module_exit(au1xpsc_i2s_unload); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver"); -MODULE_AUTHOR("Manuel Lauss "); +MODULE_AUTHOR("Manuel Lauss"); diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h index 3f474e8..32d3807 100644 --- a/sound/soc/au1x/psc.h +++ b/sound/soc/au1x/psc.h @@ -2,7 +2,7 @@ * Au12x0/Au1550 PSC ALSA ASoC audio support. * * (c) 2007-2008 MSC Vertriebsges.m.b.H., - * Manuel Lauss + * Manuel Lauss * * 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 @@ -21,6 +21,10 @@ extern struct snd_soc_dai au1xpsc_i2s_dai; extern struct snd_soc_platform au1xpsc_soc_platform; extern struct snd_ac97_bus_ops soc_ac97_ops; +/* DBDMA helpers */ +extern struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev); +extern void au1xpsc_pcm_destroy(struct platform_device *dmapd); + struct au1xpsc_audio_data { void __iomem *mmio; @@ -30,6 +34,7 @@ struct au1xpsc_audio_data { unsigned long pm[2]; struct resource *ioarea; struct mutex lock; + struct platform_device *dmapd; }; #define PCM_TX 0 -- cgit v0.10.2 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 diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3df3497..4a3e8dc 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 8f519ee..cacfc76 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 0000000..b3b60dd --- /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 0000000..ee19aa7 --- /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 v0.10.2 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 diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index f9121ef..c0b47df 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 v0.10.2 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 diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c index b3b60dd..7df5a17 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 v0.10.2 From 2624d5fa67a5d3d720613a4ab0672e8c387ba806 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 3 Nov 2009 21:56:13 +0000 Subject: ASoC: Move sysfs and debugfs functions to head of soc-core.c A fairly hefty change in diff terms but no actual code changes, will be used by the next commit. Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6e24654..d81a161 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -80,6 +80,173 @@ static int run_delayed_work(struct delayed_work *dwork) return ret; } +/* codec register dump */ +static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) +{ + int i, step = 1, count = 0; + + if (!codec->reg_cache_size) + return 0; + + if (codec->reg_cache_step) + step = codec->reg_cache_step; + + count += sprintf(buf, "%s registers\n", codec->name); + for (i = 0; i < codec->reg_cache_size; i += step) { + if (codec->readable_register && !codec->readable_register(i)) + continue; + + count += sprintf(buf + count, "%2x: ", i); + if (count >= PAGE_SIZE - 1) + break; + + if (codec->display_register) + count += codec->display_register(codec, buf + count, + PAGE_SIZE - count, i); + else + count += snprintf(buf + count, PAGE_SIZE - count, + "%4x", codec->read(codec, i)); + + if (count >= PAGE_SIZE - 1) + break; + + count += snprintf(buf + count, PAGE_SIZE - count, "\n"); + if (count >= PAGE_SIZE - 1) + break; + } + + /* Truncate count; min() would cause a warning */ + if (count >= PAGE_SIZE) + count = PAGE_SIZE - 1; + + return count; +} +static ssize_t codec_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct snd_soc_device *devdata = dev_get_drvdata(dev); + return soc_codec_reg_show(devdata->card->codec, buf); +} + +static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL); + +#ifdef CONFIG_DEBUG_FS +static int codec_reg_open_file(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + ssize_t ret; + struct snd_soc_codec *codec = file->private_data; + char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = soc_codec_reg_show(codec, buf); + if (ret >= 0) + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + kfree(buf); + return ret; +} + +static ssize_t codec_reg_write_file(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + char buf[32]; + int buf_size; + char *start = buf; + unsigned long reg, value; + int step = 1; + struct snd_soc_codec *codec = file->private_data; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + if (codec->reg_cache_step) + step = codec->reg_cache_step; + + while (*start == ' ') + start++; + reg = simple_strtoul(start, &start, 16); + if ((reg >= codec->reg_cache_size) || (reg % step)) + return -EINVAL; + while (*start == ' ') + start++; + if (strict_strtoul(start, 16, &value)) + return -EINVAL; + codec->write(codec, reg, value); + return buf_size; +} + +static const struct file_operations codec_reg_fops = { + .open = codec_reg_open_file, + .read = codec_reg_read_file, + .write = codec_reg_write_file, +}; + +static void soc_init_codec_debugfs(struct snd_soc_codec *codec) +{ + char codec_root[128]; + + if (codec->dev) + snprintf(codec_root, sizeof(codec_root), + "%s.%s", codec->name, dev_name(codec->dev)); + else + snprintf(codec_root, sizeof(codec_root), + "%s", codec->name); + + codec->debugfs_codec_root = debugfs_create_dir(codec_root, + debugfs_root); + if (!codec->debugfs_codec_root) { + printk(KERN_WARNING + "ASoC: Failed to create codec debugfs directory\n"); + return; + } + + codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, + codec->debugfs_codec_root, + codec, &codec_reg_fops); + if (!codec->debugfs_reg) + printk(KERN_WARNING + "ASoC: Failed to create codec register debugfs file\n"); + + codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744, + codec->debugfs_codec_root, + &codec->pop_time); + if (!codec->debugfs_pop_time) + printk(KERN_WARNING + "Failed to create pop time debugfs file\n"); + + codec->debugfs_dapm = debugfs_create_dir("dapm", + codec->debugfs_codec_root); + if (!codec->debugfs_dapm) + printk(KERN_WARNING + "Failed to create DAPM debugfs directory\n"); + + snd_soc_dapm_debugfs_init(codec); +} + +static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) +{ + debugfs_remove_recursive(codec->debugfs_codec_root); +} + +#else + +static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec) +{ +} + +static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) +{ +} +#endif + #ifdef CONFIG_SND_SOC_AC97_BUS /* unregister ac97 codec */ static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) @@ -1111,173 +1278,6 @@ int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg) } EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register); -/* codec register dump */ -static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) -{ - int i, step = 1, count = 0; - - if (!codec->reg_cache_size) - return 0; - - if (codec->reg_cache_step) - step = codec->reg_cache_step; - - count += sprintf(buf, "%s registers\n", codec->name); - for (i = 0; i < codec->reg_cache_size; i += step) { - if (codec->readable_register && !codec->readable_register(i)) - continue; - - count += sprintf(buf + count, "%2x: ", i); - if (count >= PAGE_SIZE - 1) - break; - - if (codec->display_register) - count += codec->display_register(codec, buf + count, - PAGE_SIZE - count, i); - else - count += snprintf(buf + count, PAGE_SIZE - count, - "%4x", codec->read(codec, i)); - - if (count >= PAGE_SIZE - 1) - break; - - count += snprintf(buf + count, PAGE_SIZE - count, "\n"); - if (count >= PAGE_SIZE - 1) - break; - } - - /* Truncate count; min() would cause a warning */ - if (count >= PAGE_SIZE) - count = PAGE_SIZE - 1; - - return count; -} -static ssize_t codec_reg_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct snd_soc_device *devdata = dev_get_drvdata(dev); - return soc_codec_reg_show(devdata->card->codec, buf); -} - -static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL); - -#ifdef CONFIG_DEBUG_FS -static int codec_reg_open_file(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - ssize_t ret; - struct snd_soc_codec *codec = file->private_data; - char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - ret = soc_codec_reg_show(codec, buf); - if (ret >= 0) - ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); - kfree(buf); - return ret; -} - -static ssize_t codec_reg_write_file(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - char buf[32]; - int buf_size; - char *start = buf; - unsigned long reg, value; - int step = 1; - struct snd_soc_codec *codec = file->private_data; - - buf_size = min(count, (sizeof(buf)-1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - if (codec->reg_cache_step) - step = codec->reg_cache_step; - - while (*start == ' ') - start++; - reg = simple_strtoul(start, &start, 16); - if ((reg >= codec->reg_cache_size) || (reg % step)) - return -EINVAL; - while (*start == ' ') - start++; - if (strict_strtoul(start, 16, &value)) - return -EINVAL; - codec->write(codec, reg, value); - return buf_size; -} - -static const struct file_operations codec_reg_fops = { - .open = codec_reg_open_file, - .read = codec_reg_read_file, - .write = codec_reg_write_file, -}; - -static void soc_init_codec_debugfs(struct snd_soc_codec *codec) -{ - char codec_root[128]; - - if (codec->dev) - snprintf(codec_root, sizeof(codec_root), - "%s.%s", codec->name, dev_name(codec->dev)); - else - snprintf(codec_root, sizeof(codec_root), - "%s", codec->name); - - codec->debugfs_codec_root = debugfs_create_dir(codec_root, - debugfs_root); - if (!codec->debugfs_codec_root) { - printk(KERN_WARNING - "ASoC: Failed to create codec debugfs directory\n"); - return; - } - - codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, - codec->debugfs_codec_root, - codec, &codec_reg_fops); - if (!codec->debugfs_reg) - printk(KERN_WARNING - "ASoC: Failed to create codec register debugfs file\n"); - - codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744, - codec->debugfs_codec_root, - &codec->pop_time); - if (!codec->debugfs_pop_time) - printk(KERN_WARNING - "Failed to create pop time debugfs file\n"); - - codec->debugfs_dapm = debugfs_create_dir("dapm", - codec->debugfs_codec_root); - if (!codec->debugfs_dapm) - printk(KERN_WARNING - "Failed to create DAPM debugfs directory\n"); - - snd_soc_dapm_debugfs_init(codec); -} - -static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) -{ - debugfs_remove_recursive(codec->debugfs_codec_root); -} - -#else - -static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec) -{ -} - -static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) -{ -} -#endif - /** * snd_soc_new_ac97_codec - initailise AC97 device * @codec: audio codec -- cgit v0.10.2 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 diff --git a/include/sound/soc.h b/include/sound/soc.h index b1245e3..7f3a4c5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -226,7 +226,6 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, /* pcm <-> DAI connect */ void snd_soc_free_pcms(struct snd_soc_device *socdev); int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid); -int snd_soc_init_card(struct snd_soc_device *socdev); /* set runtime hw params */ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 932299b..69bd0ac 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 c48485f..2e360c2 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 34b30ef..09c008a 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 d7440a9..39c0f758 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 e61dac5..d2fcc60 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 4d47bc4..3a14c6f 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 0abec0d..57a6846 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 e057c7b..b69861d 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 b61214d..364832c 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 565842d..ffe122d 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 38eac9c..d7f9bf1 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 5cda9e6..2afcd0a 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 c550750..b313033 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 befc648..bbc72c2 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 0b8dcb5..ee8cb2c 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 3387d9e..357b609 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 3395cf9..03cad25 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 3ca8934..bff476d 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 c0b47df..928257b 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 c33b92e..aa40d98 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 92ec034..a42e47d 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 714114b..2e35a35 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 bd7eecb..0e30997 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 5702435..e3c21eb 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 268cab2..2e2b01d 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 a09b23e..dde50d1 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 54189fb..70e0675 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 7df5a17..d8ffbd6 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 16e969a..1252a8a 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 bb95af9..e3675e7 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 4ba1e7e..50a3d65 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 8f73052..c652bc0 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 a0bbb28..ab2c0da 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 b48804b..0d185cb 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 94cdb81..bfeff4e 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 8d4fd3c..fc80aa6 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 b9b096a..40390af 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 b5c6f2c..07e3895 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 d66efb0..56a66e8 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 eff2933..c245f0e 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 d8d8f68..bee292e 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 f657e9a..e43cb2c 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 dac3977..0d4d2be 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 4cb6b10..3f1f844 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 e7d2840..0e817b8 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 1fd4e88..155cacf 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 ca3d449..5f81ecd 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); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d81a161..e2b6d75 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -970,6 +970,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) struct platform_device, dev); struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev; + struct snd_soc_codec *codec; struct snd_soc_platform *platform; struct snd_soc_dai *dai; int i, found, ret, ac97; @@ -1058,6 +1059,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) if (ret < 0) goto cpu_dai_err; } + codec = card->codec; if (platform->probe) { ret = platform->probe(pdev); @@ -1072,10 +1074,72 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); #endif + for (i = 0; i < card->num_links; i++) { + if (card->dai_link[i].init) { + ret = card->dai_link[i].init(codec); + if (ret < 0) { + printk(KERN_ERR "asoc: failed to init %s\n", + card->dai_link[i].stream_name); + continue; + } + } + if (card->dai_link[i].codec_dai->ac97_control) { + ac97 = 1; + snd_ac97_dev_add_pdata(codec->ac97, + card->dai_link[i].cpu_dai->ac97_pdata); + } + } + + snprintf(codec->card->shortname, sizeof(codec->card->shortname), + "%s", card->name); + snprintf(codec->card->longname, sizeof(codec->card->longname), + "%s (%s)", card->name, codec->name); + + /* Make sure all DAPM widgets are instantiated */ + snd_soc_dapm_new_widgets(codec); + + ret = snd_card_register(codec->card); + if (ret < 0) { + printk(KERN_ERR "asoc: failed to register soundcard for %s\n", + codec->name); + goto card_err; + } + + mutex_lock(&codec->mutex); +#ifdef CONFIG_SND_SOC_AC97_BUS + /* Only instantiate AC97 if not already done by the adaptor + * for the generic AC97 subsystem. + */ + if (ac97 && strcmp(codec->name, "AC97") != 0) { + ret = soc_ac97_dev_register(codec); + if (ret < 0) { + printk(KERN_ERR "asoc: AC97 device register failed\n"); + snd_card_free(codec->card); + mutex_unlock(&codec->mutex); + goto card_err; + } + } +#endif + + ret = snd_soc_dapm_sys_add(card->socdev->dev); + if (ret < 0) + printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n"); + + ret = device_create_file(card->socdev->dev, &dev_attr_codec_reg); + if (ret < 0) + printk(KERN_WARNING "asoc: failed to add codec sysfs files\n"); + + soc_init_codec_debugfs(codec); + mutex_unlock(&codec->mutex); + card->instantiated = 1; return; +card_err: + if (platform->remove) + platform->remove(pdev); + platform_err: if (codec_dev->remove) codec_dev->remove(pdev); @@ -1454,83 +1518,6 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid) EXPORT_SYMBOL_GPL(snd_soc_new_pcms); /** - * snd_soc_init_card - register sound card - * @socdev: the SoC audio device - * - * Register a SoC sound card. Also registers an AC97 device if the - * codec is AC97 for ad hoc devices. - * - * Returns 0 for success, else error. - */ -int snd_soc_init_card(struct snd_soc_device *socdev) -{ - struct snd_soc_card *card = socdev->card; - struct snd_soc_codec *codec = card->codec; - int ret = 0, i, ac97 = 0, err = 0; - - for (i = 0; i < card->num_links; i++) { - if (card->dai_link[i].init) { - err = card->dai_link[i].init(codec); - if (err < 0) { - printk(KERN_ERR "asoc: failed to init %s\n", - card->dai_link[i].stream_name); - continue; - } - } - if (card->dai_link[i].codec_dai->ac97_control) { - ac97 = 1; - snd_ac97_dev_add_pdata(codec->ac97, - card->dai_link[i].cpu_dai->ac97_pdata); - } - } - snprintf(codec->card->shortname, sizeof(codec->card->shortname), - "%s", card->name); - snprintf(codec->card->longname, sizeof(codec->card->longname), - "%s (%s)", card->name, codec->name); - - /* Make sure all DAPM widgets are instantiated */ - snd_soc_dapm_new_widgets(codec); - - ret = snd_card_register(codec->card); - if (ret < 0) { - printk(KERN_ERR "asoc: failed to register soundcard for %s\n", - codec->name); - goto out; - } - - mutex_lock(&codec->mutex); -#ifdef CONFIG_SND_SOC_AC97_BUS - /* Only instantiate AC97 if not already done by the adaptor - * for the generic AC97 subsystem. - */ - if (ac97 && strcmp(codec->name, "AC97") != 0) { - ret = soc_ac97_dev_register(codec); - if (ret < 0) { - printk(KERN_ERR "asoc: AC97 device register failed\n"); - snd_card_free(codec->card); - mutex_unlock(&codec->mutex); - goto out; - } - } -#endif - - err = snd_soc_dapm_sys_add(socdev->dev); - if (err < 0) - printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n"); - - err = device_create_file(socdev->dev, &dev_attr_codec_reg); - if (err < 0) - printk(KERN_WARNING "asoc: failed to add codec sysfs files\n"); - - soc_init_codec_debugfs(codec); - mutex_unlock(&codec->mutex); - -out: - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_init_card); - -/** * snd_soc_free_pcms - free sound card and pcms * @socdev: the SoC audio device * -- cgit v0.10.2 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 diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 4a3e8dc..52b005f 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 cacfc76..dbaecb1 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 0000000..f323092 --- /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 0000000..dbcf50e --- /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 v0.10.2 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 diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c index f323092..cc96411 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 v0.10.2 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 diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 2e360c2..b4be96d 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 09c008a..3b2222a 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 d7f9bf1..dda751c 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 0e30997..584af68 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 0d185cb..85f67db 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 v0.10.2 From 8f159d720b89f2a6c5ae8a8cc54823933a58120b Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 7 Nov 2009 01:33:53 -0700 Subject: ASoC/mpc5200: Track DMA position by period number instead of bytes All DMA blocks are lined up to period boundaries, but the DMA handling code tracks bytes instead. This patch reworks the code to track the period index into the DMA buffer instead of the physical address pointer. Doing so makes the code simpler and easier to understand. Signed-off-by: Grant Likely Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 6096d22..986d3c8 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -58,13 +58,11 @@ static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s) /* Prepare and enqueue the next buffer descriptor */ bd = bcom_prepare_next_buffer(s->bcom_task); bd->status = s->period_bytes; - bd->data[0] = s->period_next_pt; + bd->data[0] = s->runtime->dma_addr + (s->period_next * s->period_bytes); bcom_submit_next_buffer(s->bcom_task, NULL); /* Update for next period */ - s->period_next_pt += s->period_bytes; - if (s->period_next_pt >= s->period_end) - s->period_next_pt = s->period_start; + s->period_next = (s->period_next + 1) % s->runtime->periods; } static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s) @@ -79,7 +77,7 @@ static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s) if (bcom_queue_full(s->bcom_task)) return; - s->appl_ptr += s->period_size; + s->appl_ptr += s->runtime->period_size; psc_dma_bcom_enqueue_next_buffer(s); } @@ -91,7 +89,7 @@ static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s) if (bcom_queue_full(s->bcom_task)) return; - s->appl_ptr += s->period_size; + s->appl_ptr += s->runtime->period_size; psc_dma_bcom_enqueue_next_buffer(s); } @@ -108,9 +106,7 @@ static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream) while (bcom_buffer_done(s->bcom_task)) { bcom_retrieve_buffer(s->bcom_task, NULL, NULL); - s->period_current_pt += s->period_bytes; - if (s->period_current_pt >= s->period_end) - s->period_current_pt = s->period_start; + s->period_current = (s->period_current+1) % s->runtime->periods; } psc_dma_bcom_enqueue_tx(s); spin_unlock(&s->psc_dma->lock); @@ -133,9 +129,7 @@ static irqreturn_t psc_dma_bcom_irq_rx(int irq, void *_psc_dma_stream) while (bcom_buffer_done(s->bcom_task)) { bcom_retrieve_buffer(s->bcom_task, NULL, NULL); - s->period_current_pt += s->period_bytes; - if (s->period_current_pt >= s->period_end) - s->period_current_pt = s->period_start; + s->period_current = (s->period_current+1) % s->runtime->periods; psc_dma_bcom_enqueue_next_buffer(s); } @@ -185,12 +179,8 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: s->period_bytes = frames_to_bytes(runtime, runtime->period_size); - s->period_start = virt_to_phys(runtime->dma_area); - s->period_end = s->period_start + - (s->period_bytes * runtime->periods); - s->period_next_pt = s->period_start; - s->period_current_pt = s->period_start; - s->period_size = runtime->period_size; + s->period_next = 0; + s->period_current = 0; s->active = 1; /* track appl_ptr so that we have a better chance of detecting @@ -343,7 +333,7 @@ psc_dma_pointer(struct snd_pcm_substream *substream) else s = &psc_dma->playback; - count = s->period_current_pt - s->period_start; + count = s->period_current * s->period_bytes; return bytes_to_frames(substream->runtime, count); } diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h index 8d396bb..529f7a0 100644 --- a/sound/soc/fsl/mpc5200_dma.h +++ b/sound/soc/fsl/mpc5200_dma.h @@ -13,7 +13,6 @@ * @psc_dma: pointer back to parent psc_dma data structure * @bcom_task: bestcomm task structure * @irq: irq number for bestcomm task - * @period_start: physical address of start of DMA region * @period_end: physical address of end of DMA region * @period_next_pt: physical address of next DMA buffer to enqueue * @period_bytes: size of DMA period in bytes @@ -27,12 +26,9 @@ struct psc_dma_stream { struct bcom_task *bcom_task; int irq; struct snd_pcm_substream *stream; - dma_addr_t period_start; - dma_addr_t period_end; - dma_addr_t period_next_pt; - dma_addr_t period_current_pt; + int period_next; + int period_current; int period_bytes; - int period_size; }; /** -- cgit v0.10.2 From d56b6eb6df7f6fb92383a52d640e27f71e6262d0 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 7 Nov 2009 01:34:05 -0700 Subject: ASoC/mpc5200: get rid of the appl_ptr tracking nonsense Sound drivers PCM DMA is supposed to free-run until told to stop by the trigger callback. The current code tries to track appl_ptr, to avoid stale buffer data getting played out at the end of the data stream. Unfortunately it also results in race conditions which can cause the audio to stall. Signed-off-by: Grant Likely Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 986d3c8..4e47586 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -65,36 +65,6 @@ static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s) s->period_next = (s->period_next + 1) % s->runtime->periods; } -static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s) -{ - if (s->appl_ptr > s->runtime->control->appl_ptr) { - /* - * In this case s->runtime->control->appl_ptr has wrapped around. - * Play the data to the end of the boundary, then wrap our own - * appl_ptr back around. - */ - while (s->appl_ptr < s->runtime->boundary) { - if (bcom_queue_full(s->bcom_task)) - return; - - s->appl_ptr += s->runtime->period_size; - - psc_dma_bcom_enqueue_next_buffer(s); - } - s->appl_ptr -= s->runtime->boundary; - } - - while (s->appl_ptr < s->runtime->control->appl_ptr) { - - if (bcom_queue_full(s->bcom_task)) - return; - - s->appl_ptr += s->runtime->period_size; - - psc_dma_bcom_enqueue_next_buffer(s); - } -} - /* Bestcomm DMA irq handler */ static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream) { @@ -107,8 +77,9 @@ static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream) bcom_retrieve_buffer(s->bcom_task, NULL, NULL); s->period_current = (s->period_current+1) % s->runtime->periods; + + psc_dma_bcom_enqueue_next_buffer(s); } - psc_dma_bcom_enqueue_tx(s); spin_unlock(&s->psc_dma->lock); /* If the stream is active, then also inform the PCM middle layer @@ -182,28 +153,21 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) s->period_next = 0; s->period_current = 0; s->active = 1; - - /* track appl_ptr so that we have a better chance of detecting - * end of stream and not over running it. - */ s->runtime = runtime; - s->appl_ptr = s->runtime->control->appl_ptr - - (runtime->period_size * runtime->periods); /* Fill up the bestcomm bd queue and enable DMA. * This will begin filling the PSC's fifo. */ spin_lock_irqsave(&psc_dma->lock, flags); - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) bcom_gen_bd_rx_reset(s->bcom_task); - for (i = 0; i < runtime->periods; i++) - if (!bcom_queue_full(s->bcom_task)) - psc_dma_bcom_enqueue_next_buffer(s); - } else { + else bcom_gen_bd_tx_reset(s->bcom_task); - psc_dma_bcom_enqueue_tx(s); - } + + for (i = 0; i < runtime->periods; i++) + if (!bcom_queue_full(s->bcom_task)) + psc_dma_bcom_enqueue_next_buffer(s); bcom_enable(s->bcom_task); spin_unlock_irqrestore(&psc_dma->lock, flags); diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h index 529f7a0..d9c741b 100644 --- a/sound/soc/fsl/mpc5200_dma.h +++ b/sound/soc/fsl/mpc5200_dma.h @@ -19,8 +19,6 @@ */ struct psc_dma_stream { struct snd_pcm_runtime *runtime; - snd_pcm_uframes_t appl_ptr; - int active; struct psc_dma *psc_dma; struct bcom_task *bcom_task; -- cgit v0.10.2 From c4878274750ae0bb90c351a737ac6cdcb608e546 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 7 Nov 2009 01:34:18 -0700 Subject: ASoC/mpc5200: Improve printk debug output for trigger Signed-off-by: Grant Likely Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 4e47586..658e3fa 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -77,6 +77,7 @@ static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream) bcom_retrieve_buffer(s->bcom_task, NULL, NULL); s->period_current = (s->period_current+1) % s->runtime->periods; + s->period_count++; psc_dma_bcom_enqueue_next_buffer(s); } @@ -101,6 +102,7 @@ static irqreturn_t psc_dma_bcom_irq_rx(int irq, void *_psc_dma_stream) bcom_retrieve_buffer(s->bcom_task, NULL, NULL); s->period_current = (s->period_current+1) % s->runtime->periods; + s->period_count++; psc_dma_bcom_enqueue_next_buffer(s); } @@ -142,17 +144,17 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) else s = &psc_dma->playback; - dev_dbg(psc_dma->dev, "psc_dma_trigger(substream=%p, cmd=%i)" - " stream_id=%i\n", - substream, cmd, substream->pstr->stream); - switch (cmd) { case SNDRV_PCM_TRIGGER_START: + dev_dbg(psc_dma->dev, "START: stream=%i fbits=%u ps=%u #p=%u\n", + substream->pstr->stream, runtime->frame_bits, + (int)runtime->period_size, runtime->periods); s->period_bytes = frames_to_bytes(runtime, runtime->period_size); s->period_next = 0; s->period_current = 0; s->active = 1; + s->period_count = 0; s->runtime = runtime; /* Fill up the bestcomm bd queue and enable DMA. @@ -177,6 +179,8 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) break; case SNDRV_PCM_TRIGGER_STOP: + dev_dbg(psc_dma->dev, "STOP: stream=%i periods_count=%i\n", + substream->pstr->stream, s->period_count); s->active = 0; spin_lock_irqsave(&psc_dma->lock, flags); @@ -190,7 +194,8 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) break; default: - dev_dbg(psc_dma->dev, "invalid command\n"); + dev_dbg(psc_dma->dev, "unhandled trigger: stream=%i cmd=%i\n", + substream->pstr->stream, cmd); return -EINVAL; } diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h index d9c741b..c6f29e4 100644 --- a/sound/soc/fsl/mpc5200_dma.h +++ b/sound/soc/fsl/mpc5200_dma.h @@ -27,6 +27,7 @@ struct psc_dma_stream { int period_next; int period_current; int period_bytes; + int period_count; }; /** -- cgit v0.10.2 From 1d8222e8df07ce4f86fb7fa80b02bdee03b57985 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 7 Nov 2009 01:34:31 -0700 Subject: ASoC/mpc5200: add to_psc_dma_stream() helper Move the resolving of the psc_dma_stream pointer to a helper function to reduce duplicate code Signed-off-by: Grant Likely Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 658e3fa..9c88e15 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -133,17 +133,12 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct psc_dma_stream *s; + struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; u16 imr; unsigned long flags; int i; - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_dma->capture; - else - s = &psc_dma->playback; - switch (cmd) { case SNDRV_PCM_TRIGGER_START: dev_dbg(psc_dma->dev, "START: stream=%i fbits=%u ps=%u #p=%u\n", diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h index c6f29e4..956d6a5 100644 --- a/sound/soc/fsl/mpc5200_dma.h +++ b/sound/soc/fsl/mpc5200_dma.h @@ -68,6 +68,15 @@ struct psc_dma { } stats; }; +/* Utility for retrieving psc_dma_stream structure from a substream */ +inline struct psc_dma_stream * +to_psc_dma_stream(struct snd_pcm_substream *substream, struct psc_dma *psc_dma) +{ + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + return &psc_dma->capture; + return &psc_dma->playback; +} + int mpc5200_audio_dma_create(struct of_device *op); int mpc5200_audio_dma_destroy(struct of_device *op); -- cgit v0.10.2 From c939e5c82142978d9d534aca34187a8489fd13f3 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 7 Nov 2009 01:34:43 -0700 Subject: ASoC/mpc5200: fix enable/disable of AC97 slots The MPC5200 AC97 driver is disabling the slots when a stop trigger is received, but not reenabling them if the stream is started again without processing the hw_params again. This patch fixes the problem by caching the slot enable bit settings calculated at hw_params time so that they can be reapplied every time the start trigger is received. Signed-off-by: Grant Likely Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h index 956d6a5..22208b3 100644 --- a/sound/soc/fsl/mpc5200_dma.h +++ b/sound/soc/fsl/mpc5200_dma.h @@ -16,6 +16,7 @@ * @period_end: physical address of end of DMA region * @period_next_pt: physical address of next DMA buffer to enqueue * @period_bytes: size of DMA period in bytes + * @ac97_slot_bits: Enable bits for turning on the correct AC97 slot */ struct psc_dma_stream { struct snd_pcm_runtime *runtime; @@ -28,6 +29,9 @@ struct psc_dma_stream { int period_current; int period_bytes; int period_count; + + /* AC97 state */ + u32 ac97_slot_bits; }; /** diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index c4ae3e0..3dbc7f7 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -130,6 +130,7 @@ static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct psc_dma *psc_dma = cpu_dai->private_data; + struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" " periods=%i buffer_size=%i buffer_bytes=%i channels=%i" @@ -140,20 +141,10 @@ static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream, params_channels(params), params_rate(params), params_format(params)); - - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { - if (params_channels(params) == 1) - psc_dma->slots |= 0x00000100; - else - psc_dma->slots |= 0x00000300; - } else { - if (params_channels(params) == 1) - psc_dma->slots |= 0x01000000; - else - psc_dma->slots |= 0x03000000; - } - out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots); - + /* Determine the set of enable bits to turn on */ + s->ac97_slot_bits = (params_channels(params) == 1) ? 0x100 : 0x300; + if (substream->pstr->stream != SNDRV_PCM_STREAM_CAPTURE) + s->ac97_slot_bits <<= 16; return 0; } @@ -163,6 +154,8 @@ static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream, { struct psc_dma *psc_dma = cpu_dai->private_data; + dev_dbg(psc_dma->dev, "%s(substream=%p)\n", __func__, substream); + if (params_channels(params) == 1) out_be32(&psc_dma->psc_regs->ac97_slots, 0x01000000); else @@ -176,14 +169,24 @@ static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + dev_dbg(psc_dma->dev, "AC97 START: stream=%i\n", + substream->pstr->stream); + + /* Set the slot enable bits */ + psc_dma->slots |= s->ac97_slot_bits; + out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots); + break; + case SNDRV_PCM_TRIGGER_STOP: - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - psc_dma->slots &= 0xFFFF0000; - else - psc_dma->slots &= 0x0000FFFF; + dev_dbg(psc_dma->dev, "AC97 STOP: stream=%i\n", + substream->pstr->stream); + /* Clear the slot enable bits */ + psc_dma->slots &= ~(s->ac97_slot_bits); out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots); break; } -- cgit v0.10.2 From 9e5d86fe6a401f7957f6ea02ee300db0f6c03d03 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 9 Nov 2009 08:44:32 +0200 Subject: ASoC: Pandora: Pass SRG input clock frequency to the OMAP McBSP DAI Upcoming change to omap-mcbsp.c require that machine drivers using OMAP as a DAI master to pass sample rate generator input clock frequency to the omap-mcbsp.c DAI driver. Pandora is using 256*Fs output from the TWL4030 codec as an input clock to the McBSP sample rate generator. Signed-off-by: Jarkko Nikula Tested-by: Grazvydas Ignotas Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c index ad219aa..cace5f1 100644 --- a/sound/soc/omap/omap3pandora.c +++ b/sound/soc/omap/omap3pandora.c @@ -40,9 +40,12 @@ #define PREFIX "ASoC omap3pandora: " -static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai, - struct snd_soc_dai *cpu_dai, unsigned int fmt) +static int omap3pandora_cmn_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, unsigned int fmt) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; int ret; /* Set codec DAI configuration */ @@ -68,8 +71,9 @@ static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai, } /* Set McBSP clock to external */ - ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT, 0, - SND_SOC_CLOCK_IN); + ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT, + 256 * params_rate(params), + SND_SOC_CLOCK_IN); if (ret < 0) { pr_err(PREFIX "can't set cpu system clock\n"); return ret; @@ -87,11 +91,7 @@ static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai, static int omap3pandora_out_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - - return omap3pandora_cmn_hw_params(codec_dai, cpu_dai, + return omap3pandora_cmn_hw_params(substream, params, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS); @@ -100,11 +100,7 @@ static int omap3pandora_out_hw_params(struct snd_pcm_substream *substream, static int omap3pandora_in_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - - return omap3pandora_cmn_hw_params(codec_dai, cpu_dai, + return omap3pandora_cmn_hw_params(substream, params, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); -- cgit v0.10.2 From 5f63ef9909c187581c7f2c28fbc93866a0d59f7f Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Mon, 9 Nov 2009 19:02:15 +0000 Subject: ASoC: omap-mcbsp - add support for upto 16 channels. This patch increases the number of supported audio channels from 4 to 16 and has been sponsored by Shotspotter Inc. It also fixes a FSYNC rate calculation bug when McBSP is FSYNC master. Signed-off-by: Graeme Gregory Signed-off-by: Liam Girdwood Acked-by: Peter Ujfalusi Tested-by: Peter Ujfalusi Acked-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 3341f49..45be942 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -49,6 +49,8 @@ struct omap_mcbsp_data { */ int active; int configured; + unsigned int in_freq; + int clk_div; }; #define to_mcbsp(priv) container_of((priv), struct omap_mcbsp_data, bus_id) @@ -257,7 +259,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id; int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; unsigned long port; - unsigned int format; + unsigned int format, div, framesize, master; if (cpu_class_is_omap1()) { dma = omap1_dma_reqs[bus_id][substream->stream]; @@ -294,28 +296,19 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK; wpf = channels = params_channels(params); - switch (channels) { - case 2: - if (format == SND_SOC_DAIFMT_I2S) { - /* Use dual-phase frames */ - regs->rcr2 |= RPHASE; - regs->xcr2 |= XPHASE; - /* Set 1 word per (McBSP) frame for phase1 and phase2 */ - wpf--; - regs->rcr2 |= RFRLEN2(wpf - 1); - regs->xcr2 |= XFRLEN2(wpf - 1); - } - case 1: - case 4: - /* Set word per (McBSP) frame for phase1 */ - regs->rcr1 |= RFRLEN1(wpf - 1); - regs->xcr1 |= XFRLEN1(wpf - 1); - break; - default: - /* Unsupported number of channels */ - return -EINVAL; + if (channels == 2 && format == SND_SOC_DAIFMT_I2S) { + /* Use dual-phase frames */ + regs->rcr2 |= RPHASE; + regs->xcr2 |= XPHASE; + /* Set 1 word per (McBSP) frame for phase1 and phase2 */ + wpf--; + regs->rcr2 |= RFRLEN2(wpf - 1); + regs->xcr2 |= XFRLEN2(wpf - 1); } + regs->rcr1 |= RFRLEN1(wpf - 1); + regs->xcr1 |= XFRLEN1(wpf - 1); + switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: /* Set word lengths */ @@ -330,15 +323,30 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* In McBSP master modes, FRAME (i.e. sample rate) is generated + * by _counting_ BCLKs. Calculate frame size in BCLKs */ + master = mcbsp_data->fmt & SND_SOC_DAIFMT_MASTER_MASK; + if (master == SND_SOC_DAIFMT_CBS_CFS) { + div = mcbsp_data->clk_div ? mcbsp_data->clk_div : 1; + framesize = (mcbsp_data->in_freq / div) / params_rate(params); + + if (framesize < wlen * channels) { + printk(KERN_ERR "%s: not enough bandwidth for desired rate and " + "channels\n", __func__); + return -EINVAL; + } + } else + framesize = wlen * channels; + /* Set FS period and length in terms of bit clock periods */ switch (format) { case SND_SOC_DAIFMT_I2S: - regs->srgr2 |= FPER(wlen * channels - 1); - regs->srgr1 |= FWID(wlen - 1); + regs->srgr2 |= FPER(framesize - 1); + regs->srgr1 |= FWID((framesize >> 1) - 1); break; case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_B: - regs->srgr2 |= FPER(wlen * channels - 1); + regs->srgr2 |= FPER(framesize - 1); regs->srgr1 |= FWID(0); break; } @@ -454,6 +462,7 @@ static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai, if (div_id != OMAP_MCBSP_CLKGDV) return -ENODEV; + mcbsp_data->clk_div = div; regs->srgr1 |= CLKGDV(div - 1); return 0; @@ -554,6 +563,8 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; int err = 0; + mcbsp_data->in_freq = freq; + switch (clk_id) { case OMAP_MCBSP_SYSCLK_CLK: regs->srgr2 |= CLKSM; @@ -598,13 +609,13 @@ static struct snd_soc_dai_ops omap_mcbsp_dai_ops = { .id = (link_id), \ .playback = { \ .channels_min = 1, \ - .channels_max = 4, \ + .channels_max = 16, \ .rates = OMAP_MCBSP_RATES, \ .formats = SNDRV_PCM_FMTBIT_S16_LE, \ }, \ .capture = { \ .channels_min = 1, \ - .channels_max = 4, \ + .channels_max = 16, \ .rates = OMAP_MCBSP_RATES, \ .formats = SNDRV_PCM_FMTBIT_S16_LE, \ }, \ -- cgit v0.10.2 From cfd5324e699a2e74a44642d43dcf03d581f2a7db Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 4 Nov 2009 09:58:17 +0200 Subject: MFD: TWL4030: Add audio_mclk to the codec platform data Add audio_mclk to the platform data struct for the twl4030-codec MFD driver. Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz Signed-off-by: Mark Brown diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h index 42d6c72..c188961 100644 --- a/include/linux/i2c/twl4030.h +++ b/include/linux/i2c/twl4030.h @@ -414,6 +414,7 @@ struct twl4030_codec_vibra_data { }; struct twl4030_codec_data { + unsigned int audio_mclk; struct twl4030_codec_audio_data *audio; struct twl4030_codec_vibra_data *vibra; }; -- cgit v0.10.2 From 953e2f3d272db9db6671ad4f4244820557a71cf7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 4 Nov 2009 09:58:18 +0200 Subject: OMAP: Configure audio_mclk for twl4030-codec MFD audio_mclk value is going to be handled by the twl4030-codec MFD driver, configure the correct value for boards, which is using the twl4030 audio. Signed-off-by: Peter Ujfalusi Acked-by: Tony Lindgren Signed-off-by: Mark Brown diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c index 4f91f7a0..9afd957 100644 --- a/arch/arm/mach-omap2/board-3430sdp.c +++ b/arch/arm/mach-omap2/board-3430sdp.c @@ -415,6 +415,7 @@ static struct twl4030_codec_audio_data sdp3430_audio = { }; static struct twl4030_codec_data sdp3430_codec = { + .audio_mclk = 26000000, .audio = &sdp3430_audio, }; diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index 2161d85..8f0c106 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -259,6 +259,7 @@ static struct twl4030_codec_audio_data beagle_audio_data = { }; static struct twl4030_codec_data beagle_codec_data = { + .audio_mclk = 26000000, .audio = &beagle_audio_data, }; diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index d9a6103..5bb30cb 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -199,6 +199,7 @@ static struct twl4030_codec_audio_data omap3evm_audio_data = { }; static struct twl4030_codec_data omap3evm_codec_data = { + .audio_mclk = 26000000, .audio = &omap3evm_audio_data, }; diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index 5036b56..77790ee 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -286,6 +286,7 @@ static struct twl4030_codec_audio_data omap3pandora_audio_data = { }; static struct twl4030_codec_data omap3pandora_codec_data = { + .audio_mclk = 26000000, .audio = &omap3pandora_audio_data, }; diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c index dc55008..e1fb504 100644 --- a/arch/arm/mach-omap2/board-overo.c +++ b/arch/arm/mach-omap2/board-overo.c @@ -334,6 +334,7 @@ static struct twl4030_codec_audio_data overo_audio_data = { }; static struct twl4030_codec_data overo_codec_data = { + .audio_mclk = 26000000, .audio = &overo_audio_data, }; diff --git a/arch/arm/mach-omap2/board-zoom2.c b/arch/arm/mach-omap2/board-zoom2.c index f1b4e7c..de3a38d 100644 --- a/arch/arm/mach-omap2/board-zoom2.c +++ b/arch/arm/mach-omap2/board-zoom2.c @@ -234,6 +234,7 @@ static struct twl4030_codec_audio_data zoom2_audio_data = { }; static struct twl4030_codec_data zoom2_codec_data = { + .audio_mclk = 26000000, .audio = &zoom2_audio_data, }; -- cgit v0.10.2 From f9b4639e045c750e2bad37462476403995508350 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 4 Nov 2009 09:58:19 +0200 Subject: MFD: twl4030-codec: APLL_INFREQ handling in the MFD driver Configure the APLL_INFREQ field in the APLL_CTL register based on the platform data. Provide also a function for childs to query the audio_mclk frequency. Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz Signed-off-by: Mark Brown diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c index 9710307..77b9149 100644 --- a/drivers/mfd/twl4030-codec.c +++ b/drivers/mfd/twl4030-codec.c @@ -41,6 +41,7 @@ struct twl4030_codec_resource { }; struct twl4030_codec { + unsigned int audio_mclk; struct mutex mutex; struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX]; struct mfd_cell cells[TWL4030_CODEC_CELLS]; @@ -145,12 +146,45 @@ int twl4030_codec_disable_resource(unsigned id) } EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource); +unsigned int twl4030_codec_get_mclk(void) +{ + struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + + return codec->audio_mclk; +} +EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk); + static int __devinit twl4030_codec_probe(struct platform_device *pdev) { struct twl4030_codec *codec; struct twl4030_codec_data *pdata = pdev->dev.platform_data; struct mfd_cell *cell = NULL; int ret, childs = 0; + u8 val; + + if (!pdata) { + dev_err(&pdev->dev, "Platform data is missing\n"); + return -EINVAL; + } + + /* Configure APLL_INFREQ and disable APLL if enabled */ + val = 0; + switch (pdata->audio_mclk) { + case 19200000: + val |= TWL4030_APLL_INFREQ_19200KHZ; + break; + case 26000000: + val |= TWL4030_APLL_INFREQ_26000KHZ; + break; + case 38400000: + val |= TWL4030_APLL_INFREQ_38400KHZ; + break; + default: + dev_err(&pdev->dev, "Invalid audio_mclk\n"); + return -EINVAL; + } + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + val, TWL4030_REG_APLL_CTL); codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL); if (!codec) @@ -160,6 +194,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) twl4030_codec_dev = pdev; mutex_init(&codec->mutex); + codec->audio_mclk = pdata->audio_mclk; /* Codec power */ codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE; diff --git a/include/linux/mfd/twl4030-codec.h b/include/linux/mfd/twl4030-codec.h index ef0a304..2ec317c 100644 --- a/include/linux/mfd/twl4030-codec.h +++ b/include/linux/mfd/twl4030-codec.h @@ -267,5 +267,6 @@ enum twl4030_codec_res { int twl4030_codec_disable_resource(enum twl4030_codec_res id); int twl4030_codec_enable_resource(enum twl4030_codec_res id); +unsigned int twl4030_codec_get_mclk(void); #endif /* End of __TWL4030_CODEC_H__ */ -- cgit v0.10.2 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 diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 928257b..510b8b2 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 v0.10.2 From a68cc8daebdd8ba7fe457ab4b2a0ccdf3cedc9f8 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Mon, 9 Nov 2009 09:40:09 -0700 Subject: ASoC: mpc5200: remove duplicate identical IRQ handler The TX and RX irq handlers are identical. Merge them Signed-off-by: Grant Likely Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 9c88e15..30ed568 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -66,32 +66,7 @@ static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s) } /* Bestcomm DMA irq handler */ -static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream) -{ - struct psc_dma_stream *s = _psc_dma_stream; - - spin_lock(&s->psc_dma->lock); - /* For each finished period, dequeue the completed period buffer - * and enqueue a new one in it's place. */ - while (bcom_buffer_done(s->bcom_task)) { - bcom_retrieve_buffer(s->bcom_task, NULL, NULL); - - s->period_current = (s->period_current+1) % s->runtime->periods; - s->period_count++; - - psc_dma_bcom_enqueue_next_buffer(s); - } - spin_unlock(&s->psc_dma->lock); - - /* If the stream is active, then also inform the PCM middle layer - * of the period finished event. */ - if (s->active) - snd_pcm_period_elapsed(s->stream); - - return IRQ_HANDLED; -} - -static irqreturn_t psc_dma_bcom_irq_rx(int irq, void *_psc_dma_stream) +static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) { struct psc_dma_stream *s = _psc_dma_stream; @@ -486,11 +461,9 @@ int mpc5200_audio_dma_create(struct of_device *op) rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, "psc-dma-status", psc_dma); - rc |= request_irq(psc_dma->capture.irq, - &psc_dma_bcom_irq_rx, IRQF_SHARED, + rc |= request_irq(psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, "psc-dma-capture", &psc_dma->capture); - rc |= request_irq(psc_dma->playback.irq, - &psc_dma_bcom_irq_tx, IRQF_SHARED, + rc |= request_irq(psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, "psc-dma-playback", &psc_dma->playback); if (rc) { ret = -ENODEV; -- cgit v0.10.2 From 7aae816dae150caad8880357e42303935c0179a8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 10 Nov 2009 16:08:04 +0000 Subject: ASoC: Add bit clock rate calculator utility functions Many devices need to calculate the bit clock rate desired to work out the clock configuration required for the device. Provide utility functions to do this using both hw_params structures and raw numbers. Signed-off-by: Mark Brown Acked-by: Liam Girdwood diff --git a/include/sound/soc.h b/include/sound/soc.h index 7f3a4c5..310a219 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -227,6 +227,11 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, void snd_soc_free_pcms(struct snd_soc_device *socdev); int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid); +/* Utility functions to get clock rates from various things */ +int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); +int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params); +int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms); + /* set runtime hw params */ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, const struct snd_pcm_hardware *hw); diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 0c5eac0..1470141 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,4 +1,4 @@ -snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o +snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c new file mode 100644 index 0000000..b16aaae --- /dev/null +++ b/sound/soc/soc-utils.c @@ -0,0 +1,68 @@ +/* + * soc-util.c -- ALSA SoC Audio Layer utility functions + * + * Copyright 2009 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * Liam Girdwood + * + * + * 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 + +int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots) +{ + return sample_size * channels * tdm_slots; +} +EXPORT_SYMBOL_GPL(snd_soc_calc_frame_size); + +int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params) +{ + int sample_size; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + sample_size = 16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S20_3BE: + sample_size = 20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + sample_size = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + sample_size = 32; + break; + default: + return -ENOTSUPP; + } + + return snd_soc_calc_frame_size(sample_size, params_channels(params), + 1); +} +EXPORT_SYMBOL_GPL(snd_soc_params_to_frame_size); + +int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params) +{ + int ret; + + ret = snd_soc_params_to_frame_size(params); + + if (ret > 0) + return ret * params_rate(params); + else + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk); -- cgit v0.10.2 From ba2b87f5a93659a28cc4fb812ccd7b4146ac3aa9 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Wed, 11 Nov 2009 14:02:18 +0900 Subject: ASoC: Fixed arguments passed to SMDK64xx set_pll Corrected the order of 'source' and 'pll_id' arguments. Signed-off-by: Jassi Brar Signed-off-by: Mark Brown diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/s3c24xx/smdk64xx_wm8580.c index cb8a916..216dd1e 100644 --- a/sound/soc/s3c24xx/smdk64xx_wm8580.c +++ b/sound/soc/s3c24xx/smdk64xx_wm8580.c @@ -115,7 +115,7 @@ static int smdk64xx_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - ret = snd_soc_dai_set_pll(codec_dai, 0, WM8580_PLLA, + ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0, SMDK64XX_WM8580_FREQ, pll_out); if (ret < 0) return ret; -- cgit v0.10.2 From f773205300fa4a5a405f8ed6e3bb97e46c6eefb4 Mon Sep 17 00:00:00 2001 From: Barry Song <21cnbao@gmail.com> Date: Thu, 12 Nov 2009 12:01:47 +0800 Subject: ASoC: move setting ac97 platformdata earlier than ac97 read/write While probing, AC97 codec drivers and soc-core generically execute the following sequence: snd_soc_new_ac97_codec -> snd_soc_new_pcms -> reset ac-link/read AC97 ID to detect ->... -> set platform_data to ac97 by soc-core commit 474828a40f6ddab6e2a3475a19c5c84aa3ec7d60 adds platform_data to snd_ac97 instance. But ac97 platform data hasn't given to snd_ac97 before actual ac97 operations. Then while ac97_read access platform_data of snd_ac97 for detecting, NULL pointer oops will fire. That means old platform_data patch doesn't work in real-life cases. This patch moves the operation of setting ac97 platform_data earlier than ac97 reading/writing operations. Then it makes platform_data of AC97 become practically useful. Signed-off-by: Barry Song <21cnbao@gmail.com> Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e2b6d75..ef8f282 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1083,11 +1083,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) continue; } } - if (card->dai_link[i].codec_dai->ac97_control) { + if (card->dai_link[i].codec_dai->ac97_control) ac97 = 1; - snd_ac97_dev_add_pdata(codec->ac97, - card->dai_link[i].cpu_dai->ac97_pdata); - } } snprintf(codec->card->shortname, sizeof(codec->card->shortname), @@ -1510,6 +1507,10 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid) mutex_unlock(&codec->mutex); return ret; } + if (card->dai_link[i].codec_dai->ac97_control) { + snd_ac97_dev_add_pdata(codec->ac97, + card->dai_link[i].cpu_dai->ac97_pdata); + } } mutex_unlock(&codec->mutex); -- cgit v0.10.2 From c871a05315d1a76034ea06feeda92081e1d608bf Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Thu, 12 Nov 2009 17:14:04 +0900 Subject: ASoC: Add jack_status_check callback function for GPIO jacks The jack_status_check callback function is the interface to check the status of the jack. Some target provides the method to distinguish what is the jack inserted - headphone jack, microphone jack, tvout jack, etc, so we can implement it using the jack_status_check function. Signed-off-by: Joonyoung Shim Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 310a219..13b117a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -332,6 +332,8 @@ struct snd_soc_jack_gpio { int debounce_time; struct snd_soc_jack *jack; struct work_struct work; + + int (*jack_status_check)(void); }; #endif diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 1212414..3c07a94 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -163,6 +163,9 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) else report = 0; + if (gpio->jack_status_check) + report = gpio->jack_status_check(); + snd_soc_jack_report(jack, report, gpio->report); } -- cgit v0.10.2 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 diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index b4be96d..2c18e3d 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 3b2222a..5d48918 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 57a6846..ff96656 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 364832c..82fca28 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 dda751c..e000cdf 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 b313033..d2ff1cd 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 ee8cb2c..1709e3f 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 03cad25..2b4dc2b 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 bff476d..2a013e4 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 510b8b2..5f1681f 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 a42e47d..a2763c2 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 2e35a35..f82125d 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 584af68..b432f4d 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 e3c21eb..265e68c 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 2e2b01d..d3a61d7 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 dde50d1..d077df6 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 70e0675..24a3560 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 1252a8a..3fb653b 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 e3675e7..3a49781 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 50a3d65..475c67a 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 c652bc0..d6850da 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 85f67db..c9438dd 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 bfeff4e..b8cae17 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 fc80aa6..3d850b9 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 40390af..d07bcc1 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 07e3895..a8007d5 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 56a66e8..d9540d5 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 c245f0e..81c57b5 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 bee292e..2862e4d 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 e43cb2c..341481e 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 0d4d2be..5e32f2e 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 3f1f844..c468497 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 0e817b8..dfffc6c 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 155cacf..2a08722 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 5f81ecd..00bac31 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 v0.10.2 From 8df89bc35c188e389295eaf7917653f13c83ce70 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Mon, 16 Nov 2009 16:19:25 +0200 Subject: ASoC: OMAP: enable Overo driver for CM-T35 Signed-off-by: Mike Rapoport Acked-by: Liam Girdwood Acked-by: Steve Sakoman Signed-off-by: Mark Brown diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index bb5731a..4dc6b15a 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -43,12 +43,13 @@ config SND_OMAP_SOC_OSK5912 Say Y if you want to add support for SoC audio on osk5912. config SND_OMAP_SOC_OVERO - tristate "SoC Audio support for Gumstix Overo" - depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OVERO + tristate "SoC Audio support for Gumstix Overo and CompuLab CM-T35" + depends on TWL4030_CORE && SND_OMAP_SOC && (MACH_OVERO || MACH_CM_T35) select SND_OMAP_SOC_MCBSP select SND_SOC_TWL4030 help - Say Y if you want to add support for SoC audio on the Gumstix Overo. + Say Y if you want to add support for SoC audio on the + Gumstix Overo or CompuLab CM-T35 config SND_OMAP_SOC_OMAP2EVM tristate "SoC Audio support for OMAP2EVM board" diff --git a/sound/soc/omap/overo.c b/sound/soc/omap/overo.c index ec4f8fd..97a4d63 100644 --- a/sound/soc/omap/overo.c +++ b/sound/soc/omap/overo.c @@ -107,8 +107,8 @@ static int __init overo_soc_init(void) { int ret; - if (!machine_is_overo()) { - pr_debug("Not Overo!\n"); + if (!(machine_is_overo() || machine_is_cm_t35())) { + pr_debug("Incomatible machine!\n"); return -ENODEV; } printk(KERN_INFO "overo SoC init\n"); -- cgit v0.10.2 From f9ede4eca01cc64ce37549c282b6fde727c0ec84 Mon Sep 17 00:00:00 2001 From: Marin Mitov Date: Mon, 16 Nov 2009 21:39:26 +0200 Subject: ASoC: Use DMA_BIT_MASK(32) instead of deprecated DMA_32BIT_MASK Signed-off-by: Marin Mitov Signed-off-by: Takashi Iwai diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c index 83b8028..0eb1722 100644 --- a/sound/soc/s6000/s6000-pcm.c +++ b/sound/soc/s6000/s6000-pcm.c @@ -423,7 +423,7 @@ static void s6000_pcm_free(struct snd_pcm *pcm) snd_pcm_lib_preallocate_free_for_all(pcm); } -static u64 s6000_pcm_dmamask = DMA_32BIT_MASK; +static u64 s6000_pcm_dmamask = DMA_BIT_MASK(32); static int s6000_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm) @@ -435,7 +435,7 @@ static int s6000_pcm_new(struct snd_card *card, if (!card->dev->dma_mask) card->dev->dma_mask = &s6000_pcm_dmamask; if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_32BIT_MASK; + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); if (params->dma_in) { s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_in), -- cgit v0.10.2 From faa31776e4c799d631d8cd3a13dd50cd95b0875e Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Tue, 17 Nov 2009 16:53:23 +0900 Subject: ASoC: Rename s3c24xx_pcm prefix to s3c_dma The s3c24xx_pcm prefix for the soc_platform is inappropriate when some Samsung SoCs have PCM controllers which will eventually have drivers and hence namespace ambiguities. To resolve naming ambiguities in future the following have been renamed in order 1) s3c24xx_pcm_dma_params -> s3c_dma_params 2) s3c24xx_pcm_preallocate_dma_buffer -> s3c_preallocate_dma_buffer 3) s3c24xx_pcm_dmamask -> s3c_dma_mask 4) s3c24xx_pcm_XXX -> s3c_dma_XXX Signed-off-by: Jassi Brar Acked-by: Ben Dooks Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c index 28b0ab2..5a442aa 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.c +++ b/sound/soc/s3c24xx/s3c-i2s-v2.c @@ -394,7 +394,7 @@ static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd, int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); unsigned long irqs; int ret = 0; - int channel = ((struct s3c24xx_pcm_dma_params *) + int channel = ((struct s3c_dma_params *) rtd->dai->cpu_dai->dma_data)->channel; pr_debug("Entered %s\n", __func__); diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.h b/sound/soc/s3c24xx/s3c-i2s-v2.h index f66854a..ecf8eaa 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.h +++ b/sound/soc/s3c24xx/s3c-i2s-v2.h @@ -49,8 +49,8 @@ struct s3c_i2sv2_info { unsigned char master; - struct s3c24xx_pcm_dma_params *dma_playback; - struct s3c24xx_pcm_dma_params *dma_capture; + struct s3c_dma_params *dma_playback; + struct s3c_dma_params *dma_capture; u32 suspend_iismod; u32 suspend_iiscon; diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c index ac5e47b..23718ea 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.c +++ b/sound/soc/s3c24xx/s3c2412-i2s.c @@ -50,14 +50,14 @@ static struct s3c2410_dma_client s3c2412_dma_client_in = { .name = "I2S PCM Stereo in" }; -static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_out = { +static struct s3c_dma_params s3c2412_i2s_pcm_stereo_out = { .client = &s3c2412_dma_client_out, .channel = DMACH_I2S_OUT, .dma_addr = S3C2410_PA_IIS + S3C2412_IISTXD, .dma_size = 4, }; -static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_in = { +static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = { .client = &s3c2412_dma_client_in, .channel = DMACH_I2S_IN, .dma_addr = S3C2410_PA_IIS + S3C2412_IISRXD, diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c index b25e9f9..678b176 100644 --- a/sound/soc/s3c24xx/s3c2443-ac97.c +++ b/sound/soc/s3c24xx/s3c2443-ac97.c @@ -188,21 +188,21 @@ static struct s3c2410_dma_client s3c2443_dma_client_micin = { .name = "AC97 Mic Mono in" }; -static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_out = { +static struct s3c_dma_params s3c2443_ac97_pcm_stereo_out = { .client = &s3c2443_dma_client_out, .channel = DMACH_PCM_OUT, .dma_addr = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA, .dma_size = 4, }; -static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_in = { +static struct s3c_dma_params s3c2443_ac97_pcm_stereo_in = { .client = &s3c2443_dma_client_in, .channel = DMACH_PCM_IN, .dma_addr = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA, .dma_size = 4, }; -static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = { +static struct s3c_dma_params s3c2443_ac97_mic_mono_in = { .client = &s3c2443_dma_client_micin, .channel = DMACH_MIC_IN, .dma_addr = S3C2440_PA_AC97 + S3C_AC97_MIC_DATA, @@ -290,7 +290,7 @@ static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd, { u32 ac_glbctrl; struct snd_soc_pcm_runtime *rtd = substream->private_data; - int channel = ((struct s3c24xx_pcm_dma_params *) + int channel = ((struct s3c_dma_params *) rtd->dai->cpu_dai->dma_data)->channel; ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); @@ -339,7 +339,7 @@ static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream, { u32 ac_glbctrl; struct snd_soc_pcm_runtime *rtd = substream->private_data; - int channel = ((struct s3c24xx_pcm_dma_params *) + int channel = ((struct s3c_dma_params *) rtd->dai->cpu_dai->dma_data)->channel; ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c index c76b8bb..afb4bc9 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/s3c24xx/s3c24xx-i2s.c @@ -49,14 +49,14 @@ static struct s3c2410_dma_client s3c24xx_dma_client_in = { .name = "I2S PCM Stereo in" }; -static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_out = { +static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_out = { .client = &s3c24xx_dma_client_out, .channel = DMACH_I2S_OUT, .dma_addr = S3C2410_PA_IIS + S3C2410_IISFIFO, .dma_size = 2, }; -static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = { +static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_in = { .client = &s3c24xx_dma_client_in, .channel = DMACH_I2S_IN, .dma_addr = S3C2410_PA_IIS + S3C2410_IISFIFO, @@ -258,12 +258,12 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: iismod &= ~S3C2410_IISMOD_16BIT; - ((struct s3c24xx_pcm_dma_params *) + ((struct s3c_dma_params *) rtd->dai->cpu_dai->dma_data)->dma_size = 1; break; case SNDRV_PCM_FORMAT_S16_LE: iismod |= S3C2410_IISMOD_16BIT; - ((struct s3c24xx_pcm_dma_params *) + ((struct s3c_dma_params *) rtd->dai->cpu_dai->dma_data)->dma_size = 2; break; default: @@ -280,7 +280,7 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; - int channel = ((struct s3c24xx_pcm_dma_params *) + int channel = ((struct s3c_dma_params *) rtd->dai->cpu_dai->dma_data)->channel; pr_debug("Entered %s\n", __func__); diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c index 151a694..cb49400 100644 --- a/sound/soc/s3c24xx/s3c24xx-pcm.c +++ b/sound/soc/s3c24xx/s3c24xx-pcm.c @@ -32,7 +32,7 @@ #include "s3c24xx-pcm.h" -static const struct snd_pcm_hardware s3c24xx_pcm_hardware = { +static const struct snd_pcm_hardware s3c_dma_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | @@ -62,15 +62,15 @@ struct s3c24xx_runtime_data { dma_addr_t dma_start; dma_addr_t dma_pos; dma_addr_t dma_end; - struct s3c24xx_pcm_dma_params *params; + struct s3c_dma_params *params; }; -/* s3c24xx_pcm_enqueue +/* s3c_dma_enqueue * * place a dma buffer onto the queue for the dma system * to handle. */ -static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream) +static void s3c_dma_enqueue(struct snd_pcm_substream *substream) { struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; dma_addr_t pos = prtd->dma_pos; @@ -132,19 +132,19 @@ static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel, spin_lock(&prtd->lock); if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) { prtd->dma_loaded--; - s3c24xx_pcm_enqueue(substream); + s3c_dma_enqueue(substream); } spin_unlock(&prtd->lock); } -static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, +static int s3c_dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct s3c24xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data; + struct s3c_dma_params *dma = rtd->dai->cpu_dai->dma_data; unsigned long totbytes = params_buffer_bytes(params); int ret = 0; @@ -197,7 +197,7 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } -static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream) +static int s3c_dma_hw_free(struct snd_pcm_substream *substream) { struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; @@ -214,7 +214,7 @@ static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream) return 0; } -static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream) +static int s3c_dma_prepare(struct snd_pcm_substream *substream) { struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; int ret = 0; @@ -247,12 +247,12 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream) prtd->dma_pos = prtd->dma_start; /* enqueue dma buffers */ - s3c24xx_pcm_enqueue(substream); + s3c_dma_enqueue(substream); return ret; } -static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int s3c_dma_trigger(struct snd_pcm_substream *substream, int cmd) { struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; int ret = 0; @@ -287,7 +287,7 @@ static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } static snd_pcm_uframes_t -s3c24xx_pcm_pointer(struct snd_pcm_substream *substream) +s3c_dma_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd = runtime->private_data; @@ -322,7 +322,7 @@ s3c24xx_pcm_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(substream->runtime, res); } -static int s3c24xx_pcm_open(struct snd_pcm_substream *substream) +static int s3c_dma_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd; @@ -330,7 +330,7 @@ static int s3c24xx_pcm_open(struct snd_pcm_substream *substream) pr_debug("Entered %s\n", __func__); snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware); + snd_soc_set_runtime_hwparams(substream, &s3c_dma_hardware); prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL); if (prtd == NULL) @@ -342,7 +342,7 @@ static int s3c24xx_pcm_open(struct snd_pcm_substream *substream) return 0; } -static int s3c24xx_pcm_close(struct snd_pcm_substream *substream) +static int s3c_dma_close(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd = runtime->private_data; @@ -350,14 +350,14 @@ static int s3c24xx_pcm_close(struct snd_pcm_substream *substream) pr_debug("Entered %s\n", __func__); if (!prtd) - pr_debug("s3c24xx_pcm_close called with prtd == NULL\n"); + pr_debug("s3c_dma_close called with prtd == NULL\n"); kfree(prtd); return 0; } -static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream, +static int s3c_dma_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -370,23 +370,23 @@ static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream, runtime->dma_bytes); } -static struct snd_pcm_ops s3c24xx_pcm_ops = { - .open = s3c24xx_pcm_open, - .close = s3c24xx_pcm_close, +static struct snd_pcm_ops s3c_dma_ops = { + .open = s3c_dma_open, + .close = s3c_dma_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = s3c24xx_pcm_hw_params, - .hw_free = s3c24xx_pcm_hw_free, - .prepare = s3c24xx_pcm_prepare, - .trigger = s3c24xx_pcm_trigger, - .pointer = s3c24xx_pcm_pointer, - .mmap = s3c24xx_pcm_mmap, + .hw_params = s3c_dma_hw_params, + .hw_free = s3c_dma_hw_free, + .prepare = s3c_dma_prepare, + .trigger = s3c_dma_trigger, + .pointer = s3c_dma_pointer, + .mmap = s3c_dma_mmap, }; -static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +static int s3c_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = s3c24xx_pcm_hardware.buffer_bytes_max; + size_t size = s3c_dma_hardware.buffer_bytes_max; pr_debug("Entered %s\n", __func__); @@ -401,7 +401,7 @@ static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) return 0; } -static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm) +static void s3c_dma_free_dma_buffers(struct snd_pcm *pcm) { struct snd_pcm_substream *substream; struct snd_dma_buffer *buf; @@ -424,9 +424,9 @@ static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm) } } -static u64 s3c24xx_pcm_dmamask = DMA_BIT_MASK(32); +static u64 s3c_dma_mask = DMA_BIT_MASK(32); -static int s3c24xx_pcm_new(struct snd_card *card, +static int s3c_dma_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm) { int ret = 0; @@ -434,19 +434,19 @@ static int s3c24xx_pcm_new(struct snd_card *card, pr_debug("Entered %s\n", __func__); if (!card->dev->dma_mask) - card->dev->dma_mask = &s3c24xx_pcm_dmamask; + card->dev->dma_mask = &s3c_dma_mask; if (!card->dev->coherent_dma_mask) card->dev->coherent_dma_mask = 0xffffffff; if (dai->playback.channels_min) { - ret = s3c24xx_pcm_preallocate_dma_buffer(pcm, + ret = s3c_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); if (ret) goto out; } if (dai->capture.channels_min) { - ret = s3c24xx_pcm_preallocate_dma_buffer(pcm, + ret = s3c_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); if (ret) goto out; @@ -457,9 +457,9 @@ static int s3c24xx_pcm_new(struct snd_card *card, struct snd_soc_platform s3c24xx_soc_platform = { .name = "s3c24xx-audio", - .pcm_ops = &s3c24xx_pcm_ops, - .pcm_new = s3c24xx_pcm_new, - .pcm_free = s3c24xx_pcm_free_dma_buffers, + .pcm_ops = &s3c_dma_ops, + .pcm_new = s3c_dma_new, + .pcm_free = s3c_dma_free_dma_buffers, }; EXPORT_SYMBOL_GPL(s3c24xx_soc_platform); @@ -476,5 +476,5 @@ static void __exit s3c24xx_soc_platform_exit(void) module_exit(s3c24xx_soc_platform_exit); MODULE_AUTHOR("Ben Dooks, "); -MODULE_DESCRIPTION("Samsung S3C24XX PCM DMA module"); +MODULE_DESCRIPTION("Samsung S3C Audio DMA module"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.h b/sound/soc/s3c24xx/s3c24xx-pcm.h index 0088c79..8cbc071 100644 --- a/sound/soc/s3c24xx/s3c24xx-pcm.h +++ b/sound/soc/s3c24xx/s3c24xx-pcm.h @@ -9,13 +9,13 @@ * ALSA PCM interface for the Samsung S3C24xx CPU */ -#ifndef _S3C24XX_PCM_H -#define _S3C24XX_PCM_H +#ifndef _S3C_AUDIO_H +#define _S3C_AUDIO_H #define ST_RUNNING (1<<0) #define ST_OPENED (1<<1) -struct s3c24xx_pcm_dma_params { +struct s3c_dma_params { struct s3c2410_dma_client *client; /* stream identifier */ int channel; /* Channel ID */ dma_addr_t dma_addr; diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c index d68cae1..719d63c 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.c +++ b/sound/soc/s3c24xx/s3c64xx-i2s.c @@ -46,7 +46,7 @@ static struct s3c2410_dma_client s3c64xx_dma_client_in = { .name = "I2S PCM Stereo in" }; -static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_out[2] = { +static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_out[2] = { [0] = { .channel = DMACH_I2S0_OUT, .client = &s3c64xx_dma_client_out, @@ -61,7 +61,7 @@ static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_out[2] = { }, }; -static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_in[2] = { +static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_in[2] = { [0] = { .channel = DMACH_I2S0_IN, .client = &s3c64xx_dma_client_in, -- cgit v0.10.2 From d3ff5a3e610d62d9cdad5b7d53749c9381e244ed Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Tue, 17 Nov 2009 16:53:31 +0900 Subject: ASoC: Rename 's3c24xx-pcm' driver to 's3c-dma' Making room for namespace for the PCM Controller driver the platform driver(s3c24xx-pcm) has been renamed to SoC agnostic name 's3c-dma'. Signed-off-by: Jassi Brar Acked-by: Ben Dooks Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index 7790406..ff0a105 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile @@ -1,5 +1,5 @@ # S3c24XX Platform Support -snd-soc-s3c24xx-objs := s3c24xx-pcm.o +snd-soc-s3c24xx-objs := s3c-dma.o snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o snd-soc-s3c64xx-i2s-objs := s3c64xx-i2s.o diff --git a/sound/soc/s3c24xx/jive_wm8750.c b/sound/soc/s3c24xx/jive_wm8750.c index 93e6c87..59dc2c6 100644 --- a/sound/soc/s3c24xx/jive_wm8750.c +++ b/sound/soc/s3c24xx/jive_wm8750.c @@ -25,7 +25,7 @@ #include -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c2412-i2s.h" #include "../codecs/wm8750.h" diff --git a/sound/soc/s3c24xx/ln2440sbc_alc650.c b/sound/soc/s3c24xx/ln2440sbc_alc650.c index 12c7148..d00d359 100644 --- a/sound/soc/s3c24xx/ln2440sbc_alc650.c +++ b/sound/soc/s3c24xx/ln2440sbc_alc650.c @@ -24,7 +24,7 @@ #include #include "../codecs/ac97.h" -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-ac97.h" static struct snd_soc_card ln2440sbc; diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c index 26409a9..dea83d3 100644 --- a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c @@ -32,7 +32,7 @@ #include #include #include "../codecs/wm8753.h" -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" static struct snd_soc_card neo1973_gta02; diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 77de6c5..0cb4f86 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -36,7 +36,7 @@ #include "../codecs/wm8753.h" #include "lm4857.h" -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" /* define the scenarios */ diff --git a/sound/soc/s3c24xx/s3c-dma.c b/sound/soc/s3c24xx/s3c-dma.c new file mode 100644 index 0000000..7725e26 --- /dev/null +++ b/sound/soc/s3c24xx/s3c-dma.c @@ -0,0 +1,481 @@ +/* + * s3c-dma.c -- ALSA Soc Audio Layer + * + * (c) 2006 Wolfson Microelectronics PLC. + * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + * + * Copyright 2004-2005 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * 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 + +#include +#include +#include + +#include "s3c-dma.h" + +static const struct snd_pcm_hardware s3c_dma_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_LE | + SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S8, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 128*1024, + .period_bytes_min = PAGE_SIZE, + .period_bytes_max = PAGE_SIZE*2, + .periods_min = 2, + .periods_max = 128, + .fifo_size = 32, +}; + +struct s3c24xx_runtime_data { + spinlock_t lock; + int state; + unsigned int dma_loaded; + unsigned int dma_limit; + unsigned int dma_period; + dma_addr_t dma_start; + dma_addr_t dma_pos; + dma_addr_t dma_end; + struct s3c_dma_params *params; +}; + +/* s3c_dma_enqueue + * + * place a dma buffer onto the queue for the dma system + * to handle. +*/ +static void s3c_dma_enqueue(struct snd_pcm_substream *substream) +{ + struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; + dma_addr_t pos = prtd->dma_pos; + unsigned int limit; + int ret; + + pr_debug("Entered %s\n", __func__); + + if (s3c_dma_has_circular()) + limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period; + else + limit = prtd->dma_limit; + + pr_debug("%s: loaded %d, limit %d\n", + __func__, prtd->dma_loaded, limit); + + while (prtd->dma_loaded < limit) { + unsigned long len = prtd->dma_period; + + pr_debug("dma_loaded: %d\n", prtd->dma_loaded); + + if ((pos + len) > prtd->dma_end) { + len = prtd->dma_end - pos; + pr_debug(KERN_DEBUG "%s: corrected dma len %ld\n", + __func__, len); + } + + ret = s3c2410_dma_enqueue(prtd->params->channel, + substream, pos, len); + + if (ret == 0) { + prtd->dma_loaded++; + pos += prtd->dma_period; + if (pos >= prtd->dma_end) + pos = prtd->dma_start; + } else + break; + } + + prtd->dma_pos = pos; +} + +static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel, + void *dev_id, int size, + enum s3c2410_dma_buffresult result) +{ + struct snd_pcm_substream *substream = dev_id; + struct s3c24xx_runtime_data *prtd; + + pr_debug("Entered %s\n", __func__); + + if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR) + return; + + prtd = substream->runtime->private_data; + + if (substream) + snd_pcm_period_elapsed(substream); + + spin_lock(&prtd->lock); + if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) { + prtd->dma_loaded--; + s3c_dma_enqueue(substream); + } + + spin_unlock(&prtd->lock); +} + +static int s3c_dma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct s3c24xx_runtime_data *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct s3c_dma_params *dma = rtd->dai->cpu_dai->dma_data; + unsigned long totbytes = params_buffer_bytes(params); + int ret = 0; + + pr_debug("Entered %s\n", __func__); + + /* return if this is a bufferless transfer e.g. + * codec <--> BT codec or GSM modem -- lg FIXME */ + if (!dma) + return 0; + + /* this may get called several times by oss emulation + * with different params -HW */ + if (prtd->params == NULL) { + /* prepare DMA */ + prtd->params = dma; + + pr_debug("params %p, client %p, channel %d\n", prtd->params, + prtd->params->client, prtd->params->channel); + + ret = s3c2410_dma_request(prtd->params->channel, + prtd->params->client, NULL); + + if (ret < 0) { + printk(KERN_ERR "failed to get dma channel\n"); + return ret; + } + + /* use the circular buffering if we have it available. */ + if (s3c_dma_has_circular()) + s3c2410_dma_setflags(prtd->params->channel, + S3C2410_DMAF_CIRCULAR); + } + + s3c2410_dma_set_buffdone_fn(prtd->params->channel, + s3c24xx_audio_buffdone); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + runtime->dma_bytes = totbytes; + + spin_lock_irq(&prtd->lock); + prtd->dma_loaded = 0; + prtd->dma_limit = runtime->hw.periods_min; + prtd->dma_period = params_period_bytes(params); + prtd->dma_start = runtime->dma_addr; + prtd->dma_pos = prtd->dma_start; + prtd->dma_end = prtd->dma_start + totbytes; + spin_unlock_irq(&prtd->lock); + + return 0; +} + +static int s3c_dma_hw_free(struct snd_pcm_substream *substream) +{ + struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; + + pr_debug("Entered %s\n", __func__); + + /* TODO - do we need to ensure DMA flushed */ + snd_pcm_set_runtime_buffer(substream, NULL); + + if (prtd->params) { + s3c2410_dma_free(prtd->params->channel, prtd->params->client); + prtd->params = NULL; + } + + return 0; +} + +static int s3c_dma_prepare(struct snd_pcm_substream *substream) +{ + struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; + int ret = 0; + + pr_debug("Entered %s\n", __func__); + + /* return if this is a bufferless transfer e.g. + * codec <--> BT codec or GSM modem -- lg FIXME */ + if (!prtd->params) + return 0; + + /* channel needs configuring for mem=>device, increment memory addr, + * sync to pclk, half-word transfers to the IIS-FIFO. */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + s3c2410_dma_devconfig(prtd->params->channel, + S3C2410_DMASRC_MEM, + prtd->params->dma_addr); + } else { + s3c2410_dma_devconfig(prtd->params->channel, + S3C2410_DMASRC_HW, + prtd->params->dma_addr); + } + + s3c2410_dma_config(prtd->params->channel, + prtd->params->dma_size); + + /* flush the DMA channel */ + s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH); + prtd->dma_loaded = 0; + prtd->dma_pos = prtd->dma_start; + + /* enqueue dma buffers */ + s3c_dma_enqueue(substream); + + return ret; +} + +static int s3c_dma_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; + int ret = 0; + + pr_debug("Entered %s\n", __func__); + + spin_lock(&prtd->lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + prtd->state |= ST_RUNNING; + s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + prtd->state &= ~ST_RUNNING; + s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP); + break; + + default: + ret = -EINVAL; + break; + } + + spin_unlock(&prtd->lock); + + return ret; +} + +static snd_pcm_uframes_t +s3c_dma_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct s3c24xx_runtime_data *prtd = runtime->private_data; + unsigned long res; + dma_addr_t src, dst; + + pr_debug("Entered %s\n", __func__); + + spin_lock(&prtd->lock); + s3c2410_dma_getposition(prtd->params->channel, &src, &dst); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + res = dst - prtd->dma_start; + else + res = src - prtd->dma_start; + + spin_unlock(&prtd->lock); + + pr_debug("Pointer %x %x\n", src, dst); + + /* we seem to be getting the odd error from the pcm library due + * to out-of-bounds pointers. this is maybe due to the dma engine + * not having loaded the new values for the channel before being + * callled... (todo - fix ) + */ + + if (res >= snd_pcm_lib_buffer_bytes(substream)) { + if (res == snd_pcm_lib_buffer_bytes(substream)) + res = 0; + } + + return bytes_to_frames(substream->runtime, res); +} + +static int s3c_dma_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct s3c24xx_runtime_data *prtd; + + pr_debug("Entered %s\n", __func__); + + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + snd_soc_set_runtime_hwparams(substream, &s3c_dma_hardware); + + prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + + spin_lock_init(&prtd->lock); + + runtime->private_data = prtd; + return 0; +} + +static int s3c_dma_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct s3c24xx_runtime_data *prtd = runtime->private_data; + + pr_debug("Entered %s\n", __func__); + + if (!prtd) + pr_debug("s3c_dma_close called with prtd == NULL\n"); + + kfree(prtd); + + return 0; +} + +static int s3c_dma_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + pr_debug("Entered %s\n", __func__); + + return dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); +} + +static struct snd_pcm_ops s3c_dma_ops = { + .open = s3c_dma_open, + .close = s3c_dma_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = s3c_dma_hw_params, + .hw_free = s3c_dma_hw_free, + .prepare = s3c_dma_prepare, + .trigger = s3c_dma_trigger, + .pointer = s3c_dma_pointer, + .mmap = s3c_dma_mmap, +}; + +static int s3c_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = s3c_dma_hardware.buffer_bytes_max; + + pr_debug("Entered %s\n", __func__); + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + if (!buf->area) + return -ENOMEM; + buf->bytes = size; + return 0; +} + +static void s3c_dma_free_dma_buffers(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + pr_debug("Entered %s\n", __func__); + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + +static u64 s3c_dma_mask = DMA_BIT_MASK(32); + +static int s3c_dma_new(struct snd_card *card, + struct snd_soc_dai *dai, struct snd_pcm *pcm) +{ + int ret = 0; + + pr_debug("Entered %s\n", __func__); + + if (!card->dev->dma_mask) + card->dev->dma_mask = &s3c_dma_mask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = 0xffffffff; + + if (dai->playback.channels_min) { + ret = s3c_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (dai->capture.channels_min) { + ret = s3c_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } + out: + return ret; +} + +struct snd_soc_platform s3c24xx_soc_platform = { + .name = "s3c24xx-audio", + .pcm_ops = &s3c_dma_ops, + .pcm_new = s3c_dma_new, + .pcm_free = s3c_dma_free_dma_buffers, +}; +EXPORT_SYMBOL_GPL(s3c24xx_soc_platform); + +static int __init s3c24xx_soc_platform_init(void) +{ + return snd_soc_register_platform(&s3c24xx_soc_platform); +} +module_init(s3c24xx_soc_platform_init); + +static void __exit s3c24xx_soc_platform_exit(void) +{ + snd_soc_unregister_platform(&s3c24xx_soc_platform); +} +module_exit(s3c24xx_soc_platform_exit); + +MODULE_AUTHOR("Ben Dooks, "); +MODULE_DESCRIPTION("Samsung S3C Audio DMA module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/s3c-dma.h b/sound/soc/s3c24xx/s3c-dma.h new file mode 100644 index 0000000..69bb6bf --- /dev/null +++ b/sound/soc/s3c24xx/s3c-dma.h @@ -0,0 +1,31 @@ +/* + * s3c-dma.h -- + * + * 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. + * + * ALSA PCM interface for the Samsung S3C24xx CPU + */ + +#ifndef _S3C_AUDIO_H +#define _S3C_AUDIO_H + +#define ST_RUNNING (1<<0) +#define ST_OPENED (1<<1) + +struct s3c_dma_params { + struct s3c2410_dma_client *client; /* stream identifier */ + int channel; /* Channel ID */ + dma_addr_t dma_addr; + int dma_size; /* Size of the DMA transfer */ +}; + +#define S3C24XX_DAI_I2S 0 + +/* platform data */ +extern struct snd_soc_platform s3c24xx_soc_platform; +extern struct snd_ac97_bus_ops s3c24xx_ac97_ops; + +#endif diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c index 5a442aa..e994d83 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.c +++ b/sound/soc/s3c24xx/s3c-i2s-v2.c @@ -35,7 +35,7 @@ #include #include "s3c-i2s-v2.h" -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #undef S3C_IIS_V2_SUPPORTED diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c index 23718ea..359e593 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.c +++ b/sound/soc/s3c24xx/s3c2412-i2s.c @@ -37,7 +37,7 @@ #include #include -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c2412-i2s.h" #define S3C2412_I2S_DEBUG 0 diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c index 678b176..0191e3a 100644 --- a/sound/soc/s3c24xx/s3c2443-ac97.c +++ b/sound/soc/s3c24xx/s3c2443-ac97.c @@ -35,7 +35,7 @@ #include #include -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-ac97.h" struct s3c24xx_ac97_info { diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c index afb4bc9..0bc5950 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/s3c24xx/s3c24xx-i2s.c @@ -38,7 +38,7 @@ #include -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" static struct s3c2410_dma_client s3c24xx_dma_client_out = { diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c deleted file mode 100644 index cb49400..0000000 --- a/sound/soc/s3c24xx/s3c24xx-pcm.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * s3c24xx-pcm.c -- ALSA Soc Audio Layer - * - * (c) 2006 Wolfson Microelectronics PLC. - * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com - * - * Copyright 2004-2005 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * 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 - -#include -#include -#include - -#include "s3c24xx-pcm.h" - -static const struct snd_pcm_hardware s3c_dma_hardware = { - .info = SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_RESUME, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_U16_LE | - SNDRV_PCM_FMTBIT_U8 | - SNDRV_PCM_FMTBIT_S8, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 128*1024, - .period_bytes_min = PAGE_SIZE, - .period_bytes_max = PAGE_SIZE*2, - .periods_min = 2, - .periods_max = 128, - .fifo_size = 32, -}; - -struct s3c24xx_runtime_data { - spinlock_t lock; - int state; - unsigned int dma_loaded; - unsigned int dma_limit; - unsigned int dma_period; - dma_addr_t dma_start; - dma_addr_t dma_pos; - dma_addr_t dma_end; - struct s3c_dma_params *params; -}; - -/* s3c_dma_enqueue - * - * place a dma buffer onto the queue for the dma system - * to handle. -*/ -static void s3c_dma_enqueue(struct snd_pcm_substream *substream) -{ - struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; - dma_addr_t pos = prtd->dma_pos; - unsigned int limit; - int ret; - - pr_debug("Entered %s\n", __func__); - - if (s3c_dma_has_circular()) { - limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period; - } else - limit = prtd->dma_limit; - - pr_debug("%s: loaded %d, limit %d\n", __func__, prtd->dma_loaded, limit); - - while (prtd->dma_loaded < limit) { - unsigned long len = prtd->dma_period; - - pr_debug("dma_loaded: %d\n", prtd->dma_loaded); - - if ((pos + len) > prtd->dma_end) { - len = prtd->dma_end - pos; - pr_debug(KERN_DEBUG "%s: corrected dma len %ld\n", - __func__, len); - } - - ret = s3c2410_dma_enqueue(prtd->params->channel, - substream, pos, len); - - if (ret == 0) { - prtd->dma_loaded++; - pos += prtd->dma_period; - if (pos >= prtd->dma_end) - pos = prtd->dma_start; - } else - break; - } - - prtd->dma_pos = pos; -} - -static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel, - void *dev_id, int size, - enum s3c2410_dma_buffresult result) -{ - struct snd_pcm_substream *substream = dev_id; - struct s3c24xx_runtime_data *prtd; - - pr_debug("Entered %s\n", __func__); - - if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR) - return; - - prtd = substream->runtime->private_data; - - if (substream) - snd_pcm_period_elapsed(substream); - - spin_lock(&prtd->lock); - if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) { - prtd->dma_loaded--; - s3c_dma_enqueue(substream); - } - - spin_unlock(&prtd->lock); -} - -static int s3c_dma_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct s3c24xx_runtime_data *prtd = runtime->private_data; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct s3c_dma_params *dma = rtd->dai->cpu_dai->dma_data; - unsigned long totbytes = params_buffer_bytes(params); - int ret = 0; - - pr_debug("Entered %s\n", __func__); - - /* return if this is a bufferless transfer e.g. - * codec <--> BT codec or GSM modem -- lg FIXME */ - if (!dma) - return 0; - - /* this may get called several times by oss emulation - * with different params -HW */ - if (prtd->params == NULL) { - /* prepare DMA */ - prtd->params = dma; - - pr_debug("params %p, client %p, channel %d\n", prtd->params, - prtd->params->client, prtd->params->channel); - - ret = s3c2410_dma_request(prtd->params->channel, - prtd->params->client, NULL); - - if (ret < 0) { - printk(KERN_ERR "failed to get dma channel\n"); - return ret; - } - - /* use the circular buffering if we have it available. */ - if (s3c_dma_has_circular()) - s3c2410_dma_setflags(prtd->params->channel, - S3C2410_DMAF_CIRCULAR); - } - - s3c2410_dma_set_buffdone_fn(prtd->params->channel, - s3c24xx_audio_buffdone); - - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - - runtime->dma_bytes = totbytes; - - spin_lock_irq(&prtd->lock); - prtd->dma_loaded = 0; - prtd->dma_limit = runtime->hw.periods_min; - prtd->dma_period = params_period_bytes(params); - prtd->dma_start = runtime->dma_addr; - prtd->dma_pos = prtd->dma_start; - prtd->dma_end = prtd->dma_start + totbytes; - spin_unlock_irq(&prtd->lock); - - return 0; -} - -static int s3c_dma_hw_free(struct snd_pcm_substream *substream) -{ - struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; - - pr_debug("Entered %s\n", __func__); - - /* TODO - do we need to ensure DMA flushed */ - snd_pcm_set_runtime_buffer(substream, NULL); - - if (prtd->params) { - s3c2410_dma_free(prtd->params->channel, prtd->params->client); - prtd->params = NULL; - } - - return 0; -} - -static int s3c_dma_prepare(struct snd_pcm_substream *substream) -{ - struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; - int ret = 0; - - pr_debug("Entered %s\n", __func__); - - /* return if this is a bufferless transfer e.g. - * codec <--> BT codec or GSM modem -- lg FIXME */ - if (!prtd->params) - return 0; - - /* channel needs configuring for mem=>device, increment memory addr, - * sync to pclk, half-word transfers to the IIS-FIFO. */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - s3c2410_dma_devconfig(prtd->params->channel, - S3C2410_DMASRC_MEM, - prtd->params->dma_addr); - } else { - s3c2410_dma_devconfig(prtd->params->channel, - S3C2410_DMASRC_HW, - prtd->params->dma_addr); - } - - s3c2410_dma_config(prtd->params->channel, - prtd->params->dma_size); - - /* flush the DMA channel */ - s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH); - prtd->dma_loaded = 0; - prtd->dma_pos = prtd->dma_start; - - /* enqueue dma buffers */ - s3c_dma_enqueue(substream); - - return ret; -} - -static int s3c_dma_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; - int ret = 0; - - pr_debug("Entered %s\n", __func__); - - spin_lock(&prtd->lock); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - prtd->state |= ST_RUNNING; - s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START); - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - prtd->state &= ~ST_RUNNING; - s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP); - break; - - default: - ret = -EINVAL; - break; - } - - spin_unlock(&prtd->lock); - - return ret; -} - -static snd_pcm_uframes_t -s3c_dma_pointer(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct s3c24xx_runtime_data *prtd = runtime->private_data; - unsigned long res; - dma_addr_t src, dst; - - pr_debug("Entered %s\n", __func__); - - spin_lock(&prtd->lock); - s3c2410_dma_getposition(prtd->params->channel, &src, &dst); - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - res = dst - prtd->dma_start; - else - res = src - prtd->dma_start; - - spin_unlock(&prtd->lock); - - pr_debug("Pointer %x %x\n", src, dst); - - /* we seem to be getting the odd error from the pcm library due - * to out-of-bounds pointers. this is maybe due to the dma engine - * not having loaded the new values for the channel before being - * callled... (todo - fix ) - */ - - if (res >= snd_pcm_lib_buffer_bytes(substream)) { - if (res == snd_pcm_lib_buffer_bytes(substream)) - res = 0; - } - - return bytes_to_frames(substream->runtime, res); -} - -static int s3c_dma_open(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct s3c24xx_runtime_data *prtd; - - pr_debug("Entered %s\n", __func__); - - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - snd_soc_set_runtime_hwparams(substream, &s3c_dma_hardware); - - prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL); - if (prtd == NULL) - return -ENOMEM; - - spin_lock_init(&prtd->lock); - - runtime->private_data = prtd; - return 0; -} - -static int s3c_dma_close(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct s3c24xx_runtime_data *prtd = runtime->private_data; - - pr_debug("Entered %s\n", __func__); - - if (!prtd) - pr_debug("s3c_dma_close called with prtd == NULL\n"); - - kfree(prtd); - - return 0; -} - -static int s3c_dma_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - - pr_debug("Entered %s\n", __func__); - - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); -} - -static struct snd_pcm_ops s3c_dma_ops = { - .open = s3c_dma_open, - .close = s3c_dma_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = s3c_dma_hw_params, - .hw_free = s3c_dma_hw_free, - .prepare = s3c_dma_prepare, - .trigger = s3c_dma_trigger, - .pointer = s3c_dma_pointer, - .mmap = s3c_dma_mmap, -}; - -static int s3c_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = s3c_dma_hardware.buffer_bytes_max; - - pr_debug("Entered %s\n", __func__); - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - if (!buf->area) - return -ENOMEM; - buf->bytes = size; - return 0; -} - -static void s3c_dma_free_dma_buffers(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - pr_debug("Entered %s\n", __func__); - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; - } -} - -static u64 s3c_dma_mask = DMA_BIT_MASK(32); - -static int s3c_dma_new(struct snd_card *card, - struct snd_soc_dai *dai, struct snd_pcm *pcm) -{ - int ret = 0; - - pr_debug("Entered %s\n", __func__); - - if (!card->dev->dma_mask) - card->dev->dma_mask = &s3c_dma_mask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = 0xffffffff; - - if (dai->playback.channels_min) { - ret = s3c_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (dai->capture.channels_min) { - ret = s3c_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - out: - return ret; -} - -struct snd_soc_platform s3c24xx_soc_platform = { - .name = "s3c24xx-audio", - .pcm_ops = &s3c_dma_ops, - .pcm_new = s3c_dma_new, - .pcm_free = s3c_dma_free_dma_buffers, -}; -EXPORT_SYMBOL_GPL(s3c24xx_soc_platform); - -static int __init s3c24xx_soc_platform_init(void) -{ - return snd_soc_register_platform(&s3c24xx_soc_platform); -} -module_init(s3c24xx_soc_platform_init); - -static void __exit s3c24xx_soc_platform_exit(void) -{ - snd_soc_unregister_platform(&s3c24xx_soc_platform); -} -module_exit(s3c24xx_soc_platform_exit); - -MODULE_AUTHOR("Ben Dooks, "); -MODULE_DESCRIPTION("Samsung S3C Audio DMA module"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.h b/sound/soc/s3c24xx/s3c24xx-pcm.h deleted file mode 100644 index 8cbc071..0000000 --- a/sound/soc/s3c24xx/s3c24xx-pcm.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * s3c24xx-pcm.h -- - * - * 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. - * - * ALSA PCM interface for the Samsung S3C24xx CPU - */ - -#ifndef _S3C_AUDIO_H -#define _S3C_AUDIO_H - -#define ST_RUNNING (1<<0) -#define ST_OPENED (1<<1) - -struct s3c_dma_params { - struct s3c2410_dma_client *client; /* stream identifier */ - int channel; /* Channel ID */ - dma_addr_t dma_addr; - int dma_size; /* Size of the DMA transfer */ -}; - -#define S3C24XX_DAI_I2S 0 - -/* platform data */ -extern struct snd_soc_platform s3c24xx_soc_platform; -extern struct snd_ac97_bus_ops s3c24xx_ac97_ops; - -#endif diff --git a/sound/soc/s3c24xx/s3c24xx_simtec.c b/sound/soc/s3c24xx/s3c24xx_simtec.c index 1966e0d..507b2ed 100644 --- a/sound/soc/s3c24xx/s3c24xx_simtec.c +++ b/sound/soc/s3c24xx/s3c24xx_simtec.c @@ -21,7 +21,7 @@ #include -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" #include "s3c24xx_simtec.h" diff --git a/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c b/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c index 8346bd9..bdf8951 100644 --- a/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c +++ b/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c @@ -18,7 +18,7 @@ #include -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" #include "s3c24xx_simtec.h" diff --git a/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c b/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c index 25797e0..185c0ac 100644 --- a/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c +++ b/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c @@ -18,7 +18,7 @@ #include -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" #include "s3c24xx_simtec.h" diff --git a/sound/soc/s3c24xx/s3c24xx_uda134x.c b/sound/soc/s3c24xx/s3c24xx_uda134x.c index c215d32..052d596 100644 --- a/sound/soc/s3c24xx/s3c24xx_uda134x.c +++ b/sound/soc/s3c24xx/s3c24xx_uda134x.c @@ -24,7 +24,7 @@ #include -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" #include "../codecs/uda134x.h" diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c index 719d63c..cc7edb5 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.c +++ b/sound/soc/s3c24xx/s3c64xx-i2s.c @@ -35,7 +35,7 @@ #include #include -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c64xx-i2s.h" static struct s3c2410_dma_client s3c64xx_dma_client_out = { diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/s3c24xx/smdk2443_wm9710.c index a2a4f53..12b783b1 100644 --- a/sound/soc/s3c24xx/smdk2443_wm9710.c +++ b/sound/soc/s3c24xx/smdk2443_wm9710.c @@ -20,7 +20,7 @@ #include #include "../codecs/ac97.h" -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-ac97.h" static struct snd_soc_card smdk2443; diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/s3c24xx/smdk64xx_wm8580.c index 216dd1e..efe4901 100644 --- a/sound/soc/s3c24xx/smdk64xx_wm8580.c +++ b/sound/soc/s3c24xx/smdk64xx_wm8580.c @@ -19,7 +19,7 @@ #include #include "../codecs/wm8580.h" -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c64xx-i2s.h" #define S3C64XX_I2S_V4 2 -- cgit v0.10.2 From 93f85130e1e9b03cded7bfe1383919f421e479b4 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Tue, 17 Nov 2009 16:53:38 +0900 Subject: ARM: S3C64XX: Define PCM Controller base registers Signed-off-by: Jassi Brar Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h index fc8b223..866be31 100644 --- a/arch/arm/mach-s3c6400/include/mach/map.h +++ b/arch/arm/mach-s3c6400/include/mach/map.h @@ -48,6 +48,8 @@ #define S3C64XX_PA_IIS1 (0x7F003000) #define S3C64XX_PA_TIMER (0x7F006000) #define S3C64XX_PA_IIC0 (0x7F004000) +#define S3C64XX_PA_PCM0 (0x7F009000) +#define S3C64XX_PA_PCM1 (0x7F00A000) #define S3C64XX_PA_IISV4 (0x7F00D000) #define S3C64XX_PA_IIC1 (0x7F00F000) -- cgit v0.10.2 From 07e74c0ac8f1fdb197e24bbbd5aadfa0c430a95c Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Tue, 17 Nov 2009 16:53:50 +0900 Subject: ARM: S3C64XX: Added platform data header Many SoCs have their I2S pins MUXed with other functions. So we need to pass a callback for driver to configure the pins appropriately. Hence, the need of platform data and this header. As and when needed new callbacks and structure pointers maybe added to this header. Signed-off-by: Jassi Brar Acked-by: Ben Dooks Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/arch/arm/plat-s3c/include/plat/audio.h b/arch/arm/plat-s3c/include/plat/audio.h new file mode 100644 index 0000000..f22d23b --- /dev/null +++ b/arch/arm/plat-s3c/include/plat/audio.h @@ -0,0 +1,17 @@ +/* arch/arm/plat-s3c/include/plat/audio.h + * + * Copyright (c) 2009 Samsung Electronics Co. Ltd + * Author: Jaswinder Singh + * + * 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. + */ + +/** + * struct s3c_audio_pdata - common platform data for audio device drivers + * @cfg_gpio: Callback function to setup mux'ed pins in I2S/PCM/AC97 mode + */ +struct s3c_audio_pdata { + int (*cfg_gpio)(struct platform_device *); +}; -- cgit v0.10.2 From acf1aef9ecb289f7ed42b25ed55463b2cbb48ce2 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Tue, 17 Nov 2009 16:53:56 +0900 Subject: ARM: S3C64XX: Defined PCM controller platform devices Signed-off-by: Jassi Brar Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/arch/arm/plat-s3c/include/plat/devs.h b/arch/arm/plat-s3c/include/plat/devs.h index 0f540ea..932cbbb 100644 --- a/arch/arm/plat-s3c/include/plat/devs.h +++ b/arch/arm/plat-s3c/include/plat/devs.h @@ -28,6 +28,9 @@ extern struct platform_device s3c64xx_device_iis0; extern struct platform_device s3c64xx_device_iis1; extern struct platform_device s3c64xx_device_iisv4; +extern struct platform_device s3c64xx_device_pcm0; +extern struct platform_device s3c64xx_device_pcm1; + extern struct platform_device s3c_device_fb; extern struct platform_device s3c_device_usb; extern struct platform_device s3c_device_lcd; diff --git a/arch/arm/plat-s3c64xx/dev-audio.c b/arch/arm/plat-s3c64xx/dev-audio.c index 1322beb..9e07344 100644 --- a/arch/arm/plat-s3c64xx/dev-audio.c +++ b/arch/arm/plat-s3c64xx/dev-audio.c @@ -15,9 +15,14 @@ #include #include +#include +#include #include - +#include +#include +#include +#include static struct resource s3c64xx_iis0_resource[] = { [0] = { @@ -66,3 +71,97 @@ struct platform_device s3c64xx_device_iisv4 = { .resource = s3c64xx_iisv4_resource, }; EXPORT_SYMBOL(s3c64xx_device_iisv4); + + +/* PCM Controller platform_devices */ + +static int s3c64xx_pcm_cfg_gpio(struct platform_device *pdev) +{ + switch (pdev->id) { + case 0: + s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_PCM0_SCLK); + s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_PCM0_EXTCLK); + s3c_gpio_cfgpin(S3C64XX_GPD(2), S3C64XX_GPD2_PCM0_FSYNC); + s3c_gpio_cfgpin(S3C64XX_GPD(3), S3C64XX_GPD3_PCM0_SIN); + s3c_gpio_cfgpin(S3C64XX_GPD(4), S3C64XX_GPD4_PCM0_SOUT); + break; + case 1: + s3c_gpio_cfgpin(S3C64XX_GPE(0), S3C64XX_GPE0_PCM1_SCLK); + s3c_gpio_cfgpin(S3C64XX_GPE(1), S3C64XX_GPE1_PCM1_EXTCLK); + s3c_gpio_cfgpin(S3C64XX_GPE(2), S3C64XX_GPE2_PCM1_FSYNC); + s3c_gpio_cfgpin(S3C64XX_GPE(3), S3C64XX_GPE3_PCM1_SIN); + s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_PCM1_SOUT); + break; + default: + printk(KERN_DEBUG "Invalid PCM Controller number!"); + return -EINVAL; + } + + return 0; +} + +static struct resource s3c64xx_pcm0_resource[] = { + [0] = { + .start = S3C64XX_PA_PCM0, + .end = S3C64XX_PA_PCM0 + 0x100 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = DMACH_PCM0_TX, + .end = DMACH_PCM0_TX, + .flags = IORESOURCE_DMA, + }, + [2] = { + .start = DMACH_PCM0_RX, + .end = DMACH_PCM0_RX, + .flags = IORESOURCE_DMA, + }, +}; + +struct s3c_audio_pdata s3c_pcm0_pdata = { + .cfg_gpio = s3c64xx_pcm_cfg_gpio, +}; + +struct platform_device s3c64xx_device_pcm0 = { + .name = "samsung-pcm", + .id = 0, + .num_resources = ARRAY_SIZE(s3c64xx_pcm0_resource), + .resource = s3c64xx_pcm0_resource, + .dev = { + .platform_data = &s3c_pcm0_pdata, + }, +}; +EXPORT_SYMBOL(s3c64xx_device_pcm0); + +static struct resource s3c64xx_pcm1_resource[] = { + [0] = { + .start = S3C64XX_PA_PCM1, + .end = S3C64XX_PA_PCM1 + 0x100 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = DMACH_PCM1_TX, + .end = DMACH_PCM1_TX, + .flags = IORESOURCE_DMA, + }, + [2] = { + .start = DMACH_PCM1_RX, + .end = DMACH_PCM1_RX, + .flags = IORESOURCE_DMA, + }, +}; + +struct s3c_audio_pdata s3c_pcm1_pdata = { + .cfg_gpio = s3c64xx_pcm_cfg_gpio, +}; + +struct platform_device s3c64xx_device_pcm1 = { + .name = "samsung-pcm", + .id = 1, + .num_resources = ARRAY_SIZE(s3c64xx_pcm1_resource), + .resource = s3c64xx_pcm1_resource, + .dev = { + .platform_data = &s3c_pcm1_pdata, + }, +}; +EXPORT_SYMBOL(s3c64xx_device_pcm1); -- cgit v0.10.2 From 357a1db94ecc5b3d605574b164d288cd7dbf8dbd Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Tue, 17 Nov 2009 16:54:03 +0900 Subject: ASoC: Added the CPU driver for PCM controllers Signed-off-by: Jassi Brar Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index d7912f1..b489f1a 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig @@ -24,6 +24,9 @@ config SND_S3C64XX_SOC_I2S select SND_S3C_I2SV2_SOC select S3C64XX_DMA +config SND_S3C_SOC_PCM + tristate + config SND_S3C2443_SOC_AC97 tristate select S3C2410_DMA diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index ff0a105..b744657 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile @@ -5,6 +5,7 @@ snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o snd-soc-s3c64xx-i2s-objs := s3c64xx-i2s.o snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o +snd-soc-s3c-pcm-objs := s3c-pcm.o obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o @@ -12,6 +13,7 @@ obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += snd-soc-s3c64xx-i2s.o obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o +obj-$(CONFIG_SND_S3C_SOC_PCM) += snd-soc-s3c-pcm.o # S3C24XX Machine Support snd-soc-jive-wm8750-objs := jive_wm8750.o diff --git a/sound/soc/s3c24xx/s3c-pcm.c b/sound/soc/s3c24xx/s3c-pcm.c new file mode 100644 index 0000000..9e61a7c --- /dev/null +++ b/sound/soc/s3c24xx/s3c-pcm.c @@ -0,0 +1,552 @@ +/* sound/soc/s3c24xx/s3c-pcm.c + * + * ALSA SoC Audio Layer - S3C PCM-Controller driver + * + * Copyright (c) 2009 Samsung Electronics Co. Ltd + * Author: Jaswinder Singh + * based upon I2S drivers by Ben Dooks. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "s3c-dma.h" +#include "s3c-pcm.h" + +static struct s3c2410_dma_client s3c_pcm_dma_client_out = { + .name = "PCM Stereo out" +}; + +static struct s3c2410_dma_client s3c_pcm_dma_client_in = { + .name = "PCM Stereo in" +}; + +static struct s3c_dma_params s3c_pcm_stereo_out[] = { + [0] = { + .client = &s3c_pcm_dma_client_out, + .dma_size = 4, + }, + [1] = { + .client = &s3c_pcm_dma_client_out, + .dma_size = 4, + }, +}; + +static struct s3c_dma_params s3c_pcm_stereo_in[] = { + [0] = { + .client = &s3c_pcm_dma_client_in, + .dma_size = 4, + }, + [1] = { + .client = &s3c_pcm_dma_client_in, + .dma_size = 4, + }, +}; + +static struct s3c_pcm_info s3c_pcm[2]; + +static inline struct s3c_pcm_info *to_info(struct snd_soc_dai *cpu_dai) +{ + return cpu_dai->private_data; +} + +static void s3c_pcm_snd_txctrl(struct s3c_pcm_info *pcm, int on) +{ + void __iomem *regs = pcm->regs; + u32 ctl, clkctl; + + clkctl = readl(regs + S3C_PCM_CLKCTL); + ctl = readl(regs + S3C_PCM_CTL); + ctl &= ~(S3C_PCM_CTL_TXDIPSTICK_MASK + << S3C_PCM_CTL_TXDIPSTICK_SHIFT); + + if (on) { + ctl |= S3C_PCM_CTL_TXDMA_EN; + ctl |= S3C_PCM_CTL_TXFIFO_EN; + ctl |= S3C_PCM_CTL_ENABLE; + ctl |= (0x20<idleclk) + clkctl |= S3C_PCM_CLKCTL_SERCLK_EN; + } + } + + writel(clkctl, regs + S3C_PCM_CLKCTL); + writel(ctl, regs + S3C_PCM_CTL); +} + +static void s3c_pcm_snd_rxctrl(struct s3c_pcm_info *pcm, int on) +{ + void __iomem *regs = pcm->regs; + u32 ctl, clkctl; + + ctl = readl(regs + S3C_PCM_CTL); + clkctl = readl(regs + S3C_PCM_CLKCTL); + + if (on) { + ctl |= S3C_PCM_CTL_RXDMA_EN; + ctl |= S3C_PCM_CTL_RXFIFO_EN; + ctl |= S3C_PCM_CTL_ENABLE; + clkctl |= S3C_PCM_CLKCTL_SERCLK_EN; + } else { + ctl &= ~S3C_PCM_CTL_RXDMA_EN; + ctl &= ~S3C_PCM_CTL_RXFIFO_EN; + + if (!(ctl & S3C_PCM_CTL_TXFIFO_EN)) { + ctl &= ~S3C_PCM_CTL_ENABLE; + if (!pcm->idleclk) + clkctl |= S3C_PCM_CLKCTL_SERCLK_EN; + } + } + + writel(clkctl, regs + S3C_PCM_CLKCTL); + writel(ctl, regs + S3C_PCM_CTL); +} + +static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct s3c_pcm_info *pcm = to_info(rtd->dai->cpu_dai); + unsigned long flags; + + dev_dbg(pcm->dev, "Entered %s\n", __func__); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + spin_lock_irqsave(&pcm->lock, flags); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + s3c_pcm_snd_rxctrl(pcm, 1); + else + s3c_pcm_snd_txctrl(pcm, 1); + + spin_unlock_irqrestore(&pcm->lock, flags); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + spin_lock_irqsave(&pcm->lock, flags); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + s3c_pcm_snd_rxctrl(pcm, 0); + else + s3c_pcm_snd_txctrl(pcm, 0); + + spin_unlock_irqrestore(&pcm->lock, flags); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int s3c_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *socdai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai_link *dai = rtd->dai; + struct s3c_pcm_info *pcm = to_info(dai->cpu_dai); + void __iomem *regs = pcm->regs; + struct clk *clk; + int sclk_div, sync_div; + unsigned long flags; + u32 clkctl; + + dev_dbg(pcm->dev, "Entered %s\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dai->cpu_dai->dma_data = pcm->dma_playback; + else + dai->cpu_dai->dma_data = pcm->dma_capture; + + /* Strictly check for sample size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&pcm->lock, flags); + + /* Get hold of the PCMSOURCE_CLK */ + clkctl = readl(regs + S3C_PCM_CLKCTL); + if (clkctl & S3C_PCM_CLKCTL_SERCLKSEL_PCLK) + clk = pcm->pclk; + else + clk = pcm->cclk; + + /* Set the SCLK divider */ + sclk_div = clk_get_rate(clk) / pcm->sclk_per_fs / + params_rate(params) / 2 - 1; + + clkctl &= ~(S3C_PCM_CLKCTL_SCLKDIV_MASK + << S3C_PCM_CLKCTL_SCLKDIV_SHIFT); + clkctl |= ((sclk_div & S3C_PCM_CLKCTL_SCLKDIV_MASK) + << S3C_PCM_CLKCTL_SCLKDIV_SHIFT); + + /* Set the SYNC divider */ + sync_div = pcm->sclk_per_fs - 1; + + clkctl &= ~(S3C_PCM_CLKCTL_SYNCDIV_MASK + << S3C_PCM_CLKCTL_SYNCDIV_SHIFT); + clkctl |= ((sync_div & S3C_PCM_CLKCTL_SYNCDIV_MASK) + << S3C_PCM_CLKCTL_SYNCDIV_SHIFT); + + writel(clkctl, regs + S3C_PCM_CLKCTL); + + spin_unlock_irqrestore(&pcm->lock, flags); + + dev_dbg(pcm->dev, "PCMSOURCE_CLK-%lu SCLK=%ufs \ + SCLK_DIV=%d SYNC_DIV=%d\n", + clk_get_rate(clk), pcm->sclk_per_fs, + sclk_div, sync_div); + + return 0; +} + +static int s3c_pcm_set_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct s3c_pcm_info *pcm = to_info(cpu_dai); + void __iomem *regs = pcm->regs; + unsigned long flags; + int ret = 0; + u32 ctl; + + dev_dbg(pcm->dev, "Entered %s\n", __func__); + + spin_lock_irqsave(&pcm->lock, flags); + + ctl = readl(regs + S3C_PCM_CTL); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + /* Nothing to do, NB_NF by default */ + break; + default: + dev_err(pcm->dev, "Unsupported clock inversion!\n"); + ret = -EINVAL; + goto exit; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* Nothing to do, Master by default */ + break; + default: + dev_err(pcm->dev, "Unsupported master/slave format!\n"); + ret = -EINVAL; + goto exit; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CONT: + pcm->idleclk = 1; + break; + case SND_SOC_DAIFMT_GATED: + pcm->idleclk = 0; + break; + default: + dev_err(pcm->dev, "Invalid Clock gating request!\n"); + ret = -EINVAL; + goto exit; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + ctl |= S3C_PCM_CTL_TXMSB_AFTER_FSYNC; + ctl |= S3C_PCM_CTL_RXMSB_AFTER_FSYNC; + break; + case SND_SOC_DAIFMT_DSP_B: + ctl &= ~S3C_PCM_CTL_TXMSB_AFTER_FSYNC; + ctl &= ~S3C_PCM_CTL_RXMSB_AFTER_FSYNC; + break; + default: + dev_err(pcm->dev, "Unsupported data format!\n"); + ret = -EINVAL; + goto exit; + } + + writel(ctl, regs + S3C_PCM_CTL); + +exit: + spin_unlock_irqrestore(&pcm->lock, flags); + + return ret; +} + +static int s3c_pcm_set_clkdiv(struct snd_soc_dai *cpu_dai, + int div_id, int div) +{ + struct s3c_pcm_info *pcm = to_info(cpu_dai); + + switch (div_id) { + case S3C_PCM_SCLK_PER_FS: + pcm->sclk_per_fs = div; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct s3c_pcm_info *pcm = to_info(cpu_dai); + void __iomem *regs = pcm->regs; + u32 clkctl = readl(regs + S3C_PCM_CLKCTL); + + switch (clk_id) { + case S3C_PCM_CLKSRC_PCLK: + clkctl |= S3C_PCM_CLKCTL_SERCLKSEL_PCLK; + break; + + case S3C_PCM_CLKSRC_MUX: + clkctl &= ~S3C_PCM_CLKCTL_SERCLKSEL_PCLK; + + if (clk_get_rate(pcm->cclk) != freq) + clk_set_rate(pcm->cclk, freq); + + break; + + default: + return -EINVAL; + } + + writel(clkctl, regs + S3C_PCM_CLKCTL); + + return 0; +} + +static struct snd_soc_dai_ops s3c_pcm_dai_ops = { + .set_sysclk = s3c_pcm_set_sysclk, + .set_clkdiv = s3c_pcm_set_clkdiv, + .trigger = s3c_pcm_trigger, + .hw_params = s3c_pcm_hw_params, + .set_fmt = s3c_pcm_set_fmt, +}; + +#define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000 + +#define S3C_PCM_DECLARE(n) \ +{ \ + .name = "samsung-pcm", \ + .id = (n), \ + .symmetric_rates = 1, \ + .ops = &s3c_pcm_dai_ops, \ + .playback = { \ + .channels_min = 2, \ + .channels_max = 2, \ + .rates = S3C_PCM_RATES, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .capture = { \ + .channels_min = 2, \ + .channels_max = 2, \ + .rates = S3C_PCM_RATES, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ +} + +struct snd_soc_dai s3c_pcm_dai[] = { + S3C_PCM_DECLARE(0), + S3C_PCM_DECLARE(1), +}; +EXPORT_SYMBOL_GPL(s3c_pcm_dai); + +static __devinit int s3c_pcm_dev_probe(struct platform_device *pdev) +{ + struct s3c_pcm_info *pcm; + struct snd_soc_dai *dai; + struct resource *mem_res, *dmatx_res, *dmarx_res; + struct s3c_audio_pdata *pcm_pdata; + int ret; + + /* Check for valid device index */ + if ((pdev->id < 0) || pdev->id >= ARRAY_SIZE(s3c_pcm)) { + dev_err(&pdev->dev, "id %d out of range\n", pdev->id); + return -EINVAL; + } + + pcm_pdata = pdev->dev.platform_data; + + /* Check for availability of necessary resource */ + dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!dmatx_res) { + dev_err(&pdev->dev, "Unable to get PCM-TX dma resource\n"); + return -ENXIO; + } + + dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!dmarx_res) { + dev_err(&pdev->dev, "Unable to get PCM-RX dma resource\n"); + return -ENXIO; + } + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + dev_err(&pdev->dev, "Unable to get register resource\n"); + return -ENXIO; + } + + if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) { + dev_err(&pdev->dev, "Unable to configure gpio\n"); + return -EINVAL; + } + + pcm = &s3c_pcm[pdev->id]; + pcm->dev = &pdev->dev; + + spin_lock_init(&pcm->lock); + + dai = &s3c_pcm_dai[pdev->id]; + dai->dev = &pdev->dev; + + /* Default is 128fs */ + pcm->sclk_per_fs = 128; + + pcm->cclk = clk_get(&pdev->dev, "audio-bus"); + if (IS_ERR(pcm->cclk)) { + dev_err(&pdev->dev, "failed to get audio-bus\n"); + ret = PTR_ERR(pcm->cclk); + goto err1; + } + clk_enable(pcm->cclk); + + /* record our pcm structure for later use in the callbacks */ + dai->private_data = pcm; + + if (!request_mem_region(mem_res->start, + resource_size(mem_res), "samsung-pcm")) { + dev_err(&pdev->dev, "Unable to request register region\n"); + ret = -EBUSY; + goto err2; + } + + pcm->regs = ioremap(mem_res->start, 0x100); + if (pcm->regs == NULL) { + dev_err(&pdev->dev, "cannot ioremap registers\n"); + ret = -ENXIO; + goto err3; + } + + pcm->pclk = clk_get(&pdev->dev, "pcm"); + if (IS_ERR(pcm->pclk)) { + dev_err(&pdev->dev, "failed to get pcm_clock\n"); + ret = -ENOENT; + goto err4; + } + clk_enable(pcm->pclk); + + ret = snd_soc_register_dai(dai); + if (ret != 0) { + dev_err(&pdev->dev, "failed to get pcm_clock\n"); + goto err5; + } + + s3c_pcm_stereo_in[pdev->id].dma_addr = mem_res->start + + S3C_PCM_RXFIFO; + s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start + + S3C_PCM_TXFIFO; + + s3c_pcm_stereo_in[pdev->id].channel = dmarx_res->start; + s3c_pcm_stereo_out[pdev->id].channel = dmatx_res->start; + + pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id]; + pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id]; + + return 0; + +err5: + clk_disable(pcm->pclk); + clk_put(pcm->pclk); +err4: + iounmap(pcm->regs); +err3: + release_mem_region(mem_res->start, resource_size(mem_res)); +err2: + clk_disable(pcm->cclk); + clk_put(pcm->cclk); +err1: + return ret; +} + +static __devexit int s3c_pcm_dev_remove(struct platform_device *pdev) +{ + struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id]; + struct resource *mem_res; + + iounmap(pcm->regs); + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem_res->start, resource_size(mem_res)); + + clk_disable(pcm->cclk); + clk_disable(pcm->pclk); + clk_put(pcm->pclk); + clk_put(pcm->cclk); + + return 0; +} + +static struct platform_driver s3c_pcm_driver = { + .probe = s3c_pcm_dev_probe, + .remove = s3c_pcm_dev_remove, + .driver = { + .name = "samsung-pcm", + .owner = THIS_MODULE, + }, +}; + +static int __init s3c_pcm_init(void) +{ + return platform_driver_register(&s3c_pcm_driver); +} +module_init(s3c_pcm_init); + +static void __exit s3c_pcm_exit(void) +{ + platform_driver_unregister(&s3c_pcm_driver); +} +module_exit(s3c_pcm_exit); + +/* Module information */ +MODULE_AUTHOR("Jaswinder Singh, "); +MODULE_DESCRIPTION("S3C PCM Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/s3c-pcm.h b/sound/soc/s3c24xx/s3c-pcm.h new file mode 100644 index 0000000..69ff997 --- /dev/null +++ b/sound/soc/s3c24xx/s3c-pcm.h @@ -0,0 +1,123 @@ +/* sound/soc/s3c24xx/s3c-pcm.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 __S3C_PCM_H +#define __S3C_PCM_H __FILE__ + +/*Register Offsets */ +#define S3C_PCM_CTL (0x00) +#define S3C_PCM_CLKCTL (0x04) +#define S3C_PCM_TXFIFO (0x08) +#define S3C_PCM_RXFIFO (0x0C) +#define S3C_PCM_IRQCTL (0x10) +#define S3C_PCM_IRQSTAT (0x14) +#define S3C_PCM_FIFOSTAT (0x18) +#define S3C_PCM_CLRINT (0x20) + +/* PCM_CTL Bit-Fields */ +#define S3C_PCM_CTL_TXDIPSTICK_MASK (0x3f) +#define S3C_PCM_CTL_TXDIPSTICK_SHIFT (13) +#define S3C_PCM_CTL_RXDIPSTICK_MSK (0x3f<<7) +#define S3C_PCM_CTL_TXDMA_EN (0x1<<6) +#define S3C_PCM_CTL_RXDMA_EN (0x1<<5) +#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC (0x1<<4) +#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC (0x1<<3) +#define S3C_PCM_CTL_TXFIFO_EN (0x1<<2) +#define S3C_PCM_CTL_RXFIFO_EN (0x1<<1) +#define S3C_PCM_CTL_ENABLE (0x1<<0) + +/* PCM_CLKCTL Bit-Fields */ +#define S3C_PCM_CLKCTL_SERCLK_EN (0x1<<19) +#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK (0x1<<18) +#define S3C_PCM_CLKCTL_SCLKDIV_MASK (0x1ff) +#define S3C_PCM_CLKCTL_SYNCDIV_MASK (0x1ff) +#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT (9) +#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT (0) + +/* PCM_TXFIFO Bit-Fields */ +#define S3C_PCM_TXFIFO_DVALID (0x1<<16) +#define S3C_PCM_TXFIFO_DATA_MSK (0xffff<<0) + +/* PCM_RXFIFO Bit-Fields */ +#define S3C_PCM_RXFIFO_DVALID (0x1<<16) +#define S3C_PCM_RXFIFO_DATA_MSK (0xffff<<0) + +/* PCM_IRQCTL Bit-Fields */ +#define S3C_PCM_IRQCTL_IRQEN (0x1<<14) +#define S3C_PCM_IRQCTL_WRDEN (0x1<<12) +#define S3C_PCM_IRQCTL_TXEMPTYEN (0x1<<11) +#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN (0x1<<10) +#define S3C_PCM_IRQCTL_TXFULLEN (0x1<<9) +#define S3C_PCM_IRQCTL_TXALMSTFULLEN (0x1<<8) +#define S3C_PCM_IRQCTL_TXSTARVEN (0x1<<7) +#define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1<<6) +#define S3C_PCM_IRQCTL_RXEMPTEN (0x1<<5) +#define S3C_PCM_IRQCTL_RXALMSTEMPTEN (0x1<<4) +#define S3C_PCM_IRQCTL_RXFULLEN (0x1<<3) +#define S3C_PCM_IRQCTL_RXALMSTFULLEN (0x1<<2) +#define S3C_PCM_IRQCTL_RXSTARVEN (0x1<<1) +#define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1<<0) + +/* PCM_IRQSTAT Bit-Fields */ +#define S3C_PCM_IRQSTAT_IRQPND (0x1<<13) +#define S3C_PCM_IRQSTAT_WRD_XFER (0x1<<12) +#define S3C_PCM_IRQSTAT_TXEMPTY (0x1<<11) +#define S3C_PCM_IRQSTAT_TXALMSTEMPTY (0x1<<10) +#define S3C_PCM_IRQSTAT_TXFULL (0x1<<9) +#define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1<<8) +#define S3C_PCM_IRQSTAT_TXSTARV (0x1<<7) +#define S3C_PCM_IRQSTAT_TXERROVRFL (0x1<<6) +#define S3C_PCM_IRQSTAT_RXEMPT (0x1<<5) +#define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1<<4) +#define S3C_PCM_IRQSTAT_RXFULL (0x1<<3) +#define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1<<2) +#define S3C_PCM_IRQSTAT_RXSTARV (0x1<<1) +#define S3C_PCM_IRQSTAT_RXERROVRFL (0x1<<0) + +/* PCM_FIFOSTAT Bit-Fields */ +#define S3C_PCM_FIFOSTAT_TXCNT_MSK (0x3f<<14) +#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY (0x1<<13) +#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY (0x1<<12) +#define S3C_PCM_FIFOSTAT_TXFIFOFULL (0x1<<11) +#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL (0x1<<10) +#define S3C_PCM_FIFOSTAT_RXCNT_MSK (0x3f<<4) +#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY (0x1<<3) +#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY (0x1<<2) +#define S3C_PCM_FIFOSTAT_RXFIFOFULL (0x1<<1) +#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL (0x1<<0) + +#define S3C_PCM_CLKSRC_PCLK 0 +#define S3C_PCM_CLKSRC_MUX 1 + +#define S3C_PCM_SCLK_PER_FS 0 + +/** + * struct s3c_pcm_info - S3C PCM Controller information + * @dev: The parent device passed to use from the probe. + * @regs: The pointer to the device register block. + * @dma_playback: DMA information for playback channel. + * @dma_capture: DMA information for capture channel. + */ +struct s3c_pcm_info { + spinlock_t lock; + struct device *dev; + void __iomem *regs; + + unsigned int sclk_per_fs; + + /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */ + unsigned int idleclk; + + struct clk *pclk; + struct clk *cclk; + + struct s3c_dma_params *dma_playback; + struct s3c_dma_params *dma_capture; +}; + +#endif /* __S3C_PCM_H */ -- cgit v0.10.2 From 57512c6432783c9695ef54f875f705584c65c733 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Mon, 16 Nov 2009 16:52:31 -0700 Subject: ASoC: DaVinci: remove requirement that dma_params is 1st in structure Remove requirement that dma_params is 1st in the structures davinci_audio_dev and davinci_mcbsp_dev. Signed-off-by: Troy Kisky Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 2ab8093..d336786 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -98,11 +98,6 @@ enum { }; struct davinci_mcbsp_dev { - /* - * dma_params must be first because rtd->dai->cpu_dai->private_data - * is cast to a pointer of an array of struct davinci_pcm_dma_params in - * davinci_pcm_open. - */ struct davinci_pcm_dma_params dma_params[2]; void __iomem *base; #define MOD_DSP_A 0 @@ -549,6 +544,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; davinci_i2s_dai.private_data = dev; + davinci_i2s_dai.dma_data = dev->dma_params; ret = snd_soc_register_dai(&davinci_i2s_dai); if (ret != 0) goto err_free_mem; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 50ad051..0a302e1 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -904,6 +904,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->channel = res->start; davinci_mcasp_dai[pdata->op_mode].private_data = dev; + davinci_mcasp_dai[pdata->op_mode].dma_data = dev->dma_params; davinci_mcasp_dai[pdata->op_mode].dev = &pdev->dev; ret = snd_soc_register_dai(&davinci_mcasp_dai[pdata->op_mode]); diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h index 9d179cc..582c924 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/davinci/davinci-mcasp.h @@ -39,11 +39,6 @@ enum { }; struct davinci_audio_dev { - /* - * dma_params must be first because rtd->dai->cpu_dai->private_data - * is cast to a pointer of an array of struct davinci_pcm_dma_params in - * davinci_pcm_open. - */ struct davinci_pcm_dma_params dma_params[2]; void __iomem *base; int sample_rate; diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index fb10f1d..187ee96 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -253,10 +253,11 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream) struct davinci_runtime_data *prtd; int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct davinci_pcm_dma_params *pa = rtd->dai->cpu_dai->private_data; - struct davinci_pcm_dma_params *params = &pa[substream->stream]; - if (!params) + struct davinci_pcm_dma_params *pa = rtd->dai->cpu_dai->dma_data; + struct davinci_pcm_dma_params *params; + if (!pa) return -ENODEV; + params = &pa[substream->stream]; snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware); /* ensure that buffer size is a multiple of period size */ -- cgit v0.10.2 From 0d6c97742993a00ee2cbfbd6d68fba669c17bf50 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 18 Nov 2009 17:49:51 -0700 Subject: ASoC: DaVinci: i2s, reduce underruns by combining into 1 element Allow the left and right 16 bit samples to be shifted out as 1 32 bit sample. Signed-off-by: Troy Kisky Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/arch/arm/mach-davinci/include/mach/asp.h b/arch/arm/mach-davinci/include/mach/asp.h index 18e4ce3..019c647 100644 --- a/arch/arm/mach-davinci/include/mach/asp.h +++ b/arch/arm/mach-davinci/include/mach/asp.h @@ -51,6 +51,12 @@ struct snd_platform_data { u32 rx_dma_offset; enum dma_event_q eventq_no; /* event queue number */ unsigned int codec_fmt; + /* + * Allowing this is more efficient and eliminates left and right swaps + * caused by underruns, but will swap the left and right channels + * when compared to previous behavior. + */ + unsigned enable_channel_combine:1; /* McASP specific fields */ int tdm_slots; diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index d336786..b2a5372 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -97,6 +97,23 @@ enum { DAVINCI_MCBSP_WORD_32, }; +static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = { + [SNDRV_PCM_FORMAT_S8] = 1, + [SNDRV_PCM_FORMAT_S16_LE] = 2, + [SNDRV_PCM_FORMAT_S32_LE] = 4, +}; + +static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = { + [SNDRV_PCM_FORMAT_S8] = DAVINCI_MCBSP_WORD_8, + [SNDRV_PCM_FORMAT_S16_LE] = DAVINCI_MCBSP_WORD_16, + [SNDRV_PCM_FORMAT_S32_LE] = DAVINCI_MCBSP_WORD_32, +}; + +static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = { + [SNDRV_PCM_FORMAT_S8] = SNDRV_PCM_FORMAT_S16_LE, + [SNDRV_PCM_FORMAT_S16_LE] = SNDRV_PCM_FORMAT_S32_LE, +}; + struct davinci_mcbsp_dev { struct davinci_pcm_dma_params dma_params[2]; void __iomem *base; @@ -105,6 +122,27 @@ struct davinci_mcbsp_dev { int mode; u32 pcr; struct clk *clk; + /* + * Combining both channels into 1 element will at least double the + * amount of time between servicing the dma channel, increase + * effiency, and reduce the chance of overrun/underrun. But, + * it will result in the left & right channels being swapped. + * + * If relabeling the left and right channels is not possible, + * you may want to let the codec know to swap them back. + * + * It may allow x10 the amount of time to service dma requests, + * if the codec is master and is using an unnecessarily fast bit clock + * (ie. tlvaic23b), independent of the sample rate. So, having an + * entire frame at once means it can be serviced at the sample rate + * instead of the bit clock rate. + * + * In the now unlikely case that an underrun still + * occurs, both the left and right samples will be repeated + * so that no pops are heard, and the left and right channels + * won't end up being swapped because of the underrun. + */ + unsigned enable_channel_combine:1; }; static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev, @@ -344,6 +382,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, int mcbsp_word_length; unsigned int rcr, xcr, srgr; u32 spcr; + snd_pcm_format_t fmt; + unsigned element_cnt = 1; /* general line settings */ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); @@ -373,29 +413,24 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1); } /* Determine xfer data type */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S8: - dma_params->data_type = 1; - mcbsp_word_length = DAVINCI_MCBSP_WORD_8; - break; - case SNDRV_PCM_FORMAT_S16_LE: - dma_params->data_type = 2; - mcbsp_word_length = DAVINCI_MCBSP_WORD_16; - break; - case SNDRV_PCM_FORMAT_S32_LE: - dma_params->data_type = 4; - mcbsp_word_length = DAVINCI_MCBSP_WORD_32; - break; - default: + fmt = params_format(params); + if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) { printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n"); return -EINVAL; } - dma_params->acnt = dma_params->data_type; + if (params_channels(params) == 2) { + element_cnt = 2; + if (double_fmt[fmt] && dev->enable_channel_combine) { + element_cnt = 1; + fmt = double_fmt[fmt]; + } + } + dma_params->acnt = dma_params->data_type = data_type[fmt]; dma_params->fifo_level = 0; - - rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1); - xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1); + mcbsp_word_length = asp_word_length[fmt]; + rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1); + xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1); rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length); @@ -510,7 +545,8 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_release_region; } - + if (pdata) + dev->enable_channel_combine = pdata->enable_channel_combine; dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) { ret = -ENODEV; -- cgit v0.10.2 From 1587ea31572e25a0a2c9c491b7f8c937b6c0454e Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 18 Nov 2009 17:49:52 -0700 Subject: ASoC: DaVinci: pcm, rename variables in prep for ping/pong Rename variable master_lch to asp_channel Rename variable slave_lch to asp_link[0] Rename local variables: lch to link count to asp_count src to asp_src dst to asp_dst Signed-off-by: Troy Kisky Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 187ee96..42a657e 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -51,8 +51,8 @@ static struct snd_pcm_hardware davinci_pcm_hardware = { struct davinci_runtime_data { spinlock_t lock; int period; /* current DMA period */ - int master_lch; /* Master DMA channel */ - int slave_lch; /* linked parameter RAM reload slot */ + int asp_channel; /* Master DMA channel */ + int asp_link[2]; /* asp parameter link channel, ping/pong */ struct davinci_pcm_dma_params *params; /* DMA params */ }; @@ -60,7 +60,7 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) { struct davinci_runtime_data *prtd = substream->runtime->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - int lch = prtd->slave_lch; + int link = prtd->asp_link[0]; unsigned int period_size; unsigned int dma_offset; dma_addr_t dma_pos; @@ -78,7 +78,7 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) fifo_level = prtd->params->fifo_level; pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d " - "dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size); + "dma_ptr = %x period_size=%x\n", link, dma_pos, period_size); data_type = prtd->params->data_type; count = period_size / data_type; @@ -102,16 +102,16 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) } acnt = prtd->params->acnt; - edma_set_src(lch, src, INCR, W8BIT); - edma_set_dest(lch, dst, INCR, W8BIT); + edma_set_src(link, src, INCR, W8BIT); + edma_set_dest(link, dst, INCR, W8BIT); - edma_set_src_index(lch, src_bidx, src_cidx); - edma_set_dest_index(lch, dst_bidx, dst_cidx); + edma_set_src_index(link, src_bidx, src_cidx); + edma_set_dest_index(link, dst_bidx, dst_cidx); if (!fifo_level) - edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC); + edma_set_transfer_params(link, acnt, count, 1, 0, ASYNC); else - edma_set_transfer_params(lch, acnt, fifo_level, count, + edma_set_transfer_params(link, acnt, fifo_level, count, fifo_level, ABSYNC); prtd->period++; @@ -119,12 +119,12 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) prtd->period = 0; } -static void davinci_pcm_dma_irq(unsigned lch, u16 ch_status, void *data) +static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) { struct snd_pcm_substream *substream = data; struct davinci_runtime_data *prtd = substream->runtime->private_data; - pr_debug("davinci_pcm: lch=%d, status=0x%x\n", lch, ch_status); + pr_debug("davinci_pcm: link=%d, status=0x%x\n", link, ch_status); if (unlikely(ch_status != DMA_COMPLETE)) return; @@ -150,15 +150,15 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) EVENTQ_0); if (ret < 0) return ret; - prtd->master_lch = ret; + prtd->asp_channel = ret; /* Request parameter RAM reload slot */ - ret = edma_alloc_slot(EDMA_CTLR(prtd->master_lch), EDMA_SLOT_ANY); + ret = edma_alloc_slot(EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY); if (ret < 0) { - edma_free_channel(prtd->master_lch); + edma_free_channel(prtd->asp_channel); return ret; } - prtd->slave_lch = ret; + prtd->asp_link[0] = ret; /* Issue transfer completion IRQ when the channel completes a * transfer, then always reload from the same slot (by a kind @@ -169,10 +169,10 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) * the buffer and its length (ccnt) ... use it as a template * so davinci_pcm_enqueue_dma() takes less time in IRQ. */ - edma_read_slot(prtd->slave_lch, &p_ram); - p_ram.opt |= TCINTEN | EDMA_TCC(EDMA_CHAN_SLOT(prtd->master_lch)); - p_ram.link_bcntrld = EDMA_CHAN_SLOT(prtd->slave_lch) << 5; - edma_write_slot(prtd->slave_lch, &p_ram); + edma_read_slot(prtd->asp_link[0], &p_ram); + p_ram.opt |= TCINTEN | EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel)); + p_ram.link_bcntrld = EDMA_CHAN_SLOT(prtd->asp_link[0]) << 5; + edma_write_slot(prtd->asp_link[0], &p_ram); return 0; } @@ -188,12 +188,12 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - edma_start(prtd->master_lch); + edma_start(prtd->asp_channel); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - edma_stop(prtd->master_lch); + edma_stop(prtd->asp_channel); break; default: ret = -EINVAL; @@ -214,8 +214,8 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) davinci_pcm_enqueue_dma(substream); /* Copy self-linked parameter RAM entry into master channel */ - edma_read_slot(prtd->slave_lch, &temp); - edma_write_slot(prtd->master_lch, &temp); + edma_read_slot(prtd->asp_link[0], &temp); + edma_write_slot(prtd->asp_channel, &temp); davinci_pcm_enqueue_dma(substream); return 0; @@ -227,20 +227,20 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct davinci_runtime_data *prtd = runtime->private_data; unsigned int offset; - dma_addr_t count; - dma_addr_t src, dst; + int asp_count; + dma_addr_t asp_src, asp_dst; spin_lock(&prtd->lock); - edma_get_position(prtd->master_lch, &src, &dst); + edma_get_position(prtd->asp_channel, &asp_src, &asp_dst); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - count = src - runtime->dma_addr; + asp_count = asp_src - runtime->dma_addr; else - count = dst - runtime->dma_addr; + asp_count = asp_dst - runtime->dma_addr; spin_unlock(&prtd->lock); - offset = bytes_to_frames(runtime, count); + offset = bytes_to_frames(runtime, asp_count); if (offset >= runtime->buffer_size) offset = 0; @@ -289,10 +289,10 @@ static int davinci_pcm_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct davinci_runtime_data *prtd = runtime->private_data; - edma_unlink(prtd->slave_lch); + edma_unlink(prtd->asp_link[0]); - edma_free_slot(prtd->slave_lch); - edma_free_channel(prtd->master_lch); + edma_free_slot(prtd->asp_link[0]); + edma_free_channel(prtd->asp_channel); kfree(prtd); -- cgit v0.10.2 From 1e224f322bf22280957a5f76164d848526ed9b08 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 18 Nov 2009 17:49:53 -0700 Subject: ASoC: DaVinci: pcm, fix underrun by using sram Fix underruns by using dma to copy 1st to sram in a ping/pong buffer style and then copying from the sram to the ASP. This also has the advantage of tolerating very long interrupt latency on dma completion. Signed-off-by: Troy Kisky Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/arch/arm/mach-davinci/include/mach/asp.h b/arch/arm/mach-davinci/include/mach/asp.h index 019c647..e07f70e 100644 --- a/arch/arm/mach-davinci/include/mach/asp.h +++ b/arch/arm/mach-davinci/include/mach/asp.h @@ -57,6 +57,8 @@ struct snd_platform_data { * when compared to previous behavior. */ unsigned enable_channel_combine:1; + unsigned sram_size_playback; + unsigned sram_size_capture; /* McASP specific fields */ int tdm_slots; diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index b2a5372..6362ca0 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -545,8 +545,13 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_release_region; } - if (pdata) + if (pdata) { dev->enable_channel_combine = pdata->enable_channel_combine; + dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].sram_size = + pdata->sram_size_playback; + dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size = + pdata->sram_size_capture; + } dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) { ret = -ENODEV; diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 42a657e..664d493 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -3,6 +3,7 @@ * * Author: Vladimir Barinov, * Copyright: (C) 2007 MontaVista Software, Inc., + * added SRAM ping/pong (C) 2008 Troy Kisky * * 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 @@ -23,10 +24,29 @@ #include #include +#include #include "davinci-pcm.h" -static struct snd_pcm_hardware davinci_pcm_hardware = { +#ifdef DEBUG +static void print_buf_info(int slot, char *name) +{ + struct edmacc_param p; + if (slot < 0) + return; + edma_read_slot(slot, &p); + printk(KERN_DEBUG "%s: 0x%x, opt=%x, src=%x, a_b_cnt=%x dst=%x\n", + name, slot, p.opt, p.src, p.a_b_cnt, p.dst); + printk(KERN_DEBUG " src_dst_bidx=%x link_bcntrld=%x src_dst_cidx=%x ccnt=%x\n", + p.src_dst_bidx, p.link_bcntrld, p.src_dst_cidx, p.ccnt); +} +#else +static void print_buf_info(int slot, char *name) +{ +} +#endif + +static struct snd_pcm_hardware pcm_hardware_playback = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), @@ -48,14 +68,80 @@ static struct snd_pcm_hardware davinci_pcm_hardware = { .fifo_size = 0, }; +static struct snd_pcm_hardware pcm_hardware_capture = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = (SNDRV_PCM_RATE_8000 | 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 | + SNDRV_PCM_RATE_KNOT), + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8 * 1024, + .periods_min = 16, + .periods_max = 255, + .fifo_size = 0, +}; + +/* + * How ping/pong works.... + * + * Playback: + * ram_params - copys 2*ping_size from start of SDRAM to iram, + * links to ram_link2 + * ram_link2 - copys rest of SDRAM to iram in ping_size units, + * links to ram_link + * ram_link - copys entire SDRAM to iram in ping_size uints, + * links to self + * + * asp_params - same as asp_link[0] + * asp_link[0] - copys from lower half of iram to asp port + * links to asp_link[1], triggers iram copy event on completion + * asp_link[1] - copys from upper half of iram to asp port + * links to asp_link[0], triggers iram copy event on completion + * triggers interrupt only needed to let upper SOC levels update position + * in stream on completion + * + * When playback is started: + * ram_params started + * asp_params started + * + * Capture: + * ram_params - same as ram_link, + * links to ram_link + * ram_link - same as playback + * links to self + * + * asp_params - same as playback + * asp_link[0] - same as playback + * asp_link[1] - same as playback + * + * When capture is started: + * asp_params started + */ struct davinci_runtime_data { spinlock_t lock; int period; /* current DMA period */ int asp_channel; /* Master DMA channel */ int asp_link[2]; /* asp parameter link channel, ping/pong */ struct davinci_pcm_dma_params *params; /* DMA params */ + int ram_channel; + int ram_link; + int ram_link2; + struct edmacc_param asp_params; + struct edmacc_param ram_params; }; +/* + * Not used with ping/pong + */ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) { struct davinci_runtime_data *prtd = substream->runtime->private_data; @@ -124,41 +210,290 @@ static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) struct snd_pcm_substream *substream = data; struct davinci_runtime_data *prtd = substream->runtime->private_data; + print_buf_info(prtd->ram_channel, "i ram_channel"); pr_debug("davinci_pcm: link=%d, status=0x%x\n", link, ch_status); if (unlikely(ch_status != DMA_COMPLETE)) return; if (snd_pcm_running(substream)) { + if (prtd->ram_channel < 0) { + /* No ping/pong must fix up link dma data*/ + spin_lock(&prtd->lock); + davinci_pcm_enqueue_dma(substream); + spin_unlock(&prtd->lock); + } snd_pcm_period_elapsed(substream); + } +} + +static int allocate_sram(struct snd_pcm_substream *substream, unsigned size, + struct snd_pcm_hardware *ppcm) +{ + struct snd_dma_buffer *buf = &substream->dma_buffer; + struct snd_dma_buffer *iram_dma = NULL; + dma_addr_t iram_phys = 0; + void *iram_virt = NULL; + + if (buf->private_data || !size) + return 0; + + ppcm->period_bytes_max = size; + iram_virt = sram_alloc(size, &iram_phys); + if (!iram_virt) + goto exit1; + iram_dma = kzalloc(sizeof(*iram_dma), GFP_KERNEL); + if (!iram_dma) + goto exit2; + iram_dma->area = iram_virt; + iram_dma->addr = iram_phys; + memset(iram_dma->area, 0, size); + iram_dma->bytes = size; + buf->private_data = iram_dma; + return 0; +exit2: + if (iram_virt) + sram_free(iram_virt, size); +exit1: + return -ENOMEM; +} - spin_lock(&prtd->lock); - davinci_pcm_enqueue_dma(substream); - spin_unlock(&prtd->lock); +/* + * Only used with ping/pong. + * This is called after runtime->dma_addr, period_bytes and data_type are valid + */ +static int ping_pong_dma_setup(struct snd_pcm_substream *substream) +{ + unsigned short ram_src_cidx, ram_dst_cidx; + struct snd_pcm_runtime *runtime = substream->runtime; + struct davinci_runtime_data *prtd = runtime->private_data; + struct snd_dma_buffer *iram_dma = + (struct snd_dma_buffer *)substream->dma_buffer.private_data; + struct davinci_pcm_dma_params *params = prtd->params; + unsigned int data_type = params->data_type; + unsigned int acnt = params->acnt; + /* divide by 2 for ping/pong */ + unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1; + int link = prtd->asp_link[1]; + unsigned int fifo_level = prtd->params->fifo_level; + unsigned int count; + if ((data_type == 0) || (data_type > 4)) { + printk(KERN_ERR "%s: data_type=%i\n", __func__, data_type); + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dma_addr_t asp_src_pong = iram_dma->addr + ping_size; + ram_src_cidx = ping_size; + ram_dst_cidx = -ping_size; + edma_set_src(link, asp_src_pong, INCR, W8BIT); + + link = prtd->asp_link[0]; + edma_set_src_index(link, data_type, data_type * fifo_level); + link = prtd->asp_link[1]; + edma_set_src_index(link, data_type, data_type * fifo_level); + + link = prtd->ram_link; + edma_set_src(link, runtime->dma_addr, INCR, W32BIT); + } else { + dma_addr_t asp_dst_pong = iram_dma->addr + ping_size; + ram_src_cidx = -ping_size; + ram_dst_cidx = ping_size; + edma_set_dest(link, asp_dst_pong, INCR, W8BIT); + + link = prtd->asp_link[0]; + edma_set_dest_index(link, data_type, data_type * fifo_level); + link = prtd->asp_link[1]; + edma_set_dest_index(link, data_type, data_type * fifo_level); + + link = prtd->ram_link; + edma_set_dest(link, runtime->dma_addr, INCR, W32BIT); + } + + if (!fifo_level) { + count = ping_size / data_type; + edma_set_transfer_params(prtd->asp_link[0], acnt, count, + 1, 0, ASYNC); + edma_set_transfer_params(prtd->asp_link[1], acnt, count, + 1, 0, ASYNC); + } else { + count = ping_size / (data_type * fifo_level); + edma_set_transfer_params(prtd->asp_link[0], acnt, fifo_level, + count, fifo_level, ABSYNC); + edma_set_transfer_params(prtd->asp_link[1], acnt, fifo_level, + count, fifo_level, ABSYNC); + } + + link = prtd->ram_link; + edma_set_src_index(link, ping_size, ram_src_cidx); + edma_set_dest_index(link, ping_size, ram_dst_cidx); + edma_set_transfer_params(link, ping_size, 2, + runtime->periods, 2, ASYNC); + + /* init master params */ + edma_read_slot(prtd->asp_link[0], &prtd->asp_params); + edma_read_slot(prtd->ram_link, &prtd->ram_params); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + struct edmacc_param p_ram; + /* Copy entire iram buffer before playback started */ + prtd->ram_params.a_b_cnt = (1 << 16) | (ping_size << 1); + /* 0 dst_bidx */ + prtd->ram_params.src_dst_bidx = (ping_size << 1); + /* 0 dst_cidx */ + prtd->ram_params.src_dst_cidx = (ping_size << 1); + prtd->ram_params.ccnt = 1; + + /* Skip 1st period */ + edma_read_slot(prtd->ram_link, &p_ram); + p_ram.src += (ping_size << 1); + p_ram.ccnt -= 1; + edma_write_slot(prtd->ram_link2, &p_ram); + /* + * When 1st started, ram -> iram dma channel will fill the + * entire iram. Then, whenever a ping/pong asp buffer finishes, + * 1/2 iram will be filled. + */ + prtd->ram_params.link_bcntrld = + EDMA_CHAN_SLOT(prtd->ram_link2) << 5; + } + return 0; +} + +/* 1 asp tx or rx channel using 2 parameter channels + * 1 ram to/from iram channel using 1 parameter channel + * + * Playback + * ram copy channel kicks off first, + * 1st ram copy of entire iram buffer completion kicks off asp channel + * asp tcc always kicks off ram copy of 1/2 iram buffer + * + * Record + * asp channel starts, tcc kicks off ram copy + */ +static int request_ping_pong(struct snd_pcm_substream *substream, + struct davinci_runtime_data *prtd, + struct snd_dma_buffer *iram_dma) +{ + dma_addr_t asp_src_ping; + dma_addr_t asp_dst_ping; + int link; + struct davinci_pcm_dma_params *params = prtd->params; + + /* Request ram master channel */ + link = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY, + davinci_pcm_dma_irq, substream, + EVENTQ_1); + if (link < 0) + goto exit1; + + /* Request ram link channel */ + link = prtd->ram_link = edma_alloc_slot( + EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY); + if (link < 0) + goto exit2; + + link = prtd->asp_link[1] = edma_alloc_slot( + EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY); + if (link < 0) + goto exit3; + + prtd->ram_link2 = -1; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + link = prtd->ram_link2 = edma_alloc_slot( + EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY); + if (link < 0) + goto exit4; + } + /* circle ping-pong buffers */ + edma_link(prtd->asp_link[0], prtd->asp_link[1]); + edma_link(prtd->asp_link[1], prtd->asp_link[0]); + /* circle ram buffers */ + edma_link(prtd->ram_link, prtd->ram_link); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + asp_src_ping = iram_dma->addr; + asp_dst_ping = params->dma_addr; /* fifo */ + } else { + asp_src_ping = params->dma_addr; /* fifo */ + asp_dst_ping = iram_dma->addr; } + /* ping */ + link = prtd->asp_link[0]; + edma_set_src(link, asp_src_ping, INCR, W16BIT); + edma_set_dest(link, asp_dst_ping, INCR, W16BIT); + edma_set_src_index(link, 0, 0); + edma_set_dest_index(link, 0, 0); + + edma_read_slot(link, &prtd->asp_params); + prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN); + prtd->asp_params.opt |= TCCHEN | EDMA_TCC(prtd->ram_channel & 0x3f); + edma_write_slot(link, &prtd->asp_params); + + /* pong */ + link = prtd->asp_link[1]; + edma_set_src(link, asp_src_ping, INCR, W16BIT); + edma_set_dest(link, asp_dst_ping, INCR, W16BIT); + edma_set_src_index(link, 0, 0); + edma_set_dest_index(link, 0, 0); + + edma_read_slot(link, &prtd->asp_params); + prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f)); + /* interrupt after every pong completion */ + prtd->asp_params.opt |= TCINTEN | TCCHEN | + EDMA_TCC(EDMA_CHAN_SLOT(prtd->ram_channel)); + edma_write_slot(link, &prtd->asp_params); + + /* ram */ + link = prtd->ram_link; + edma_set_src(link, iram_dma->addr, INCR, W32BIT); + edma_set_dest(link, iram_dma->addr, INCR, W32BIT); + pr_debug("%s: audio dma channels/slots in use for ram:%u %u %u," + "for asp:%u %u %u\n", __func__, + prtd->ram_channel, prtd->ram_link, prtd->ram_link2, + prtd->asp_channel, prtd->asp_link[0], + prtd->asp_link[1]); + return 0; +exit4: + edma_free_channel(prtd->asp_link[1]); + prtd->asp_link[1] = -1; +exit3: + edma_free_channel(prtd->ram_link); + prtd->ram_link = -1; +exit2: + edma_free_channel(prtd->ram_channel); + prtd->ram_channel = -1; +exit1: + return link; } static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) { + struct snd_dma_buffer *iram_dma; struct davinci_runtime_data *prtd = substream->runtime->private_data; - struct edmacc_param p_ram; - int ret; + struct davinci_pcm_dma_params *params = prtd->params; + int link; - /* Request master DMA channel */ - ret = edma_alloc_channel(prtd->params->channel, - davinci_pcm_dma_irq, substream, - EVENTQ_0); - if (ret < 0) - return ret; - prtd->asp_channel = ret; + if (!params) + return -ENODEV; - /* Request parameter RAM reload slot */ - ret = edma_alloc_slot(EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY); - if (ret < 0) { - edma_free_channel(prtd->asp_channel); - return ret; + /* Request asp master DMA channel */ + link = prtd->asp_channel = edma_alloc_channel(params->channel, + davinci_pcm_dma_irq, substream, EVENTQ_0); + if (link < 0) + goto exit1; + + /* Request asp link channels */ + link = prtd->asp_link[0] = edma_alloc_slot( + EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY); + if (link < 0) + goto exit2; + + iram_dma = (struct snd_dma_buffer *)substream->dma_buffer.private_data; + if (iram_dma) { + if (request_ping_pong(substream, prtd, iram_dma) == 0) + return 0; + printk(KERN_WARNING "%s: dma channel allocation failed," + "not using sram\n", __func__); } - prtd->asp_link[0] = ret; /* Issue transfer completion IRQ when the channel completes a * transfer, then always reload from the same slot (by a kind @@ -169,12 +504,17 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) * the buffer and its length (ccnt) ... use it as a template * so davinci_pcm_enqueue_dma() takes less time in IRQ. */ - edma_read_slot(prtd->asp_link[0], &p_ram); - p_ram.opt |= TCINTEN | EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel)); - p_ram.link_bcntrld = EDMA_CHAN_SLOT(prtd->asp_link[0]) << 5; - edma_write_slot(prtd->asp_link[0], &p_ram); - + edma_read_slot(link, &prtd->asp_params); + prtd->asp_params.opt |= TCINTEN | + EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel)); + prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(link) << 5; + edma_write_slot(link, &prtd->asp_params); return 0; +exit2: + edma_free_channel(prtd->asp_channel); + prtd->asp_channel = -1; +exit1: + return link; } static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) @@ -208,14 +548,34 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) static int davinci_pcm_prepare(struct snd_pcm_substream *substream) { struct davinci_runtime_data *prtd = substream->runtime->private_data; - struct edmacc_param temp; + if (prtd->ram_channel >= 0) { + int ret = ping_pong_dma_setup(substream); + if (ret < 0) + return ret; + + edma_write_slot(prtd->ram_channel, &prtd->ram_params); + edma_write_slot(prtd->asp_channel, &prtd->asp_params); + + print_buf_info(prtd->ram_channel, "ram_channel"); + print_buf_info(prtd->ram_link, "ram_link"); + print_buf_info(prtd->ram_link2, "ram_link2"); + print_buf_info(prtd->asp_channel, "asp_channel"); + print_buf_info(prtd->asp_link[0], "asp_link[0]"); + print_buf_info(prtd->asp_link[1], "asp_link[1]"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* copy 1st iram buffer */ + edma_start(prtd->ram_channel); + } + return 0; + } prtd->period = 0; davinci_pcm_enqueue_dma(substream); /* Copy self-linked parameter RAM entry into master channel */ - edma_read_slot(prtd->asp_link[0], &temp); - edma_write_slot(prtd->asp_channel, &temp); + edma_read_slot(prtd->asp_link[0], &prtd->asp_params); + edma_write_slot(prtd->asp_channel, &prtd->asp_params); davinci_pcm_enqueue_dma(substream); return 0; @@ -231,13 +591,46 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream) dma_addr_t asp_src, asp_dst; spin_lock(&prtd->lock); - - edma_get_position(prtd->asp_channel, &asp_src, &asp_dst); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - asp_count = asp_src - runtime->dma_addr; - else - asp_count = asp_dst - runtime->dma_addr; - + if (prtd->ram_channel >= 0) { + int ram_count; + int mod_ram; + dma_addr_t ram_src, ram_dst; + unsigned int period_size = snd_pcm_lib_period_bytes(substream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* reading ram before asp should be safe + * as long as the asp transfers less than a ping size + * of bytes between the 2 reads + */ + edma_get_position(prtd->ram_channel, + &ram_src, &ram_dst); + edma_get_position(prtd->asp_channel, + &asp_src, &asp_dst); + asp_count = asp_src - prtd->asp_params.src; + ram_count = ram_src - prtd->ram_params.src; + mod_ram = ram_count % period_size; + mod_ram -= asp_count; + if (mod_ram < 0) + mod_ram += period_size; + else if (mod_ram == 0) { + if (snd_pcm_running(substream)) + mod_ram += period_size; + } + ram_count -= mod_ram; + if (ram_count < 0) + ram_count += period_size * runtime->periods; + } else { + edma_get_position(prtd->ram_channel, + &ram_src, &ram_dst); + ram_count = ram_dst - prtd->ram_params.dst; + } + asp_count = ram_count; + } else { + edma_get_position(prtd->asp_channel, &asp_src, &asp_dst); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + asp_count = asp_src - runtime->dma_addr; + else + asp_count = asp_dst - runtime->dma_addr; + } spin_unlock(&prtd->lock); offset = bytes_to_frames(runtime, asp_count); @@ -251,6 +644,7 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct davinci_runtime_data *prtd; + struct snd_pcm_hardware *ppcm; int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct davinci_pcm_dma_params *pa = rtd->dai->cpu_dai->dma_data; @@ -259,7 +653,10 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream) return -ENODEV; params = &pa[substream->stream]; - snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware); + ppcm = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + &pcm_hardware_playback : &pcm_hardware_capture; + allocate_sram(substream, params->sram_size, ppcm); + snd_soc_set_runtime_hwparams(substream, ppcm); /* ensure that buffer size is a multiple of period size */ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); @@ -272,6 +669,11 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream) spin_lock_init(&prtd->lock); prtd->params = params; + prtd->asp_channel = -1; + prtd->asp_link[0] = prtd->asp_link[1] = -1; + prtd->ram_channel = -1; + prtd->ram_link = -1; + prtd->ram_link2 = -1; runtime->private_data = prtd; @@ -289,10 +691,29 @@ static int davinci_pcm_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct davinci_runtime_data *prtd = runtime->private_data; - edma_unlink(prtd->asp_link[0]); - - edma_free_slot(prtd->asp_link[0]); - edma_free_channel(prtd->asp_channel); + if (prtd->ram_channel >= 0) + edma_stop(prtd->ram_channel); + if (prtd->asp_channel >= 0) + edma_stop(prtd->asp_channel); + if (prtd->asp_link[0] >= 0) + edma_unlink(prtd->asp_link[0]); + if (prtd->asp_link[1] >= 0) + edma_unlink(prtd->asp_link[1]); + if (prtd->ram_link >= 0) + edma_unlink(prtd->ram_link); + + if (prtd->asp_link[0] >= 0) + edma_free_slot(prtd->asp_link[0]); + if (prtd->asp_link[1] >= 0) + edma_free_slot(prtd->asp_link[1]); + if (prtd->asp_channel >= 0) + edma_free_channel(prtd->asp_channel); + if (prtd->ram_link >= 0) + edma_free_slot(prtd->ram_link); + if (prtd->ram_link2 >= 0) + edma_free_slot(prtd->ram_link2); + if (prtd->ram_channel >= 0) + edma_free_channel(prtd->ram_channel); kfree(prtd); @@ -334,11 +755,11 @@ static struct snd_pcm_ops davinci_pcm_ops = { .mmap = davinci_pcm_mmap, }; -static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream, + size_t size) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = davinci_pcm_hardware.buffer_bytes_max; buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; @@ -363,6 +784,7 @@ static void davinci_pcm_free(struct snd_pcm *pcm) int stream; for (stream = 0; stream < 2; stream++) { + struct snd_dma_buffer *iram_dma; substream = pcm->streams[stream].substream; if (!substream) continue; @@ -374,6 +796,11 @@ static void davinci_pcm_free(struct snd_pcm *pcm) dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area, buf->addr); buf->area = NULL; + iram_dma = (struct snd_dma_buffer *)buf->private_data; + if (iram_dma) { + sram_free(iram_dma->area, iram_dma->bytes); + kfree(iram_dma); + } } } @@ -391,14 +818,16 @@ static int davinci_pcm_new(struct snd_card *card, if (dai->playback.channels_min) { ret = davinci_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); + SNDRV_PCM_STREAM_PLAYBACK, + pcm_hardware_playback.buffer_bytes_max); if (ret) return ret; } if (dai->capture.channels_min) { ret = davinci_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); + SNDRV_PCM_STREAM_CAPTURE, + pcm_hardware_capture.buffer_bytes_max); if (ret) return ret; } diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h index c8b0d2b..0764944 100644 --- a/sound/soc/davinci/davinci-pcm.h +++ b/sound/soc/davinci/davinci-pcm.h @@ -20,6 +20,7 @@ struct davinci_pcm_dma_params { int channel; /* sync dma channel ID */ unsigned short acnt; dma_addr_t dma_addr; /* device physical address for DMA */ + unsigned sram_size; enum dma_event_q eventq_no; /* event queue number */ unsigned char data_type; /* xfer data type */ unsigned char convert_mono_stereo; -- cgit v0.10.2 From 2b7b250df74f1f9e15cdf33fa90f6c98a419842d Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 18 Nov 2009 17:49:54 -0700 Subject: ASoC: DaVinci: use edma_pause, edma_resume Use edma_pause and edma_resume to make missing dma_events less likely. This may not be needed, but it looks better. Signed-off-by: Troy Kisky Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 664d493..ad4d7f4 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -528,12 +528,12 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - edma_start(prtd->asp_channel); + edma_resume(prtd->asp_channel); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - edma_stop(prtd->asp_channel); + edma_pause(prtd->asp_channel); break; default: ret = -EINVAL; @@ -568,6 +568,7 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) /* copy 1st iram buffer */ edma_start(prtd->ram_channel); } + edma_start(prtd->asp_channel); return 0; } prtd->period = 0; @@ -577,6 +578,7 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) edma_read_slot(prtd->asp_link[0], &prtd->asp_params); edma_write_slot(prtd->asp_channel, &prtd->asp_params); davinci_pcm_enqueue_dma(substream); + edma_start(prtd->asp_channel); return 0; } -- cgit v0.10.2 From b2a2236d1f5e7c09c8e74b61f13d8ba3fe82f7be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enric=20Balletb=C3=B2=20i=20Serra?= Date: Wed, 18 Nov 2009 15:59:24 +0100 Subject: ASoC: Add support for IGEP v2 Signed-off-by: Enric Balletbo i Serra Signed-off-by: Mark Brown diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 4dc6b15a..61952aa 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -109,3 +109,10 @@ config SND_OMAP_SOC_ZOOM2 help Say Y if you want to add support for Soc audio on Zoom2 board. +config SND_OMAP_SOC_IGEP0020 + tristate "SoC Audio support for IGEP v2" + depends on TWL4030_CORE && SND_OMAP_SOC && MACH_IGEP0020 + select SND_OMAP_SOC_MCBSP + select SND_SOC_TWL4030 + help + Say Y if you want to add support for Soc audio on IGEP v2 board. diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 0c78ae4..d49458a 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -17,6 +17,7 @@ snd-soc-sdp3430-objs := sdp3430.o snd-soc-omap3pandora-objs := omap3pandora.o snd-soc-omap3beagle-objs := omap3beagle.o snd-soc-zoom2-objs := zoom2.o +snd-soc-igep0020-objs := igep0020.o obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o @@ -29,3 +30,4 @@ obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o +obj-$(CONFIG_SND_OMAP_SOC_IGEP0020) += snd-soc-igep0020.o diff --git a/sound/soc/omap/igep0020.c b/sound/soc/omap/igep0020.c new file mode 100644 index 0000000..3583c42 --- /dev/null +++ b/sound/soc/omap/igep0020.c @@ -0,0 +1,148 @@ +/* + * igep0020.c -- SoC audio for IGEP v2 + * + * Based on sound/soc/omap/overo.c by Steve Sakoman + * + * 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 "omap-mcbsp.h" +#include "omap-pcm.h" +#include "../codecs/twl4030.h" + +static int igep2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int ret; + + /* Set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + printk(KERN_ERR "can't set codec DAI configuration\n"); + return ret; + } + + /* Set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + printk(KERN_ERR "can't set cpu DAI configuration\n"); + return ret; + } + + /* Set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, + SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set codec system clock\n"); + return ret; + } + + return 0; +} + +static struct snd_soc_ops igep2_ops = { + .hw_params = igep2_hw_params, +}; + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link igep2_dai = { + .name = "TWL4030", + .stream_name = "TWL4030", + .cpu_dai = &omap_mcbsp_dai[0], + .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI], + .ops = &igep2_ops, +}; + +/* Audio machine driver */ +static struct snd_soc_card snd_soc_card_igep2 = { + .name = "igep2", + .platform = &omap_soc_platform, + .dai_link = &igep2_dai, + .num_links = 1, +}; + +/* Audio subsystem */ +static struct snd_soc_device igep2_snd_devdata = { + .card = &snd_soc_card_igep2, + .codec_dev = &soc_codec_dev_twl4030, +}; + +static struct platform_device *igep2_snd_device; + +static int __init igep2_soc_init(void) +{ + int ret; + + if (!machine_is_igep0020()) { + pr_debug("Not IGEP v2!\n"); + return -ENODEV; + } + printk(KERN_INFO "IGEP v2 SoC init\n"); + + igep2_snd_device = platform_device_alloc("soc-audio", -1); + if (!igep2_snd_device) { + printk(KERN_ERR "Platform device allocation failed\n"); + return -ENOMEM; + } + + platform_set_drvdata(igep2_snd_device, &igep2_snd_devdata); + igep2_snd_devdata.dev = &igep2_snd_device->dev; + *(unsigned int *)igep2_dai.cpu_dai->private_data = 1; /* McBSP2 */ + + ret = platform_device_add(igep2_snd_device); + if (ret) + goto err1; + + return 0; + +err1: + printk(KERN_ERR "Unable to add platform device\n"); + platform_device_put(igep2_snd_device); + + return ret; +} +module_init(igep2_soc_init); + +static void __exit igep2_soc_exit(void) +{ + platform_device_unregister(igep2_snd_device); +} +module_exit(igep2_soc_exit); + +MODULE_AUTHOR("Enric Balletbo i Serra "); +MODULE_DESCRIPTION("ALSA SoC IGEP v2"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From b5e102cdcfb4a5d4349a9628eb5ad11bd65a1f3b Mon Sep 17 00:00:00 2001 From: Daniel J Blueman Date: Wed, 25 Nov 2009 12:12:59 +0000 Subject: mfd: twl4030: fix ELF section mismatch... Since twl4030_probe is only called from functions in the init ELF section, annotate it so. Signed-off-by: Daniel J Blueman Acked-by: Samuel Ortiz Signed-off-by: Mark Brown diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index 0ee81e4..5049d04 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c @@ -774,7 +774,7 @@ static int twl4030_remove(struct i2c_client *client) } /* NOTE: this driver only handles a single twl4030/tps659x0 chip */ -static int +static int __init twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id) { int status; -- cgit v0.10.2 From c0fa59df7214e546f8a37bc677867ac7b67b5c93 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 19 Nov 2009 11:36:10 +0000 Subject: ASoC: Add BCLK calculation utility for TDM mode too Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 13b117a..0d7718f9 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -230,6 +230,7 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid); /* Utility functions to get clock rates from various things */ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params); +int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots); int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms); /* set runtime hw params */ diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index b16aaae..1d07b93 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -54,6 +54,12 @@ int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params) } EXPORT_SYMBOL_GPL(snd_soc_params_to_frame_size); +int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots) +{ + return fs * snd_soc_calc_frame_size(sample_size, channels, tdm_slots); +} +EXPORT_SYMBOL_GPL(snd_soc_calc_bclk); + int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params) { int ret; -- cgit v0.10.2 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 diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 2a013e4..9c8903d 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 v0.10.2 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 diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index dfffc6c..ec54c6d 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 2a08722..0ac1215 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 00bac31..4d74ecb 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 v0.10.2 From 49af574b60669a58a2e96960ac694ce953119083 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 27 Nov 2009 13:47:10 +0100 Subject: ALSA: ARM: add Raumfeld audio support Signed-off-by: Daniel Mack Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index d4f4031..376e14a 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -118,6 +118,15 @@ config SND_SOC_ZYLONITE Say Y if you want to add support for SoC audio on the Marvell Zylonite reference platform. +config SND_SOC_RAUMFELD + tristate "SoC Audio support Raumfeld audio adapter" + depends on SND_PXA2XX_SOC && (MACH_RAUMFELD_SPEAKER || MACH_RAUMFELD_CONNECTOR) + select SND_PXA_SOC_SSP + select SND_SOC_CS4270 + select SND_SOC_AK4104 + help + Say Y if you want to add support for SoC audio on Raumfeld devices + config SND_PXA2XX_SOC_MAGICIAN tristate "SoC Audio support for HTC Magician" depends on SND_PXA2XX_SOC && MACH_MAGICIAN diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index 6e096b4..f3e08fd 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -23,6 +23,7 @@ snd-soc-zylonite-objs := zylonite.o snd-soc-magician-objs := magician.o snd-soc-mioa701-objs := mioa701_wm9713.o snd-soc-imote2-objs := imote2.o +snd-soc-raumfeld-objs := raumfeld.o obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o @@ -37,3 +38,4 @@ obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o +obj-$(CONFIG_SND_SOC_RAUMFELD) += snd-soc-raumfeld.o diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c new file mode 100644 index 0000000..f272269 --- /dev/null +++ b/sound/soc/pxa/raumfeld.c @@ -0,0 +1,335 @@ +/* + * raumfeld_audio.c -- SoC audio for Raumfeld audio devices + * + * Copyright (c) 2009 Daniel Mack + * + * based on code from: + * + * Wolfson Microelectronics PLC. + * Openedhand Ltd. + * Liam Girdwood + * Richard Purdie + * + * 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 "../codecs/cs4270.h" +#include "../codecs/ak4104.h" +#include "pxa2xx-pcm.h" +#include "pxa-ssp.h" + +#define GPIO_SPDIF_RESET (38) +#define GPIO_MCLK_RESET (111) +#define GPIO_CODEC_RESET (120) + +static struct i2c_client *max9486_client; +static struct i2c_board_info max9486_hwmon_info = { + I2C_BOARD_INFO("max9485", 0x63), +}; + +#define MAX9485_MCLK_FREQ_112896 0x22 +#define MAX9485_MCLK_FREQ_122880 0x23 + +static void set_max9485_clk(char clk) +{ + i2c_master_send(max9486_client, &clk, 1); +} + +static void raumfeld_enable_audio(bool en) +{ + if (en) { + gpio_set_value(GPIO_MCLK_RESET, 1); + + /* wait some time to let the clocks become stable */ + msleep(100); + + gpio_set_value(GPIO_SPDIF_RESET, 1); + gpio_set_value(GPIO_CODEC_RESET, 1); + } else { + gpio_set_value(GPIO_MCLK_RESET, 0); + gpio_set_value(GPIO_SPDIF_RESET, 0); + gpio_set_value(GPIO_CODEC_RESET, 0); + } +} + +/* CS4270 */ +static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + + set_max9485_clk(MAX9485_MCLK_FREQ_112896); + + return snd_soc_dai_set_sysclk(codec_dai, 0, 11289600, 0); +} + +static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + unsigned int fmt, clk = 0; + int ret = 0; + + switch (params_rate(params)) { + case 8000: + case 16000: + case 48000: + case 96000: + set_max9485_clk(MAX9485_MCLK_FREQ_122880); + clk = 12288000; + break; + case 11025: + case 22050: + case 44100: + case 88200: + set_max9485_clk(MAX9485_MCLK_FREQ_112896); + clk = 11289600; + break; + } + + fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; + + /* setup the CODEC DAI */ + ret = snd_soc_dai_set_fmt(codec_dai, fmt); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0); + if (ret < 0) + return ret; + + /* setup the CPU DAI */ + ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, clk); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, 0, 1); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops raumfeld_cs4270_ops = { + .startup = raumfeld_cs4270_startup, + .hw_params = raumfeld_cs4270_hw_params, +}; + +static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state) +{ + raumfeld_enable_audio(false); + return 0; +} + +static int raumfeld_line_resume(struct platform_device *pdev) +{ + raumfeld_enable_audio(true); + return 0; +} + +static struct snd_soc_dai_link raumfeld_line_dai = { + .name = "CS4270", + .stream_name = "CS4270", + .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP1], + .codec_dai = &cs4270_dai, + .ops = &raumfeld_cs4270_ops, +}; + +static struct snd_soc_card snd_soc_line_raumfeld = { + .name = "Raumfeld analog", + .platform = &pxa2xx_soc_platform, + .dai_link = &raumfeld_line_dai, + .suspend_post = raumfeld_line_suspend, + .resume_pre = raumfeld_line_resume, + .num_links = 1, +}; + + +/* AK4104 */ + +static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int fmt, ret = 0, clk = 0; + + switch (params_rate(params)) { + case 8000: + case 16000: + case 48000: + case 96000: + set_max9485_clk(MAX9485_MCLK_FREQ_122880); + clk = 12288000; + break; + case 11025: + case 22050: + case 44100: + case 88200: + set_max9485_clk(MAX9485_MCLK_FREQ_112896); + clk = 11289600; + break; + } + + fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF; + + /* setup the CODEC DAI */ + ret = snd_soc_dai_set_fmt(codec_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* setup the CPU DAI */ + ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, clk); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, 0, 1); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops raumfeld_ak4104_ops = { + .hw_params = raumfeld_ak4104_hw_params, +}; + +static struct snd_soc_dai_link raumfeld_spdif_dai = { + .name = "ak4104", + .stream_name = "Playback", + .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP2], + .codec_dai = &ak4104_dai, + .ops = &raumfeld_ak4104_ops, +}; + +static struct snd_soc_card snd_soc_spdif_raumfeld = { + .name = "Raumfeld S/PDIF", + .platform = &pxa2xx_soc_platform, + .dai_link = &raumfeld_spdif_dai, + .num_links = 1 +}; + +/* raumfeld_audio audio subsystem */ +static struct snd_soc_device raumfeld_line_devdata = { + .card = &snd_soc_line_raumfeld, + .codec_dev = &soc_codec_device_cs4270, +}; + +static struct snd_soc_device raumfeld_spdif_devdata = { + .card = &snd_soc_spdif_raumfeld, + .codec_dev = &soc_codec_device_ak4104, +}; + +static struct platform_device *raumfeld_audio_line_device; +static struct platform_device *raumfeld_audio_spdif_device; + +static int __init raumfeld_audio_init(void) +{ + int ret; + + if (!machine_is_raumfeld_speaker() && + !machine_is_raumfeld_connector()) + return 0; + + max9486_client = i2c_new_device(i2c_get_adapter(0), + &max9486_hwmon_info); + + if (!max9486_client) + return -ENOMEM; + + set_max9485_clk(MAX9485_MCLK_FREQ_122880); + + /* LINE */ + raumfeld_audio_line_device = platform_device_alloc("soc-audio", 0); + if (!raumfeld_audio_line_device) + return -ENOMEM; + + platform_set_drvdata(raumfeld_audio_line_device, + &raumfeld_line_devdata); + raumfeld_line_devdata.dev = &raumfeld_audio_line_device->dev; + ret = platform_device_add(raumfeld_audio_line_device); + if (ret) + platform_device_put(raumfeld_audio_line_device); + + /* no S/PDIF on Speakers */ + if (machine_is_raumfeld_speaker()) + return ret; + + /* S/PDIF */ + raumfeld_audio_spdif_device = platform_device_alloc("soc-audio", 1); + if (!raumfeld_audio_spdif_device) { + platform_device_put(raumfeld_audio_line_device); + return -ENOMEM; + } + + platform_set_drvdata(raumfeld_audio_spdif_device, + &raumfeld_spdif_devdata); + raumfeld_spdif_devdata.dev = &raumfeld_audio_spdif_device->dev; + ret = platform_device_add(raumfeld_audio_spdif_device); + if (ret) { + platform_device_put(raumfeld_audio_line_device); + platform_device_put(raumfeld_audio_spdif_device); + } + + raumfeld_enable_audio(true); + + return ret; +} + +static void __exit raumfeld_audio_exit(void) +{ + raumfeld_enable_audio(false); + + platform_device_unregister(raumfeld_audio_line_device); + + if (machine_is_raumfeld_connector()) + platform_device_unregister(raumfeld_audio_spdif_device); + + i2c_unregister_device(max9486_client); + + gpio_free(GPIO_MCLK_RESET); + gpio_free(GPIO_CODEC_RESET); + gpio_free(GPIO_SPDIF_RESET); +} + +module_init(raumfeld_audio_init); +module_exit(raumfeld_audio_exit); + +/* Module information */ +MODULE_AUTHOR("Daniel Mack "); +MODULE_DESCRIPTION("Raumfeld audio SoC"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From d53bd80cb32d917e224b19925bb8f500941a3659 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 9 Nov 2009 11:12:49 +0900 Subject: sh: ms7724se: Add runtime PM support for FSI Signed-off-by: Kuninori Morimoto Acked-by: Paul Mundt Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index e78c3be..0894bba 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -313,6 +313,9 @@ static struct platform_device fsi_device = { .dev = { .platform_data = &fsi_info, }, + .archdata = { + .hwblk_id = HWBLK_SPU, /* FSI needs SPU hwblk */ + }, }; /* KEYSC in SoC (Needs SW33-2 set to ON) */ -- cgit v0.10.2 From 785d1c45ce11820d5838eb6399caa0ac98c836cf Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 30 Nov 2009 20:24:48 +0900 Subject: ASoC: sh: fsi: Add runtime PM support This patch add support runtime PM. Driver callbacks for Runtime PM are empty because the device registers are always re-initialized after pm_runtime_get_sync(). The Runtime PM functions replaces the clock framework module stop bit handling in this driver. Signed-off-by: Kuninori Morimoto Acked-by: Paul Mundt Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index e1a3d1a..9c49c11 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -105,7 +105,6 @@ struct fsi_priv { struct fsi_master { void __iomem *base; int irq; - struct clk *clk; struct fsi_priv fsia; struct fsi_priv fsib; struct sh_fsi_platform_info *info; @@ -559,7 +558,7 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, int is_master; int ret = 0; - clk_enable(master->clk); + pm_runtime_get_sync(dai->dev); /* CKG1 */ data = is_play ? (1 << 0) : (1 << 4); @@ -674,7 +673,7 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream, fsi_irq_disable(fsi, is_play); fsi_clk_ctrl(fsi, 0); - clk_disable(master->clk); + pm_runtime_put_sync(dai->dev); } static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, @@ -872,7 +871,6 @@ EXPORT_SYMBOL_GPL(fsi_soc_platform); static int fsi_probe(struct platform_device *pdev) { struct resource *res; - char clk_name[8]; unsigned int irq; int ret; @@ -903,14 +901,8 @@ static int fsi_probe(struct platform_device *pdev) master->fsia.base = master->base; master->fsib.base = master->base + 0x40; - /* FSI is based on SPU mstp */ - snprintf(clk_name, sizeof(clk_name), "spu%d", pdev->id); - master->clk = clk_get(NULL, clk_name); - if (IS_ERR(master->clk)) { - dev_err(&pdev->dev, "cannot get %s mstp\n", clk_name); - ret = -EIO; - goto exit_iounmap; - } + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); fsi_soc_dai[0].dev = &pdev->dev; fsi_soc_dai[1].dev = &pdev->dev; @@ -935,6 +927,7 @@ exit_free_irq: free_irq(irq, master); exit_iounmap: iounmap(master->base); + pm_runtime_disable(&pdev->dev); exit_kfree: kfree(master); master = NULL; @@ -947,7 +940,7 @@ static int fsi_remove(struct platform_device *pdev) snd_soc_unregister_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai)); snd_soc_unregister_platform(&fsi_soc_platform); - clk_put(master->clk); + pm_runtime_disable(&pdev->dev); free_irq(master->irq, master); @@ -957,9 +950,27 @@ static int fsi_remove(struct platform_device *pdev) return 0; } +static int fsi_runtime_nop(struct device *dev) +{ + /* Runtime PM callback shared between ->runtime_suspend() + * and ->runtime_resume(). Simply returns success. + * + * This driver re-initializes all registers after + * pm_runtime_get_sync() anyway so there is no need + * to save and restore registers here. + */ + return 0; +} + +static struct dev_pm_ops fsi_pm_ops = { + .runtime_suspend = fsi_runtime_nop, + .runtime_resume = fsi_runtime_nop, +}; + static struct platform_driver fsi_driver = { .driver = { .name = "sh_fsi", + .pm = &fsi_pm_ops, }, .probe = fsi_probe, .remove = fsi_remove, -- cgit v0.10.2 From a649d1fcc9bd2299cb06b6594fabb429fa50f174 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 30 Nov 2009 14:06:37 +0100 Subject: ASoC: pxa/raumfeld: adopt new snd_soc_dai_set_pll() API ALSA's for-2.6.33 branch has a new source argument to snd_soc_dai_set_pll(). Signed-off-by: Daniel Mack Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c index f272269..acfce1c 100644 --- a/sound/soc/pxa/raumfeld.c +++ b/sound/soc/pxa/raumfeld.c @@ -116,7 +116,7 @@ static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, return ret; /* setup the CPU DAI */ - ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, clk); + ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); if (ret < 0) return ret; @@ -205,7 +205,7 @@ static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream, return ret; /* setup the CPU DAI */ - ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, clk); + ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); if (ret < 0) return ret; -- cgit v0.10.2 From 1bc8079879e8edfff451b62b7550bdd18523f963 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Tue, 1 Dec 2009 18:10:34 +0100 Subject: ASoC: au1x: dbdma2: fix oops on soc device removal. platform_device_unregister() frees resources for us, no need to do it explicitly. Fixes an oops when machine code removes the soc-audio device. Signed-off-by: Manuel Lauss Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index fe9f465..2ca33b0 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -488,11 +488,8 @@ EXPORT_SYMBOL_GPL(au1xpsc_pcm_add); void au1xpsc_pcm_destroy(struct platform_device *dmapd) { - if (dmapd) { - kfree(dmapd->resource); - dmapd->resource = NULL; + if (dmapd) platform_device_unregister(dmapd); - } } EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy); -- cgit v0.10.2 From efd9eb96d5604c2c133e500f7b8c7b3f3fbdece8 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Tue, 1 Dec 2009 18:10:35 +0100 Subject: ASoC: au1x: dbdma2: plug memleak in pcm device creation error path free the allocated pcm platform device in the error path. Signed-off-by: Manuel Lauss Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index 2ca33b0..19e4d37 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -480,6 +480,7 @@ struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev) if (!ret) return pd; + platform_device_put(pd); out: kfree(res); return NULL; -- cgit v0.10.2 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 diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 810a563..d73c305 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 v0.10.2 From 43f0de8d0298e624e6c3bf2185b6003a59b331bd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Nov 2009 16:43:53 +0000 Subject: S3C64XX: Staticise platform data for PCM devices The symbols aren't declared and don't need to be exported, they go along with the device structure. Signed-off-by: Mark Brown Acked-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/dev-audio.c b/arch/arm/plat-s3c64xx/dev-audio.c index 9e07344..a21a88f 100644 --- a/arch/arm/plat-s3c64xx/dev-audio.c +++ b/arch/arm/plat-s3c64xx/dev-audio.c @@ -118,7 +118,7 @@ static struct resource s3c64xx_pcm0_resource[] = { }, }; -struct s3c_audio_pdata s3c_pcm0_pdata = { +static struct s3c_audio_pdata s3c_pcm0_pdata = { .cfg_gpio = s3c64xx_pcm_cfg_gpio, }; @@ -151,7 +151,7 @@ static struct resource s3c64xx_pcm1_resource[] = { }, }; -struct s3c_audio_pdata s3c_pcm1_pdata = { +static struct s3c_audio_pdata s3c_pcm1_pdata = { .cfg_gpio = s3c64xx_pcm_cfg_gpio, }; -- cgit v0.10.2