diff options
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r-- | sound/soc/codecs/Kconfig | 4 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/ak4642.c | 502 | ||||
-rw-r--r-- | sound/soc/codecs/ak4642.h | 20 | ||||
-rw-r--r-- | sound/soc/codecs/tlv320aic3x.c | 221 | ||||
-rw-r--r-- | sound/soc/codecs/tlv320aic3x.h | 2 | ||||
-rw-r--r-- | sound/soc/codecs/wm8993.c | 103 | ||||
-rw-r--r-- | sound/soc/codecs/wm9705.c | 2 | ||||
-rw-r--r-- | sound/soc/codecs/wm_hubs.c | 7 |
9 files changed, 727 insertions, 136 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 167a5ce06cd..0edca93af3b 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -18,6 +18,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_AD73311 if I2C select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C + select SND_SOC_AK4642 if I2C select SND_SOC_CS4270 if I2C select SND_SOC_MAX9877 if I2C select SND_SOC_PCM3008 @@ -92,6 +93,9 @@ config SND_SOC_AK4104 config SND_SOC_AK4535 tristate +config SND_SOC_AK4642 + tristate + # Cirrus Logic CS4270 Codec config SND_SOC_CS4270 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index fbab43bbe3a..fb4af28486b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -5,6 +5,7 @@ snd-soc-ad1980-objs := ad1980.o 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-cs4270-objs := cs4270.o snd-soc-cx20442-objs := cx20442.o snd-soc-l3-objs := l3.o @@ -54,6 +55,7 @@ obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o 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_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/ak4642.c b/sound/soc/codecs/ak4642.c new file mode 100644 index 00000000000..e057c7b578d --- /dev/null +++ b/sound/soc/codecs/ak4642.c @@ -0,0 +1,502 @@ +/* + * ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver + * + * Copyright (C) 2009 Renesas Solutions Corp. + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * Based on wm8731.c by Richard Purdie + * Based on ak4535.c by Richard Purdie + * Based on wm8753.c by Liam Girdwood + * + * 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. + */ + +/* ** CAUTION ** + * + * This is very simple driver. + * It can use headphone output / stereo input only + * + * AK4642 is not tested. + * AK4643 is tested. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> + +#include "ak4642.h" + +#define AK4642_VERSION "0.0.1" + +#define PW_MGMT1 0x00 +#define PW_MGMT2 0x01 +#define SG_SL1 0x02 +#define SG_SL2 0x03 +#define MD_CTL1 0x04 +#define MD_CTL2 0x05 +#define TIMER 0x06 +#define ALC_CTL1 0x07 +#define ALC_CTL2 0x08 +#define L_IVC 0x09 +#define L_DVC 0x0a +#define ALC_CTL3 0x0b +#define R_IVC 0x0c +#define R_DVC 0x0d +#define MD_CTL3 0x0e +#define MD_CTL4 0x0f +#define PW_MGMT3 0x10 +#define DF_S 0x11 +#define FIL3_0 0x12 +#define FIL3_1 0x13 +#define FIL3_2 0x14 +#define FIL3_3 0x15 +#define EQ_0 0x16 +#define EQ_1 0x17 +#define EQ_2 0x18 +#define EQ_3 0x19 +#define EQ_4 0x1a +#define EQ_5 0x1b +#define FIL1_0 0x1c +#define FIL1_1 0x1d +#define FIL1_2 0x1e +#define FIL1_3 0x1f +#define PW_MGMT4 0x20 +#define MD_CTL5 0x21 +#define LO_MS 0x22 +#define HP_MS 0x23 +#define SPK_MS 0x24 + +#define AK4642_CACHEREGNUM 0x25 + +struct snd_soc_codec_device soc_codec_dev_ak4642; + +/* codec private data */ +struct ak4642_priv { + struct snd_soc_codec codec; + unsigned int sysclk; +}; + +static struct snd_soc_codec *ak4642_codec; + +/* + * ak4642 register cache + */ +static const u16 ak4642_reg[AK4642_CACHEREGNUM] = { + 0x0000, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0000, + 0x00e1, 0x00e1, 0x0018, 0x0000, + 0x00e1, 0x0018, 0x0011, 0x0008, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, +}; + +/* + * read ak4642 register cache + */ +static inline unsigned int ak4642_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + if (reg >= AK4642_CACHEREGNUM) + return -1; + return cache[reg]; +} + +/* + * write ak4642 register cache + */ +static inline void ak4642_write_reg_cache(struct snd_soc_codec *codec, + u16 reg, unsigned int value) +{ + u16 *cache = codec->reg_cache; + if (reg >= AK4642_CACHEREGNUM) + return; + + cache[reg] = value; +} + +/* + * write to the AK4642 register space + */ +static int ak4642_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[2]; + + /* data is + * D15..D8 AK4642 register offset + * D7...D0 register data + */ + data[0] = reg & 0xff; + data[1] = value & 0xff; + + if (codec->hw_write(codec->control_data, data, 2) == 2) { + ak4642_write_reg_cache(codec, reg, value); + return 0; + } else + return -EIO; +} + +static int ak4642_sync(struct snd_soc_codec *codec) +{ + u16 *cache = codec->reg_cache; + int i, r = 0; + + for (i = 0; i < AK4642_CACHEREGNUM; i++) + r |= ak4642_write(codec, i, cache[i]); + + return r; +}; + +static int ak4642_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct snd_soc_codec *codec = dai->codec; + + if (is_play) { + /* + * start headphone output + * + * PLL, Master Mode + * Audio I/F Format :MSB justified (ADC & DAC) + * Sampling Frequency: 44.1kHz + * Digital Volume: −8dB + * Bass Boost Level : Middle + * + * This operation came from example code of + * "ASAHI KASEI AK4642" (japanese) manual p97. + * + * Example code use 0x39, 0x79 value for 0x01 address, + * But we need MCKO (0x02) bit now + */ + ak4642_write(codec, 0x05, 0x27); + ak4642_write(codec, 0x0f, 0x09); + ak4642_write(codec, 0x0e, 0x19); + ak4642_write(codec, 0x09, 0x91); + ak4642_write(codec, 0x0c, 0x91); + ak4642_write(codec, 0x0a, 0x28); + ak4642_write(codec, 0x0d, 0x28); + ak4642_write(codec, 0x00, 0x64); + ak4642_write(codec, 0x01, 0x3b); /* + MCKO bit */ + ak4642_write(codec, 0x01, 0x7b); /* + MCKO bit */ + } else { + /* + * start stereo input + * + * PLL Master Mode + * Audio I/F Format:MSB justified (ADC & DAC) + * Sampling Frequency:44.1kHz + * Pre MIC AMP:+20dB + * MIC Power On + * ALC setting:Refer to Table 35 + * ALC bit=“1” + * + * This operation came from example code of + * "ASAHI KASEI AK4642" (japanese) manual p94. + */ + ak4642_write(codec, 0x05, 0x27); + ak4642_write(codec, 0x02, 0x05); + ak4642_write(codec, 0x06, 0x3c); + ak4642_write(codec, 0x08, 0xe1); + ak4642_write(codec, 0x0b, 0x00); + ak4642_write(codec, 0x07, 0x21); + ak4642_write(codec, 0x00, 0x41); + ak4642_write(codec, 0x10, 0x01); + } + + return 0; +} + +static void ak4642_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct snd_soc_codec *codec = dai->codec; + + if (is_play) { + /* stop headphone output */ + ak4642_write(codec, 0x01, 0x3b); + ak4642_write(codec, 0x01, 0x0b); + ak4642_write(codec, 0x00, 0x40); + ak4642_write(codec, 0x0e, 0x11); + ak4642_write(codec, 0x0f, 0x08); + } else { + /* stop stereo input */ + ak4642_write(codec, 0x00, 0x40); + ak4642_write(codec, 0x10, 0x00); + ak4642_write(codec, 0x07, 0x01); + } +} + +static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct ak4642_priv *ak4642 = codec->private_data; + + ak4642->sysclk = freq; + return 0; +} + +static struct snd_soc_dai_ops ak4642_dai_ops = { + .startup = ak4642_dai_startup, + .shutdown = ak4642_dai_shutdown, + .set_sysclk = ak4642_dai_set_sysclk, +}; + +struct snd_soc_dai ak4642_dai = { + .name = "AK4642", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE }, + .ops = &ak4642_dai_ops, +}; +EXPORT_SYMBOL_GPL(ak4642_dai); + +static int ak4642_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + ak4642_sync(codec); + return 0; +} + +/* + * initialise the AK4642 driver + * register the mixer and dsp interfaces with the kernel + */ +static int ak4642_init(struct ak4642_priv *ak4642) +{ + struct snd_soc_codec *codec = &ak4642->codec; + int ret = 0; + + if (ak4642_codec) { + dev_err(codec->dev, "Another ak4642 is registered\n"); + return -EINVAL; + } + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + codec->private_data = ak4642; + codec->name = "AK4642"; + codec->owner = THIS_MODULE; + codec->read = ak4642_read_reg_cache; + codec->write = ak4642_write; + codec->dai = &ak4642_dai; + codec->num_dai = 1; + codec->hw_write = (hw_write_t)i2c_master_send; + codec->reg_cache_size = ARRAY_SIZE(ak4642_reg); + codec->reg_cache = kmemdup(ak4642_reg, + sizeof(ak4642_reg), GFP_KERNEL); + + if (!codec->reg_cache) + return -ENOMEM; + + ak4642_dai.dev = codec->dev; + ak4642_codec = codec; + + ret = snd_soc_register_codec(codec); + if (ret) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + goto reg_cache_err; + } + + ret = snd_soc_register_dai(&ak4642_dai); + if (ret) { + dev_err(codec->dev, "Failed to register DAI: %d\n", ret); + snd_soc_unregister_codec(codec); + goto reg_cache_err; + } + + /* + * clock setting + * + * Audio I/F Format: MSB justified (ADC & DAC) + * BICK frequency at Master Mode: 64fs + * Input Master Clock Select at PLL Mode: 11.2896MHz + * MCKO: Enable + * Sampling Frequency: 44.1kHz + * + * This operation came from example code of + * "ASAHI KASEI AK4642" (japanese) manual p89. + * + * please fix-me + */ + ak4642_write(codec, 0x01, 0x08); + ak4642_write(codec, 0x04, 0x4a); + ak4642_write(codec, 0x05, 0x27); + ak4642_write(codec, 0x00, 0x40); + ak4642_write(codec, 0x01, 0x0b); + + return ret; + +reg_cache_err: + kfree(codec->reg_cache); + codec->reg_cache = NULL; + + return ret; +} + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static int ak4642_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct ak4642_priv *ak4642; + struct snd_soc_codec *codec; + int ret; + + ak4642 = kzalloc(sizeof(struct ak4642_priv), GFP_KERNEL); + if (!ak4642) + return -ENOMEM; + + codec = &ak4642->codec; + codec->dev = &i2c->dev; + + i2c_set_clientdata(i2c, ak4642); + codec->control_data = i2c; + + ret = ak4642_init(ak4642); + if (ret < 0) + printk(KERN_ERR "failed to initialise AK4642\n"); + + return ret; +} + +static int ak4642_i2c_remove(struct i2c_client *client) +{ + struct ak4642_priv *ak4642 = i2c_get_clientdata(client); + + snd_soc_unregister_dai(&ak4642_dai); + snd_soc_unregister_codec(&ak4642->codec); + kfree(ak4642->codec.reg_cache); + kfree(ak4642); + ak4642_codec = NULL; + + return 0; +} + +static const struct i2c_device_id ak4642_i2c_id[] = { + { "ak4642", 0 }, + { "ak4643", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id); + +static struct i2c_driver ak4642_i2c_driver = { + .driver = { + .name = "AK4642 I2C Codec", + .owner = THIS_MODULE, + }, + .probe = ak4642_i2c_probe, + .remove = ak4642_i2c_remove, + .id_table = ak4642_i2c_id, +}; + +#endif + +static int ak4642_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + int ret; + + if (!ak4642_codec) { + dev_err(&pdev->dev, "Codec device not registered\n"); + return -ENODEV; + } + + socdev->card->codec = ak4642_codec; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "ak4642: failed to create pcms\n"); + 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; + +} + +/* power down chip */ +static int ak4642_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_ak4642 = { + .probe = ak4642_probe, + .remove = ak4642_remove, + .resume = ak4642_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_ak4642); + +static int __init ak4642_modinit(void) +{ + int ret; +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&ak4642_i2c_driver); +#endif + return ret; + +} +module_init(ak4642_modinit); + +static void __exit ak4642_exit(void) +{ +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&ak4642_i2c_driver); +#endif + +} +module_exit(ak4642_exit); + +MODULE_DESCRIPTION("Soc AK4642 driver"); +MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ak4642.h b/sound/soc/codecs/ak4642.h new file mode 100644 index 00000000000..e476833d314 --- /dev/null +++ b/sound/soc/codecs/ak4642.h @@ -0,0 +1,20 @@ +/* + * ak4642.h -- AK4642 Soc Audio driver + * + * Copyright (C) 2009 Renesas Solutions Corp. + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * Based on ak4535.c + * + * 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 _AK4642_H +#define _AK4642_H + +extern struct snd_soc_dai ak4642_dai; +extern struct snd_soc_codec_device soc_codec_dev_ak4642; + +#endif diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 126b15b18ae..5d547675b85 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -53,6 +53,7 @@ /* codec private data */ struct aic3x_priv { + struct snd_soc_codec codec; unsigned int sysclk; int master; }; @@ -1156,11 +1157,13 @@ static int aic3x_resume(struct platform_device *pdev) * initialise the AIC3X driver * register the mixer and dsp interfaces with the kernel */ -static int aic3x_init(struct snd_soc_device *socdev) +static int aic3x_init(struct snd_soc_codec *codec) { - struct snd_soc_codec *codec = socdev->card->codec; - struct aic3x_setup_data *setup = socdev->codec_data; - int reg, ret = 0; + int reg; + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); codec->name = "tlv320aic3x"; codec->owner = THIS_MODULE; @@ -1177,13 +1180,6 @@ static int aic3x_init(struct snd_soc_device *socdev) aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT); aic3x_write(codec, AIC3X_RESET, SOFT_RESET); - /* register pcms */ - ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); - if (ret < 0) { - printk(KERN_ERR "aic3x: failed to create pcms\n"); - goto pcm_err; - } - /* DAC default volume and mute */ aic3x_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON); aic3x_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON); @@ -1250,30 +1246,51 @@ static int aic3x_init(struct snd_soc_device *socdev) /* off, with power on */ aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* setup GPIO functions */ - aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4); - aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4); + return 0; +} - snd_soc_add_controls(codec, aic3x_snd_controls, - ARRAY_SIZE(aic3x_snd_controls)); - aic3x_add_widgets(codec); - ret = snd_soc_init_card(socdev); +static struct snd_soc_codec *aic3x_codec; + +static int aic3x_register(struct snd_soc_codec *codec) +{ + int ret; + + ret = aic3x_init(codec); if (ret < 0) { - printk(KERN_ERR "aic3x: failed to register card\n"); - goto card_err; + dev_err(codec->dev, "Failed to initialise device\n"); + return ret; } - return ret; + aic3x_codec = codec; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); -pcm_err: - kfree(codec->reg_cache); - return ret; + ret = snd_soc_register_codec(codec); + if (ret) { + dev_err(codec->dev, "Failed to register codec\n"); + return ret; + } + + ret = snd_soc_register_dai(&aic3x_dai); + if (ret) { + dev_err(codec->dev, "Failed to register dai\n"); + snd_soc_unregister_codec(codec); + return ret; + } + + return 0; } -static struct snd_soc_device *aic3x_socdev; +static int aic3x_unregister(struct aic3x_priv *aic3x) +{ + aic3x_set_bias_level(&aic3x->codec, SND_SOC_BIAS_OFF); + + snd_soc_unregister_dai(&aic3x_dai); + snd_soc_unregister_codec(&aic3x->codec); + + kfree(aic3x); + aic3x_codec = NULL; + + return 0; +} #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) /* @@ -1288,28 +1305,36 @@ static struct snd_soc_device *aic3x_socdev; static int aic3x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - struct snd_soc_device *socdev = aic3x_socdev; - struct snd_soc_codec *codec = socdev->card->codec; - int ret; + struct snd_soc_codec *codec; + struct aic3x_priv *aic3x; - i2c_set_clientdata(i2c, codec); + aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL); + if (aic3x == NULL) { + dev_err(&i2c->dev, "failed to create private data\n"); + return -ENOMEM; + } + + codec = &aic3x->codec; + codec->dev = &i2c->dev; + codec->private_data = aic3x; codec->control_data = i2c; + codec->hw_write = (hw_write_t) i2c_master_send; - ret = aic3x_init(socdev); - if (ret < 0) - printk(KERN_ERR "aic3x: failed to initialise AIC3X\n"); - return ret; + i2c_set_clientdata(i2c, aic3x); + + return aic3x_register(codec); } static int aic3x_i2c_remove(struct i2c_client *client) { - struct snd_soc_codec *codec = i2c_get_clientdata(client); - kfree(codec->reg_cache); - return 0; + struct aic3x_priv *aic3x = i2c_get_clientdata(client); + + return aic3x_unregister(aic3x); } static const struct i2c_device_id aic3x_i2c_id[] = { { "tlv320aic3x", 0 }, + { "tlv320aic33", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); @@ -1320,50 +1345,28 @@ static struct i2c_driver aic3x_i2c_driver = { .name = "aic3x I2C Codec", .owner = THIS_MODULE, }, - .probe = aic3x_i2c_probe, + .probe = aic3x_i2c_probe, .remove = aic3x_i2c_remove, .id_table = aic3x_i2c_id, }; -static int aic3x_add_i2c_device(struct platform_device *pdev, - const struct aic3x_setup_data *setup) +static inline void aic3x_i2c_init(void) { - struct i2c_board_info info; - struct i2c_adapter *adapter; - struct i2c_client *client; int ret; ret = i2c_add_driver(&aic3x_i2c_driver); - if (ret != 0) { - dev_err(&pdev->dev, "can't add i2c driver\n"); - return ret; - } - - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = setup->i2c_address; - strlcpy(info.type, "tlv320aic3x", I2C_NAME_SIZE); - - adapter = i2c_get_adapter(setup->i2c_bus); - if (!adapter) { - dev_err(&pdev->dev, "can't get i2c adapter %d\n", - setup->i2c_bus); - goto err_driver; - } - - client = i2c_new_device(adapter, &info); - i2c_put_adapter(adapter); - if (!client) { - dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", - (unsigned int)info.addr); - goto err_driver; - } - - return 0; + if (ret) + printk(KERN_ERR "%s: error regsitering i2c driver, %d\n", + __func__, ret); +} -err_driver: +static inline void aic3x_i2c_exit(void) +{ i2c_del_driver(&aic3x_i2c_driver); - return -ENODEV; } +#else +static inline void aic3x_i2c_init(void) { } +static inline void aic3x_i2c_exit(void) { } #endif static int aic3x_probe(struct platform_device *pdev) @@ -1371,42 +1374,52 @@ static int aic3x_probe(struct platform_device *pdev) struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct aic3x_setup_data *setup; struct snd_soc_codec *codec; - struct aic3x_priv *aic3x; int ret = 0; - printk(KERN_INFO "AIC3X Audio Codec %s\n", AIC3X_VERSION); + codec = aic3x_codec; + if (!codec) { + dev_err(&pdev->dev, "Codec not registered\n"); + return -ENODEV; + } + socdev->card->codec = codec; setup = socdev->codec_data; - codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); - if (codec == NULL) - return -ENOMEM; - aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL); - if (aic3x == NULL) { - kfree(codec); - return -ENOMEM; + if (!setup) { + dev_err(&pdev->dev, "No setup data supplied\n"); + return -EINVAL; } - codec->private_data = aic3x; - socdev->card->codec = codec; - mutex_init(&codec->mutex); - INIT_LIST_HEAD(&codec->dapm_widgets); - INIT_LIST_HEAD(&codec->dapm_paths); + /* setup GPIO functions */ + aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4); + aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4); - aic3x_socdev = socdev; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - if (setup->i2c_address) { - codec->hw_write = (hw_write_t) i2c_master_send; - ret = aic3x_add_i2c_device(pdev, setup); + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "aic3x: failed to create pcms\n"); + goto pcm_err; } -#else - /* Add other interfaces here */ -#endif - if (ret != 0) { - kfree(codec->private_data); - kfree(codec); + snd_soc_add_controls(codec, aic3x_snd_controls, + ARRAY_SIZE(aic3x_snd_controls)); + + 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; } @@ -1421,12 +1434,8 @@ static int aic3x_remove(struct platform_device *pdev) snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_unregister_device(codec->control_data); - i2c_del_driver(&aic3x_i2c_driver); -#endif - kfree(codec->private_data); - kfree(codec); + + kfree(codec->reg_cache); return 0; } @@ -1441,13 +1450,15 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_aic3x); static int __init aic3x_modinit(void) { - return snd_soc_register_dai(&aic3x_dai); + aic3x_i2c_init(); + + return 0; } module_init(aic3x_modinit); static void __exit aic3x_exit(void) { - snd_soc_unregister_dai(&aic3x_dai); + aic3x_i2c_exit(); } module_exit(aic3x_exit); diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h index ac827e578c4..9af1c886213 100644 --- a/sound/soc/codecs/tlv320aic3x.h +++ b/sound/soc/codecs/tlv320aic3x.h @@ -282,8 +282,6 @@ int aic3x_headset_detected(struct snd_soc_codec *codec); int aic3x_button_pressed(struct snd_soc_codec *codec); struct aic3x_setup_data { - int i2c_bus; - unsigned short i2c_address; unsigned int gpio_func[2]; }; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index ff9b63b0ff8..d9987999e92 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -218,6 +218,8 @@ struct wm8993_priv { struct snd_soc_codec codec; int master; int sysclk_source; + int tdm_slots; + int tdm_width; unsigned int mclk_rate; unsigned int sysclk_rate; unsigned int fs; @@ -519,7 +521,7 @@ static int configure_clock(struct snd_soc_codec *codec) dev_dbg(codec->dev, "Using %dHz MCLK\n", wm8993->mclk_rate); reg = wm8993_read(codec, WM8993_CLOCKING_2); - reg &= ~WM8993_SYSCLK_SRC; + reg &= ~(WM8993_MCLK_DIV | WM8993_SYSCLK_SRC); if (wm8993->mclk_rate > 13500000) { reg |= WM8993_MCLK_DIV; wm8993->sysclk_rate = wm8993->mclk_rate / 2; @@ -527,8 +529,6 @@ static int configure_clock(struct snd_soc_codec *codec) reg &= ~WM8993_MCLK_DIV; wm8993->sysclk_rate = wm8993->mclk_rate; } - reg &= ~WM8993_MCLK_DIV; - reg &= ~(WM8993_MCLK_DIV | WM8993_SYSCLK_SRC); wm8993_write(codec, WM8993_CLOCKING_2, reg); break; @@ -1189,24 +1189,30 @@ static int wm8993_hw_params(struct snd_pcm_substream *substream, /* What BCLK do we need? */ wm8993->fs = params_rate(params); wm8993->bclk = 2 * wm8993->fs; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - wm8993->bclk *= 16; - break; - case SNDRV_PCM_FORMAT_S20_3LE: - wm8993->bclk *= 20; - aif1 |= 0x8; - break; - case SNDRV_PCM_FORMAT_S24_LE: - wm8993->bclk *= 24; - aif1 |= 0x10; - break; - case SNDRV_PCM_FORMAT_S32_LE: - wm8993->bclk *= 32; - aif1 |= 0x18; - break; - default: - return -EINVAL; + if (wm8993->tdm_slots) { + dev_dbg(codec->dev, "Configuring for %d %d bit TDM slots\n", + wm8993->tdm_slots, wm8993->tdm_width); + wm8993->bclk *= wm8993->tdm_width * wm8993->tdm_slots; + } else { + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + wm8993->bclk *= 16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + wm8993->bclk *= 20; + aif1 |= 0x8; + break; + case SNDRV_PCM_FORMAT_S24_LE: + wm8993->bclk *= 24; + aif1 |= 0x10; + break; + case SNDRV_PCM_FORMAT_S32_LE: + wm8993->bclk *= 32; + aif1 |= 0x18; + break; + default: + return -EINVAL; + } } dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8993->bclk); @@ -1325,12 +1331,67 @@ static int wm8993_digital_mute(struct snd_soc_dai *codec_dai, int mute) return 0; } +static int wm8993_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8993_priv *wm8993 = codec->private_data; + int aif1 = 0; + int aif2 = 0; + + /* Don't need to validate anything if we're turning off TDM */ + if (slots == 0) { + wm8993->tdm_slots = 0; + goto out; + } + + /* Note that we allow configurations we can't handle ourselves - + * for example, we can generate clocks for slots 2 and up even if + * we can't use those slots ourselves. + */ + aif1 |= WM8993_AIFADC_TDM; + aif2 |= WM8993_AIFDAC_TDM; + + switch (rx_mask) { + case 3: + break; + case 0xc: + aif1 |= WM8993_AIFADC_TDM_CHAN; + break; + default: + return -EINVAL; + } + + + switch (tx_mask) { + case 3: + break; + case 0xc: + aif2 |= WM8993_AIFDAC_TDM_CHAN; + break; + default: + return -EINVAL; + } + +out: + wm8993->tdm_width = slot_width; + wm8993->tdm_slots = slots / 2; + + snd_soc_update_bits(codec, WM8993_AUDIO_INTERFACE_1, + WM8993_AIFADC_TDM | WM8993_AIFADC_TDM_CHAN, aif1); + snd_soc_update_bits(codec, WM8993_AUDIO_INTERFACE_2, + WM8993_AIFDAC_TDM | WM8993_AIFDAC_TDM_CHAN, aif2); + + return 0; +} + static struct snd_soc_dai_ops wm8993_ops = { .set_sysclk = wm8993_set_sysclk, .set_fmt = wm8993_set_dai_fmt, .hw_params = wm8993_hw_params, .digital_mute = wm8993_digital_mute, .set_pll = wm8993_set_fll, + .set_tdm_slot = wm8993_set_tdm_slot, }; #define WM8993_RATES SNDRV_PCM_RATE_8000_48000 diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index fa88b463e71..e7d2840d9e5 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -406,7 +406,7 @@ static int wm9705_soc_probe(struct platform_device *pdev) ret = snd_soc_init_card(socdev); if (ret < 0) { printk(KERN_ERR "wm9705: failed to register card\n"); - goto pcm_err; + goto reset_err; } return 0; diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index e8fc474ba5c..41699bd1986 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -18,7 +18,6 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> -#include <linux/regulator/consumer.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -474,12 +473,6 @@ SND_SOC_DAPM_MIXER("MIXINL", WM8993_POWER_MANAGEMENT_2, 9, 0, SND_SOC_DAPM_MIXER("MIXINR", WM8993_POWER_MANAGEMENT_2, 8, 0, mixinr, ARRAY_SIZE(mixinr)), -SND_SOC_DAPM_ADC("ADCL", "Capture", WM8993_POWER_MANAGEMENT_2, 1, 0), -SND_SOC_DAPM_ADC("ADCR", "Capture", WM8993_POWER_MANAGEMENT_2, 0, 0), - -SND_SOC_DAPM_DAC("DACL", "Playback", WM8993_POWER_MANAGEMENT_3, 1, 0), -SND_SOC_DAPM_DAC("DACR", "Playback", WM8993_POWER_MANAGEMENT_3, 0, 0), - SND_SOC_DAPM_MIXER("Left Output Mixer", WM8993_POWER_MANAGEMENT_3, 5, 0, left_output_mixer, ARRAY_SIZE(left_output_mixer)), SND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0, |